uart receive interrupt waits for enter then continues after enter - c

I am trying to program a kind of cli with uart receive interrupts for an embedded system. I want my code to stay in uart function till i press the enter as '\r' then go on till it gets to the next uart function and wait for enter again. like some kind of scanf.
int main(void)
{
while (1)
{
uart_interrupt_receive();
//something else
uart_interrupt_receive();
//something else
}
I dont want to use scanf or getchar if possible. I got here so far. I cant decide which flags to use to make it work the way i want or how else i can change it?
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)(){
if (USART2->ISR & USART_ISR_RXNE) //is rx flag active
{
char rx = (char)(USART2->RDR & 0xFF); //received char rx
if ((rx == '\r') || (rx == '\n')) //if rx enter
{
//go on
}//if
else
{
//wait
}//else
}//if
}//uart_rxcplt_callback

Stalling the whole MCU inside an interrupt is not a great idea. Generally speaking, you should keep data reception and higher layer application logic separated if possible. There's two ways to deal with UART rx:
"Old school" way of interrupts where the hardware doesn't have much in the way of rx hardware buffers. In that case you would let the UART rx interrupt store the incoming character in a software ring buffer. This ring buffer needs protection against race conditions.
If DMA is supported then you can skip interrupts and DMA everything into a DMA buffer. Race condition considerations apply here too.
In the end your main application sits with a buffer of received data of some kind. It call poll that buffer until empty and do whatever the program should be doing based on data received.

Related

Why are all irq disabled for retarget write on STM32?

I am looking at the following function which retargets stdout to UART in the STM32 Std peripheral library.
int _write(int fd, char *ptr, int len) {
uint32_t primask = __get_PRIMASK();
__disable_irq();
for (int i = 0; i < len; i++) {
while (USART_GetFlagStatus(RETARGET_CFG_UART, USART_FLAG_TXE) == RESET) {
}
USART_SendData(RETARGET_CFG_UART, (uint8_t) * (ptr + i));
}
if (!primask) {
__enable_irq();
}
return len;
}
Before transmitting over UART it masks exceptions which can have a priority set via __disable_irq() (which I understand includes all peripheral and GPIO interrupts).
My question is, why is this UART tx implemented this way?
Can the disable/enable irq calls be removed so that other functionality in the program is not delayed due to potentially lengthy data transactions?
I suspect the author of that code is disabling all interrupts just because there might be some interrupts that write bytes to the UART, and the author wants to ensure that all the bytes sent to the _write function get written consecutively, instead of having other, unrelated bytes in the middle.
I'm assuming all the ISRs defined in your system are relatively quick compared to the serial transmission, and the receiver doesn't care if there are some small delays in the transmission. So it should be OK to leave any interrupts enabled if those interrupts are not related to the UART.
You should get a firm grasp of all the ISRs in your system and what they do, and then you should use that to decide which specific IRQs should be disabled during this operation (if any).
It's a quasi-bad design.
For one thing, always calling __disable_irq but not __enable_irq is suspicious.
It prevents nested calls where the caller is also doing __disable_irq/__enable_irq.
It assumes that a Tx ISR is not involved. That is, the UART is running in polled mode. But, if that were true, why did the code disable/enable interrupts?
If there is more data to send to the UART Tx than can be put into the Tx FIFO, the code [as you've seen] will block.
This means that the base/task level can't do other things.
When I do such UART code, I use a ring queue. I define one that is large enough to accomodate any bursts (e.g. queue is 10,000).
The design assumes that if the _write is called and the ring queue becomes full before all data is added, the ring queue size should be increased (i.e. ring queue full is a [fatal] design error).
Otherwise, the base/task level is trying to send too much data. That is, it's generating more data than can be sent at the Tx baud rate.
The _write process:
Copy bytes to the ring queue.
Copy as many bytes from the queue to the UART as space in the UART Tx FIFO allows.
Tx ISR will be called if space becomes available. It repeats step (2)
With this, _write will not block if the UART Tx FIFO becomes full.
The Tx ISR will pick up the slack. That is, when there is more space available in the FIFO, and the Tx is "ready", the Tx ISR will be called.
Here is some pseudo code to illustrate what I mean:
// push_tx_data -- copy data from Tx ring queue into UART Tx FIFO
int
push_tx_data(void)
{
// fill Tx FIFO with as much data as it can hold
while (1) {
// no free space in Tx FIFO
if (USART_GetFlagStatus(RETARGET_CFG_UART, USART_FLAG_TXE) != RESET)
break;
// dequeue byte to transmit (stop if queue empty)
int i = tx_queue_dequeue();
if (i < 0)
break;
// put this data byte into the UART Tx FIFO
uint8_t buf = i;
USART_SendData(RETARGET_CFG_UART, buf);
}
}
// tx_ISR -- handle interrupt from UART Tx
void
tx_ISR(void)
{
// presumeably interrupts are already disabled in the ISR ...
// copy all the data we can
push_tx_data();
// clear the interrupt pending flag in the interrupt controller or ISR if
// necessary (i.e.) USART_SendData didn't clear it when the FIFO was full
}
// _write -- send data
int
_write(int fd, char *ptr, int len)
{
uint32_t primask = __get_PRIMASK();
// running from task level so disable interrupts
if (! primask)
__disable_irq();
int sent = 0;
// add to [S/W] tx ring queue
for (int i = 0; i < len; i++) {
if (! tx_queue_enqueue(ptr[i]))
break;
++sent;
}
// send data from queue into FIFO
push_tx_data();
// reenable interrupts (task level)
if (! primask)
__enable_irq();
return sent;
}

Weirdness using the raspberry pi pico uart with the c sdk

I am extremely disconcerted.
I'm making a remote controlled machine using a pi pico to drive the motors and read some sensors, and a raspberry pi 4 to send commands to the pi pico via serial and host the web interface.
The following code seems to work... but... If I remove the if with uart_is_writable coding and its content it doesn't work. Does anyone have any idea why?
#include <stdlib.h>
#include <string.h>
#include "pico/stdlib.h"
#include "hardware/uart.h"
#include "hardware/irq.h"
//DEFINES
#define UART_ID uart0
#define BAUD_RATE 19200
#define DATA_BITS 8
#define STOP_BITS 1
#define PARITY UART_PARITY_NONE
#define UART_TX_PIN 0
#define UART_RX_PIN 1
#define LED_PIN PICO_DEFAULT_LED_PIN
static int chars_rxed = 0;
volatile char uCommand[32] = {0, 0};
void on_uart_rx(void) {
char tmp_string[] = {0, 0};
new_command = true;
while (uart_is_readable(UART_ID)) {
uint8_t ch = uart_getc(UART_ID);
tmp_string[0] = ch;
strcat(uCommand, tmp_string);
if(uart_is_writable(UART_ID)){
uart_putc(UART_ID, '-');
uart_puts(UART_ID, uCommand);
uart_putc(UART_ID, '-');
}
chars_rxed++;
}
}
int main(){
uart_init(UART_ID, BAUD_RATE);
gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART);
gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART);
uart_set_hw_flow(UART_ID, false, false);
uart_set_format(UART_ID, DATA_BITS, STOP_BITS, PARITY);
uart_set_fifo_enabled(UART_ID, false);
int UART_IRQ = UART_ID == uart0 ? UART0_IRQ : UART1_IRQ;
irq_set_exclusive_handler(UART_IRQ, on_uart_rx);
irq_set_enabled(UART_IRQ, true);
uart_set_irq_enables(UART_ID, true, false);
uart_puts(UART_ID, "\nOK\n");
while (1){
tight_loop_contents();
if(uCommand[0] != 0){
uart_putc(UART_ID, '/');
uart_puts(UART_ID, uCommand);
memset(uCommand, 0, sizeof(uCommand));
}
}
}
The example code you linked to is for a simple tty/echo implementation.
You'll need to tweak it for your use case.
Because Tx interrupts are disabled, all output to the transmitter has to be polled I/O. Also, the FIFOs in the uart are disabled, so only single char I/O is used.
Hence, the uart_is_writable to check whether a char can be transmitted.
The linked code ...
echos back the received char in the Rx ISR. So, it needs to call the above function. Note that if Tx is not ready (i.e. full), the char is not echoed and is dropped.
I don't know whether uart_putc and uart_puts check for ready-to-transmit in this manner.
So, I'll assume that they do not.
This means that if you call uart_putc/uart_puts and the Tx is full, the current/pending char in the uart may get trashed/corrupted.
So, uart_is_writable should be called for any/each char to be sent.
Or ... uart_putc/uart_puts do check and will block until space is available in the uart Tx fifo. For you use case, such blocking is not desirable.
What you want ...
Side note: I have done similar [product/commercial grade] programming on an RPi for motor control via a uart, so some of this is from my own experience.
For your use case, you do not want to echo the received char. You want to append it to a receive buffer.
To implement this, you probably want to use ring queues: one for received chars and one for chars to be transmitted.
I assume you have [or will have] devised some sort of simple packet protocol to send/receive commands. The Rpi sends commands that are (e.g.):
Set motor speed
Get current sensor data
The other side should respond to these commands and execute the desired action or return the desired data.
Both processors probably need to have similar service loops and ISRs.
The Rx ISR just checks for space available in the Rx ring queue. If space is available, it gets a char from the uart and enqueues it. If no Rx char is available in the uart, the ISR may exit.
The base level code service loop should:
Check if the uart Tx can accept another character (via uart_is_writable) and, if so, it can dequeue a char from the Tx ring queue [if available] and send it (via uart_putc). It can loop on this to keep the uart transmitter busy.
Check to see if enough chars are received to form a packet/message from the other side. If there is such a packet, it can service the "request" contained in it [dequeueing the chars to make more space in the Rx ring queue].
If the base level needs to send a message, it should enqueue it to the Tx ring queue. It will be sent [eventually] in the prior step.
Some additional thoughts ...
The linked code only enables Rx interrupts and runs the Tx in polled mode. This may be enough. But, for maximum throughput and lowest latency, you may want to make the Tx interrupt driven as well.
You may also wish the enable the FIFOs in the uart so you can queue up multiple characters. This can reduce the number of calls to the ISR and provide better throughput/latency because the service loop doesn't have to poll so often.

How receive data with HAL_UART?

I'm learning about the STM32. I'm want receive data by UART byte-to-byte with interruption.
HAL_UART_Receive_IT(&huart1, buffer, length)
Where &huart1 is my uart gate, buffer is the input storage and length is the amount of input bytes. I use the following function to read data
static requestRead(void *buffer, uint16_t length)
{
uint8_t teste;
while (HAL_UART_Receive_IT(&huart1, buffer, length) != HAL_OK) osDelay(1);
//HAL_UART_RxCpltCallback
}
I store my data in:
void StartDefaultTask(void const *argument)
{
char sender[] = "Alaska Sending\n";
uint8_t receive[10];
uint8_t data[30];
for (;;)
{
uint8_t i = 0;
memset(data, 0, 30);
requestRead(&receive, 1);
data[i++] = receive;
while (data != '\r')
{
requestRead(&receive, 1);
data[i++] = receive;
}
//HAL_UART_Transmit(&huart1, data, i, HAL_MAX_DELAY);
}
/* USER CODE END StartDefaultTask */
}
My problem is the value receive and store. When I send by serial a string of character as Welcome to Alaska\n, only W is read and stored, then I need send again the buffer and again just store only W. How solve this?
Well, there are a few issues here.
Arrays and their contents
data[i++] = receive;
stores the address of the receive buffer, a memory pointer value, into the data array. That's certainly not what you want. As this is a very basic C programming paradigm, I'd recommend reviewing the chapter on arrays and pointers in a good C textbook.
What you send and what you expect
while (data != '\r')
Even if you'd get the array address and its value right (see above), you are sending a string terminated with '\n', and check for a '\r' character, so change one or the other to get a match.
Missing volatile
uint8_t receive[10];
The receive buffer should be declared volatile, as it would be accessed by an interrupt handler. Otherwise the main program would miss writes to the buffer even if it had checked whether the receiving is complete (see below).
Working with hardware in realtime
while (HAL_UART_Receive_IT(&huart1, buffer, length) != HAL_OK) osDelay(1);
This would enable the UART receive (and error handling) interrupt to receive one byte. That's fine so far, but the function returns before receiving the byte, and as it's called again immediately, it would return HAL_BUSY the second time, and wait a millisecond before attempting it again. In that millisecond, it would miss most of the rest of the transmission, as bytes are arriving faster than that, and your program does nothing about it.
Moreover, the main program does not check when the receive is complete, possibly accessing the buffer before the interrupt handler places a value in it.
If you receive data using interrupts, you'd have to do something about that data in the interrupt handler. (If you don't use interrupts, but polling for data, then be sure that you'd meet the deadline imposed by the speed of the interface).
HAL is not suited for this kind of tasks
HAL has no interface for receiving an unknown length of data terminated by a specific value. Of course the main program can poll the receiver one byte at a time, but then it must ensure that the polling occurs faster than the data comes in. In other words, the program can do very little else while expecting a transmission.
There are some workarounds, but I won't even hint at them now, because they would lead to deadlocks in an RTOS environment which tend to occur at the most inconvenient times, and are quite hard to investigate and to properly avoid.
Write your interrupt handler instead
(all of this is detailed in the Reference Manual for your controller)
set the UART interrupt priority NVIC_SetPriority()
enable the interrupt with NVIC_EnableIRQ()
set the USART_CR1_RXNEIE bit to 1
in the interrupt handler,
read the SR register
if the RXNE bit is set,
read the data from the data register
store it in the buffer
set a global flag if it matches the terminating character
switch to another buffer if more data is expected while the program processes the first line.
Don't forget declaring declaring all variables touched by the interrupt handler as volatile.

Why does stm32f4 uart skip some characters when receiving using an interrupt

Having some problems trying to receive characters from a serial Putty Session. When I type sentences/multiple characters into Putty to send character to the stm32f4 microcontroller, it doesn't seem to receive all of the characters using an interrupt approach.
So two questions:
Not sure what is going on here. Am I missing something?
I am a little confused when an interrupt is called for receiving characters. Is the interrupt called for each character or do you process the whole string with one interrupt call?
Code:
void USART_Write(USART_TypeDef * USARTx, char * str) {
while(*str){
// First check if the Transmission Data register is empty
while ( !(USARTx->SR & USART_SR_TXE)){};
// Write Data to the register which will then get shifted out
USARTx->DR =(*str &(uint16_t) 0x01FF);
str++;
}
// Wait until transmission is complete
while ( !(USARTx->SR & USART_SR_TC)){};
}
void receive(USART_TypeDef * USARTx, volatile char *buffer,
volatile uint32_t * pCounter){
if(USARTx->SR & USART_SR_RXNE){
char c = USARTx->DR;
USART_Write(USARTx,&c);
USART_Write(USARTx,"\n\r");
}
}
void USART2_IRQHandler(void){
receive(USART2, USART2_Buffer_Rx,&Rx2_Counter);
}
Putty Session:
(I type asdfghjkl into Putty and print out the receiving characters using the USART_WRITE(...) function)
asdfghjkl
a
s
g
k
USART_Write(USARTx,&c);
...........
while(*str)...
NO! Single chars are not NUL-terminated char arrays!
You must not use a polled tx approach directly from an rx interrupt-handler. It will likely result in rx overrun as the rx buffer gets overwritten by newly received chars while the rx interrupt-handler is stuck polling the tx registers.
You need a tx interrupt and some kind of buffer, eg. a circular char queue.
Yes, I know it's a bit of a pain handling tx interrupts, circular buffers etc. If the tx interrupt finds no more chars to send it has to exit having sent none. This means that when the rx interrupt next needs to queue up char to send, it must load it into the tx register instead of the queue in order to 'prime' the tx interrupt mechanism.
Things get even more umm 'interesting' if the chars need to traverse waiting non-interrupt thread/s and/or need to be blocked up into protocol units.
Anyway, whatever, you must not use this mixed interrupt/polling. It will not work reliably and the extra delays will adversely affect other interrupts, especially lower-priority interrupts, that will remain disabled for long periods:(

AT commands in embedded systems

I am using embedded C and trying to make application for GPRS terminal. My main problem is working with AT commands. I send AT command using serial line, but if it is some network oriented command its response could take time and because of that I have a lot of waiting, while processor don`t do anything. Idea is to make this waiting to be done in same way parallel like in different thread. Does anyone have idea how to do that because my system does not support threads?
I had idea to use some timers, because we have interrupt which is called every 5ms, but I don't know ho many seconds I have to wait for response, and if I compare strings in interrupt to check if all message is received it could be very inefficient, right?
you could either use interrupts, configure the serial interface to interrupt when data is available, or use an RTOS something, like FreeRTOS, to run two threads, one for the main code and the other to block and wait for the serial data.
Update: based on your comments, you say you don't know the size of the data, that's fine, in the interrupt handler check for the byte that terminates the data, this is a simple and generic example you should check the examples for your MCU:
void on_serial_char()
{
//disable interrupts
disable_interrupts();
//read byte
byte = serial.read();
//check if it's the terminating byte
if (byte == END) {
//set the flag here
MESSAGE_COMPLETE = 1;
}
//add byte to buffer
buf[length++] = byte;
//enable interrupts
enable_interrupts();
}
And check for that flag in your main loop:
...
if (MESSAGE_COMPLETE) {
//process data
...
//you may want to clear the flag here
MESSAGE_COMPLETE = 0;
//send next command
...
}
You can simply call a packetHandler in each mainLoopCycle.
This handler checks if new characters are available from the serial port.
The packetHandler will build the response message bit for bit, if the message is complete (CR LF found) then it calls a messageReceive function, else it simply returns to the mainLoop.
int main()
{
init();
for (;;)
{
packetHandler();
}
}
char msgBuffer[80];
int pos=0;
void packetHandler()
{
char ch;
while ( isCharAvailable() )
{
ch=getChar();
msgBuffer[pos++] = ch;
if ( ch == '\n' )
{
messageReceived(msgBuffer);
pos=0;
}
}
}
It sounds like you are rather close to the hardware drivers. If so, the best way is to use DMA, if the MCU supports it, then use the flag from the DMA hardware to determine when to start parse out the received data.
The second best option is to use rx interrupts, store every received byte in a simple FIFO, such as a circular buffer, then set some flag once you have received them. One buffer for incoming data and one for the latest valid data received may be necessary.

Resources