diff --git a/src/ASF/sam0/drivers/sercom/usart/usart.c b/src/ASF/sam0/drivers/sercom/usart/usart.c index 6264e4534ab685ceafc2ee8adb64371354f60428..00aede3f3a4672b564e50a0d598b283e085e0ee9 100644 --- a/src/ASF/sam0/drivers/sercom/usart/usart.c +++ b/src/ASF/sam0/drivers/sercom/usart/usart.c @@ -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; } } diff --git a/src/ASF/sam0/drivers/sercom/usart/usart_interrupt.c b/src/ASF/sam0/drivers/sercom/usart/usart_interrupt.c index 151dba23494a5d5b99fa63758ca78caf393d19e0..d094dcf3aed6390eace29f019eef65ed8a79bfc7 100644 --- a/src/ASF/sam0/drivers/sercom/usart/usart_interrupt.c +++ b/src/ASF/sam0/drivers/sercom/usart/usart_interrupt.c @@ -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; } diff --git a/src/ArduinoZeroTemplate.c b/src/ArduinoZeroTemplate.c index c3ce3b2493496c279fd7968e19a5d243eda57164..e0143769a2d4a6de7ebd9c54514441dffd4649ad 100644 --- a/src/ArduinoZeroTemplate.c +++ b/src/ArduinoZeroTemplate.c @@ -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) { diff --git a/src/ArduinoZeroTemplate.h b/src/ArduinoZeroTemplate.h index a09765fbb289e6ef80001963f8faa6f57aa8c1ec..e5c75b67fece170a62bbc8bd79c8b2f0c7c9a744 100644 --- a/src/ArduinoZeroTemplate.h +++ b/src/ArduinoZeroTemplate.h @@ -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 diff --git a/src/macros.h b/src/macros.h index c23c97f44d685529a0fa81120bc9a7f2f09ee474..faf6a2f34723c5a2f17ecede133a9a7c82447a96 100644 --- a/src/macros.h +++ b/src/macros.h @@ -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