Skip to content
Snippets Groups Projects
Commit 21ddfa82 authored by Bengt's avatar Bengt
Browse files

USART callback almost done

parent e31dbf99
No related branches found
No related tags found
No related merge requests found
......@@ -673,13 +673,18 @@ enum status_code usart_write_buffer_wait(
uint16_t tx_pos = 0;
/* Blocks while buffer is being transferred */
while (length--) {
while (length--)
{
/* Wait for the USART to be ready for new data and abort
* operation if it doesn't get ready within the timeout*/
for (uint32_t i = 0; i <= USART_TIMEOUT; i++) {
if (usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_DRE) {
for (uint32_t i = 0; i <= USART_TIMEOUT; i++)
{
if (usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_DRE)
{
break;
} else if (i == USART_TIMEOUT) {
}
else if (i == USART_TIMEOUT)
{
return STATUS_ERR_TIMEOUT;
}
}
......@@ -688,7 +693,8 @@ enum status_code usart_write_buffer_wait(
uint16_t data_to_send = tx_data[tx_pos++];
/* Check if the character size exceeds 8 bit */
if (module->character_size == USART_CHARACTER_SIZE_9BIT) {
if (module->character_size == USART_CHARACTER_SIZE_9BIT)
{
data_to_send |= (tx_data[tx_pos++] << 8);
}
......@@ -697,10 +703,14 @@ enum status_code usart_write_buffer_wait(
}
/* Wait until Transmit is complete or timeout */
for (uint32_t i = 0; i <= USART_TIMEOUT; i++) {
if (usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_TXC) {
for (uint32_t i = 0; i <= USART_TIMEOUT; i++)
{
if (usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_TXC)
{
break;
} else if (i == USART_TIMEOUT) {
}
else if (i == USART_TIMEOUT)
{
return STATUS_ERR_TIMEOUT;
}
}
......
......@@ -448,8 +448,7 @@ enum status_code usart_get_job_status(
* \param[in] instance ID of the SERCOM instance calling the interrupt
* handler.
*/
void _usart_interrupt_handler(
uint8_t instance)
void _usart_interrupt_handler(uint8_t instance)
{
/* Temporary variables */
uint16_t interrupt_status;
......@@ -476,14 +475,17 @@ void _usart_interrupt_handler(
/* Check if a DATA READY interrupt has occurred,
* and if there is more to transfer */
if (interrupt_status & SERCOM_USART_INTFLAG_DRE) {
if (module->remaining_tx_buffer_length) {
if (interrupt_status & SERCOM_USART_INTFLAG_DRE)
{
if (module->remaining_tx_buffer_length)
{
/* Write value will be at least 8-bits long */
uint16_t data_to_send = *(module->tx_buffer_ptr);
/* Increment 8-bit pointer */
(module->tx_buffer_ptr)++;
if (module->character_size == USART_CHARACTER_SIZE_9BIT) {
if (module->character_size == USART_CHARACTER_SIZE_9BIT)
{
data_to_send |= (*(module->tx_buffer_ptr) << 8);
/* Increment 8-bit pointer */
(module->tx_buffer_ptr)++;
......@@ -491,76 +493,91 @@ void _usart_interrupt_handler(
/* Write the data to send */
usart_hw->DATA.reg = (data_to_send & SERCOM_USART_DATA_MASK);
if (--(module->remaining_tx_buffer_length) == 0) {
if (--(module->remaining_tx_buffer_length) == 0)
{
/* Disable the Data Register Empty Interrupt */
usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_DRE;
/* Enable Transmission Complete interrupt */
usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_TXC;
}
} else {
}
else
{
usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_DRE;
}
}
/* Check if the Transmission Complete interrupt has occurred and
* that the transmit buffer is empty */
if (interrupt_status & SERCOM_USART_INTFLAG_TXC) {
if (interrupt_status & SERCOM_USART_INTFLAG_TXC)
{
/* Disable TX Complete Interrupt, and set STATUS_OK */
usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_TXC;
module->tx_status = STATUS_OK;
/* Run callback if registered and enabled */
if (callback_status & (1 << USART_CALLBACK_BUFFER_TRANSMITTED)) {
if (callback_status & (1 << USART_CALLBACK_BUFFER_TRANSMITTED))
{
(*(module->callback[USART_CALLBACK_BUFFER_TRANSMITTED]))(module);
}
}
/* Check if the Receive Complete interrupt has occurred, and that
* there's more data to receive */
if (interrupt_status & SERCOM_USART_INTFLAG_RXC) {
if (module->remaining_rx_buffer_length) {
if (interrupt_status & SERCOM_USART_INTFLAG_RXC)
{
if (module->remaining_rx_buffer_length)
{
/* Read out the status code and mask away all but the 4 LSBs*/
error_code = (uint8_t)(usart_hw->STATUS.reg & SERCOM_USART_STATUS_MASK);
#if !SAMD20
/* CTS status should not be considered as an error */
if(error_code & SERCOM_USART_STATUS_CTS) {
if (error_code & SERCOM_USART_STATUS_CTS)
{
error_code &= ~SERCOM_USART_STATUS_CTS;
}
#endif
#ifdef FEATURE_USART_LIN_MASTER
/* TXE status should not be considered as an error */
if(error_code & SERCOM_USART_STATUS_TXE) {
if(error_code & SERCOM_USART_STATUS_TXE)
{
error_code &= ~SERCOM_USART_STATUS_TXE;
}
#endif
/* Check if an error has occurred during the receiving */
if (error_code) {
if (error_code)
{
/* Check which error occurred */
if (error_code & SERCOM_USART_STATUS_FERR) {
if (error_code & SERCOM_USART_STATUS_FERR)
{
/* Store the error code and clear flag by writing 1 to it */
module->rx_status = STATUS_ERR_BAD_FORMAT;
usart_hw->STATUS.reg = SERCOM_USART_STATUS_FERR;
} else if (error_code & SERCOM_USART_STATUS_BUFOVF) {
}
else if (error_code & SERCOM_USART_STATUS_BUFOVF)
{
/* Store the error code and clear flag by writing 1 to it */
module->rx_status = STATUS_ERR_OVERFLOW;
usart_hw->STATUS.reg = SERCOM_USART_STATUS_BUFOVF;
} else if (error_code & SERCOM_USART_STATUS_PERR) {
}
else if (error_code & SERCOM_USART_STATUS_PERR)
{
/* Store the error code and clear flag by writing 1 to it */
module->rx_status = STATUS_ERR_BAD_DATA;
usart_hw->STATUS.reg = SERCOM_USART_STATUS_PERR;
}
#ifdef FEATURE_USART_LIN_SLAVE
else if (error_code & SERCOM_USART_STATUS_ISF) {
else if (error_code & SERCOM_USART_STATUS_ISF)
{
/* Store the error code and clear flag by writing 1 to it */
module->rx_status = STATUS_ERR_PROTOCOL;
usart_hw->STATUS.reg = SERCOM_USART_STATUS_ISF;
}
#endif
#ifdef FEATURE_USART_COLLISION_DECTION
else if (error_code & SERCOM_USART_STATUS_COLL) {
else if (error_code & SERCOM_USART_STATUS_COLL)
{
/* Store the error code and clear flag by writing 1 to it */
module->rx_status = STATUS_ERR_PACKET_COLLISION;
usart_hw->STATUS.reg = SERCOM_USART_STATUS_COLL;
......@@ -568,13 +585,14 @@ void _usart_interrupt_handler(
#endif
/* Run callback if registered and enabled */
if (callback_status
& (1 << USART_CALLBACK_ERROR)) {
if (callback_status & (1 << USART_CALLBACK_ERROR))
{
(*(module->callback[USART_CALLBACK_ERROR]))(module);
}
} else {
}
else
{
/* Read current packet from DATA register,
* increment buffer pointer and decrement buffer length */
uint16_t received_data = (usart_hw->DATA.reg & SERCOM_USART_DATA_MASK);
......@@ -584,7 +602,8 @@ void _usart_interrupt_handler(
/* Increment 8-bit pointer */
module->rx_buffer_ptr += 1;
if (module->character_size == USART_CHARACTER_SIZE_9BIT) {
if (module->character_size == USART_CHARACTER_SIZE_9BIT)
{
/* 9-bit data, write next received byte to the buffer */
*(module->rx_buffer_ptr) = (received_data >> 8);
/* Increment 8-bit pointer */
......@@ -592,20 +611,23 @@ void _usart_interrupt_handler(
}
/* Check if the last character have been received */
if(--(module->remaining_rx_buffer_length) == 0) {
if (--(module->remaining_rx_buffer_length) == 0)
{
/* Disable RX Complete Interrupt,
* and set STATUS_OK */
usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_RXC;
module->rx_status = STATUS_OK;
/* Run callback if registered and enabled */
if (callback_status
& (1 << USART_CALLBACK_BUFFER_RECEIVED)) {
if (callback_status & (1 << USART_CALLBACK_BUFFER_RECEIVED))
{
(*(module->callback[USART_CALLBACK_BUFFER_RECEIVED]))(module);
}
}
}
} else {
}
else
{
/* This should not happen. Disable Receive Complete interrupt. */
usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_RXC;
}
......
......@@ -24,46 +24,34 @@ uint64_t t1ms_timer = 0;
static uint64_t next_ms=0; // To save the time of next ms. Value in ticks of the RTC clock. Global so it can be initialized.
// Other variables for peripherals:
volatile uint8_t usart5_rx_buffer[MAX_RX_BUFFER_LENGTH];
volatile uint8_t usart5_rx_buffer[MAX_USART5_RX_BUFFER_LENGTH];
uint8_t usart5_tx_buffer[5];
bool usart5_command_received = false;
/*
// For I2C Master
static uint8_t write_buffer[DATA_LENGTH];
static uint8_t read_buffer[DATA_LENGTH];
*/
uint32_t readport;
uint32_t example;
void ArduinoZeroTemplate(void)
{
while (true)
// USART example. Start a reading of a known length. Will create an interrupt for each character until buffer is full.
usart_read_buffer_job(&usart5_instance_struct, (uint8_t *)usart5_rx_buffer, MAX_USART5_RX_BUFFER_LENGTH);
while (!usart5_command_received)
{} // Wait for finished command receive.
usart5_command_received = false;
if (usart5_instance_struct.rx_status == 0) // Check that no errors
{
// I/O example of using the macros for pin fiddling.
PORT_PIN_INPUT_EN(PIN_PB10);
PORT_SET_CTRLSAMPLING(PIN_PB10);
PORT_CLR_CTRLSAMPLING(PIN_PB10);
PORT->Group[GROUPNR(PIN_PB10)].CTRL.reg = 0;
PORT_PIN_PULL_EN(PIN_PB10);
PORT_PIN_PULL_DIS(PIN_PB10);
PORT_PIN_PULL_DRIVE_STRONG(PIN_PB10);
PORT_PIN_PULL_DRIVE_WEAK(PIN_PB10);
PORT_PIN_SET(LED_0_PIN);
PORT_PIN_CLR(LED_0_PIN);
PORT_PIN_TOGGLE(LED_0_PIN);
PORT_PIN_TO_INPUT(LED_0_PIN);
PORT_PIN_TO_OUTPUT(LED_0_PIN);
PORT_PIN_TOGGLE_DIRECTION(LED_0_PIN);
readport=PORT_READ_PORT(PIN_PA00);
readport=PORT_READ_PIN(PIN_PA00);
example = readport;
// End I/O example
// USART example. Setts up the USART using interrupt. Reads a buffer of 5 bytes.
// Adds one to each byte and sends it back after 5 seconds.
// Do something with the data.
}
else
{
// Error
}
while (true)
{
}// end while (true) infinite loop
}// end ArduinoZeroTemplate
......@@ -140,22 +128,40 @@ void ArduinoZeroTemplateInit(void)
{
}
usart_enable(&usart5_instance_struct);
usart_enable_callback(&usart5_instance_struct, USART_CALLBACK_BUFFER_RECEIVED);
// How to use in callback mode:
// ASF use all interrupt driven code as callbacks. That means that the address of the interrupt service routine
// is not defined at compile time. Instead the address of the function that is to be called by the interrupt
// must be registered at run-time. This method is defined as callback.
// It makes more sense in OS driven systems.
// Receive a specific nr of bytes: An interrupt is triggered on data receive which calls usart_read_buffer_job.
// Send a buffer: usart_write_buffer_wait. Holds until the buffer is sent.
// ASF use all interrupt driven code using the concept of callbacks.
// That means that the Interrupt Service Routine (ISR) is triggered as normal and the source code is also
// included in ASF but the ISR does not include any signaling to the main program that the ISR is finished.
// For example it can read a specific number of bytes and store them in a buffer. This is there the
// callback comes in. The code for the ISR is already made by ASF so there must by some way to include such
// signaling. Therefore it is possible to define a function that the ISR calls to then it is finished.
// That is the callback.
// The address of the interrupt service routine is not defined at compile time. Instead the address of the
// callback function that is to be called by the ISR must be registered at run-time.
// The need for an RX interrupt is obvious. An TX interrupt is not as much needed.
// An TX interrupt can be used to trigger a free position in the buffer or it can be used to trigger
// an action on the bus after the last byte is sent. Typical for RS485.
// Test to send a string with length 5:
usart_write_buffer_wait(&usart5_instance_struct, usart5_tx_buffer, 5);
// Use example:
// Receive a specific nr of bytes using interrupt driven code:
// Call usart_read_buffer_job that enables the RX interrupt and will read the specified number of bytes
// after it will make a call to the callback function that can for example set a finished flag and also
// include errors.
// Send a buffer without using interrupt driven code:
// Call usart_write_buffer_wait. Will lock until the buffer is sent.
// For example to send a string with length 5:
// usart_write_buffer_wait(&usart5_instance_struct, usart5_tx_buffer, 5);
callback routine for usart 5:
....
// Define what usart function and what interrupt that shall be used for the callback. (Here only RX is used):
usart_register_callback(&usart5_instance_struct, usart5_read_callback, USART_CALLBACK_BUFFER_RECEIVED);
usart_register_callback(&usart5_instance_struct, usart5_readerror_callback, USART_CALLBACK_ERROR);
// Enable the callbacks:
usart_enable_callback(&usart5_instance_struct, USART_CALLBACK_BUFFER_RECEIVED);
usart_enable_callback(&usart5_instance_struct, USART_CALLBACK_ERROR);
// Enable global interrupts:
system_interrupt_enable_global();
/* USART if used for I2C
// USART 2, I2C
......@@ -209,6 +215,17 @@ callback routine for usart 5:
Init1msClock();
} // end init
void usart5_read_callback(struct usart_module *const usart_module)
{
// Here some signaling to main should be done. This is an example.
usart5_command_received = true;
}
void usart5_readerror_callback(struct usart_module *const usart_module)
{
// End the communication. Main will read and identify that it was an error.
usart5_command_received = true;
}
void Init1msClock(void)
{
......
......@@ -131,10 +131,12 @@ Available for user
#define TIMEOUT 3 // Nr of retries
*/
/* For internal DAC if used
// For internal DAC if used
#define DAC_PIN PIN_PA02 // Min 5 kohm load, max 100 pF.
*/
// USART5 in UART mode if used:
#define MAX_USART5_RX_BUFFER_LENGTH 5
// Really need to add analogue in!
// Functions
......@@ -143,6 +145,8 @@ void ArduinoZeroTemplate(void);
void Update1msClock(void);
void Init1msClock(void);
void usart5_read_callback(struct usart_module *const usart_module);
void usart5_readerror_callback(struct usart_module *const usart_module);
// Put any typedefs here
......
......@@ -48,8 +48,29 @@ SET_PIN and not OUTSET.
The above method is what I find the most general method of writing to PORT registers but there
are other ways to write to the PORT registers.
Recommended methods are method number 1 or if need for extreme performance, method 2 using IOBUS.
Method 1. Method as described above that access the PORT and its groups using pointers.
Method 2. ARM Cortex M0+ has a local but between CPU and I/O called IOBUS that gives the
Examples:
PORT_PIN_INPUT_EN(PIN_PB10);
PORT_SET_CTRLSAMPLING(PIN_PB10);
PORT_CLR_CTRLSAMPLING(PIN_PB10);
PORT->Group[GROUPNR(PIN_PB10)].CTRL.reg = 0;
PORT_PIN_PULL_EN(PIN_PB10);
PORT_PIN_PULL_DIS(PIN_PB10);
PORT_PIN_PULL_DRIVE_STRONG(PIN_PB10);
PORT_PIN_PULL_DRIVE_WEAK(PIN_PB10);
PORT_PIN_SET(LED_0_PIN);
PORT_PIN_CLR(LED_0_PIN);
PORT_PIN_TOGGLE(LED_0_PIN);
PORT_PIN_TO_INPUT(LED_0_PIN);
PORT_PIN_TO_OUTPUT(LED_0_PIN);
PORT_PIN_TOGGLE_DIRECTION(LED_0_PIN);
readport=PORT_READ_PORT(PIN_PA00);
readport=PORT_READ_PIN(PIN_PA00);
example = readport;
Method 2. ARM Cortex M0+ has a local buss between CPU and I/O called IOBUS that gives the
possibility to access as I/O registers in one clock cycle. The registers that can be
accessed through the IOBUS are:
Pin Direction (DIRCLR, DIRSET, DIRTGL),
......@@ -74,7 +95,6 @@ Example to set pin PB02:
REG_PORT_OUTSET1 = PORT_PB02;
It works but as you can see that you must yourself take care of the group because the macro
combined the port and the group in the same definition.
Recommendation: Use method nr 1 and 2.
Read more here about writing to ports:
https://electronics.stackexchange.com/questions/139117/atmels-arm-programming-without-asf
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment