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.
Related
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:(
Suppose you have some data transmitting peripheral, like a UART, that signals an interrupt whenever it's ready to transmit more data. We're sending data from a circular buffer, where tail is where the data is removed from, head is where you add data, and tail == head means that there's no more data to transmit.
Let's also assume that the peripheral has no buffering whatsoever, and you can't pass it the next value to send while it's busy sending the current one. If you need a concrete, if made-up, example, think of a shift register attached directly to a CPU's parallel I/O port.
To keep transmitter as busy as possible, you might wish to transmit as soon as the transmit interrupt handler is entered. When there's no data to transmit, the interrupt is masked out and the handler will not be invoked even though the interrupt has been armed. The system starts in with the interrupt masked out.
I'll use C to illustrate things, although the issue is not C-specific. The interrupt handler, and the buffer, are set up as follows:
char buf[...];
char * head = buf; ///< write pointer
char * tail = buf; ///< read pointer
char * const first = buf; ///< first byte of the buffer
char * const last = buf+sizeof(buf)-1; ///< last byte of the buffer
/// Sends one byte out. The interrupt handler will be invoked as soon
/// as another byte can be sent.
void transmit(char);
void handler() {
transmit(*tail);
if (tail == last)
tail = first;
else
tail++;
if (tail == head)
mask_interrupt();
}
So far, so good. Now let's see how one might implement putch(). We can invoke putch() in bursts much faster than the device is able to send the data out. Let's assume that the caller knows not to overflow the buffer.
void putch(char c) {
*head = c;
if (head == last)
head = first;
else
head++;
/***/
unmask_interrupt();
}
Suppose now that these things happen:
The transmitter was busy, and when putch was called, there a byte was being sent.
The transmission happens to finish when putch is in the spot marked /***/ above. The handler() happens to execute right there.
The handler() happens to send the last byte of the data in the buffer - the byte that we have just loaded in preceding lines in putch().
The handler masks the interrupt, as there's no more data to send, but putch incorrectly unmasks it right after handler() returns. Thus the handler will have another go through the buffer, and will send a buffer's worth of stale data until tail equals head again.
My questions is: Is the only fix to increase the latency and check for empty buffer before sending in the handler? The fixed code looks as follows:
void fixed_handler() {
if (head == tail) {
mask_interrupt();
arm_interrupt(); // so that next time we unmask it, we get invoked
return;
}
transmit(*tail);
if (tail == last)
tail = first;
else
tail++;
}
This fix adds some latency, and also adds an extra operation (arm_interrupt) that's executed once when there's no more data to send.
For possible other approaches, feel free to assume the existence of at least the following operations:
/// Is the interrupt armed and will the handler fire once unmasked?
bool is_armed();
/// Is the interrupt unmasked?
bool is_unmasked();
I've always done this with double-buffering, so that at any point in time the program and the UART are "owning" different buffers.
When the UART finishes sending its buffer, a swap can happen, with interrupts masked.
That way, it doesn't have to mask interrupts on every character.
One fix would be to prevent the interrupt handler from running within putch:
void putch(char c) {
*head = c;
mask_interrupt();
if (head == last)
head = first;
else
head++;
unmask_interrupt();
}
This lets us use the original transmit-first interrupt handler. The problem with that is that overall it increases the number of operations performed per byte sent. It also increases the peak latency as well, since there are now times when handler() simply won't run even though the hardware is ready for more data and there's data to be sent.
The average latency in getting the transmitter busy again is determined by the interrupt handler. The peak latency on top of that is determined by the code that delays the execution of the interrupt handler.
I am pulling my hair out with an intermittent bug. I am receiving and transmitting bytes asynchronously (on a PIC16F77) and have implemented a circular software FIFO buffer for receiving and transmitting, combined with an interrupt service routine that is triggered when a byte can be sent or has been received.
The problem is that sometimes the bytes to be transmitted are done so in the wrong order.
I would hugely appreciate either:
advice on debugging it, or
assistance spotting the problem in the code
Progress so far:
It seems it only happens when there are some bytes being received - however I have been unsuccessful narrowing it down further. I am not getting any underruns or overruns, AFAIK.
It works without problems when I alter send_char() to either: 1. bypass software buffer by waiting for space in hardware buffer, and putting the byte directly into it, or 2. put the byte into the software buffer, even if there is space in the hardware buffer.
The code: (See bottom of question for description of hardware variables)
unsigned volatile char volatile rc_buff[16];
unsigned char volatile rc_begin = 0;
unsigned char volatile rc_next_free = 0;
unsigned char volatile rc_count = 0;
unsigned volatile char volatile tx_buff[16];
unsigned char volatile tx_begin = 0;
unsigned char volatile tx_next_free = 0;
unsigned char volatile tx_count = 0;
__interrupt isr(){
// If a character has arrived in the hardware buffer
if (RCIF){
// Put it in the software buffer
if (rc_count >= 16) die(ERROR_RC_OVERFLOW);
rc_buff[rc_next_free] = RCREG;
rc_next_free = (rc_next_free + 1) % 16;
rc_count++;
}
// If there is space in hardware FIFO, and interrupt
// has been enabled because stuff in software FIFO needs to be sent.
if (TXIE && TXIF){
// Put a byte from s/w fifo to h/w fifo.
// (Here, tx_count is always > 0 (in theory))
TXREG = tx_buff[tx_begin];
tx_count--;
tx_begin = (tx_begin + 1) % 16;
// If this was the last byte in the s/w FIFO,
// disable the interrupt: we don't care
// when it has finished sending.
if(tx_count==0) TXIE = 0;
}
}
void send_char(char c){
// disable interrupts to avoid bad things happening
di();
// if the hardware buffer is empty,
if (TXIF){
// put a byte directly into the hardware FIFO
TXREG = c;
} else {
// cannot send byte directly so put in the software FIFO
if (tx_count >= 16) die(ERROR_TX_OVERFLOW);
tx_buff[tx_next_free] = c;
tx_next_free = (tx_next_free + 1) % 16;
tx_count++;
// Enable TX interrupt since it now has something
// it needs to transfer from the s/w FIFO to the h/w FIFO
TXIE = 1;
}
ei();
}
char get_char(){
// wait for a byte to appear in the s/w buffer
while (!rc_count) {
// If the h/w buffer overflowed, die with error
if (OERR) die(ERROR_RC_HW_OVERFLOW)
}
// disable interrupts to avoid bad things happening
di();
unsigned char c = rc_buff[rc_begin];
rc_count--;
rc_begin = (rc_begin + 1) % 16;
ei();
return c;
}
void send_str(const unsigned char * str){
unsigned char char_idx = 0;
// until we reach the end-of-string null character,
while (str[char_idx]){
// queue a character for sending
send_char(str[char_idx++]);
}
}
Description of hardware variables:
For reference, the following are the (volatile) variables mapped to hardware registers and flags:
RCIF // Read-only receive flag: True == byte(s) are waiting in hardware receive FIFO
TXIF // Read-only transmit flag: True == there is space in the hardware transmit FIFO
RCREG // Read only: Holds the next byte from the hardware FIFO that has been received
TXREG // Write-only: Assigning a byte to this transfers the byte to the hardware transmit FIFO
TXIE // Read/Write: Enable transmit interrupt: True == trigger ISR when TX h/w FIFO has space
RCIE // Read/Write: Enable receive interrupt: True == trigger ISR when RC h/w FIFO has a byte to be read
Also, the below are special inline functions that suspend/resume interrupts to keep multiple grouped operations atomic. (The ISR cannot be interrupted by anything, including other interrupts)
di() // suspend interrupts
ei() // re-enable interrupts
Hmmm. I think there is some logic missing in your program (I will only cover the send part, since the receiver part seems to be working?):
The interrupt routine gets triggered if there is space in the send hw FIFO. You then send out one single byte from the sw buffer, adjust the index and return (note that there might still be some bytes queued within the sw buffer after that).
Whenever you send a byte, you look for space in the HW fifo and put the byte directly there, if not, you queue it in the SW buffer.
The problem seems to me that you expect the interrupt routine to drain the software buffer before returning to send_char() which isn't necessarily the case. After returning from the interrupt, the next instruction will be fully executed (there is no interrupt in the middle of an instruction). If this next instruction is the di() in send_char(), this interrupt will not happen and there will still be bytes in the sw buffer that can only be sent later (too late).
I'd either rather enqueue the bytes into the sw buffer from send_char() instead of writing directly to the fifo from send_char() or additionally check for the sw buffer to be empty before accessing the hw fifo directly.
I have a queue structure, that I attempted to implement using a circular buffer, which I am using in a networking application. I am looking for some guidance and feedback. First, let me present the relevant code.
typedef struct nwk_packet_type
{
uint8_t dest_address[NRF24_ADDR_LEN];
uint8_t data[32];
uint8_t data_len;
}nwk_packet_t;
/* The circular fifo on which outgoing packets are stored */
nwk_packet_t nwk_send_queue[NWK_QUEUE_SIZE];
nwk_packet_t* send_queue_in; /* pointer to queue head */
nwk_packet_t* send_queue_out; /* pointer to queue tail */
static nwk_packet_t* nwk_tx_pkt_allocate(void)
{
/* Make sure the send queue is not full */
if(send_queue_in == (send_queue_out - 1 + NWK_QUEUE_SIZE) % NWK_QUEUE_SIZE)
return 0;
/* return pointer to the next add and increment the tracker */
return send_queue_in++;//TODO: it's not just ++, it has to be modular by packet size
}
/* External facing function for application layer to send network data */
// simply adds the packet to the network queue if there is space
// returns an appropriate error code if anything goes wrong
uint8_t nwk_send(uint8_t* address, uint8_t* data, uint8_t len)
{
/* First check all the parameters */
if(!address)
return NWK_BAD_ADDRESS;
if(!data)
return NWK_BAD_DATA_PTR;
if(!len || len > 32)
return NWK_BAD_DATA_LEN;
//TODO: PROBABLY NEED TO START BLOCKING HERE
/* Allocate the packet on the queue */
nwk_packet_t* packet;
if(!( packet = nwk_tx_pkt_allocate() ))
return NWK_QUEUE_FULL;
/* Build the packet */
memcpy(packet->dest_address, address, NRF24_ADDR_LEN);
memcpy(packet->data, data, len);
packet->data_len = len;
//TODO: PROBABLY SAFE TO STOP BLOCKING HERE
return NWK_SUCCESS;
}
/* Only called during NWK_IDLE, pushes the next item on the send queue out to the chip's "MAC" layer over SPI */
void nwk_transmit_pkt(void)
{
nwk_packet_t tx_pkt = nwk_send_queue[send_queue_out];
nrf24_send(tx_pkt->data, tx_pkt->data_len);
}
/* The callback for transceiver interrupt when a sent packet is either completed or ran out of retries */
void nwk_tx_result_cb(bool completed)
{
if( (completed) && (nwk_tx_state == NWK_SENDING))
send_queue_out++;//TODO: it's not just ++, it has to be modular by packet size with in the buffer
}
Ok now for a quick explanation and then my questions. So the basic idea is that I've got this queue for data which is being sent onto the network. The function nwk_send() can be called from anywhere in application code, which by the wall will be a small pre-emptive task based operating system (FreeRTOS) and thus can happen from lots of places in the code and be interrupted by the OS tick interrupt.
Now since that function is modifying the pointers into the global queue, I know it needs to be blocking when it is doing that. Am I correct in my comments on the code about where I should be blocking (ie disabling interrupts)? Also would be smarter to make a mutex using a global boolean variable or something rather than just disabling interrupts?
Also, I think there's a second place I should be blocking when things are being taken off the queue, but I'm not sure where that is exactly. Is it in nwk_transmit_pkt() where I'm actually copying the data off the queue and into a local ram variable?
Final question, how do I achieve the modulus operation on my pointers within the arrays? I feel like it should look something like:
send_queue_in = ((send_queue_in + 1) % (NWK_QUEUE_SIZE*sizeof(nwk_packet_t))) + nwk_send_queue;
Any feedback is greatly appreciated, thank you.
About locking it will be best to use some existing mutex primitive from the OS you use. I am not familiar with FreeRTOS but it should have builtin primitives for locking between interrupt and user context.
For circular buffer you may use these:
check for empty queue
send_queue_in == send_queue_out
check for full queue
(send_queue_in + 1) % NWK_QUEUE_SIZE == send_queue_out
push element [pseudo code]
if (queue is full)
return error;
queue[send_queue_in] = new element;
send_queue_in = (send_queue_in + 1) % NWK_QUEUE_SIZE;
pop element [pseudo code]
if (queue is empty)
return error;
element = queue[send_queue_out];
send_queue_out = (send_queue_out + 1) % NWK_QUEUE_SIZE;
It looks that you copy and do not just reference the packet data before sending. This means that you can hold the lock until the copy is done.
Without an overall driver framework to develop with, and when communicating with interrupt-state on a uC, you need to be very careful.
You cannot use OS synchro primitives to communicate to interrupt state. Attmpting to do so will certainly crash your OS because interrupt-handlers cannot block.
Copying the actual bulk data should be avoided.
On an 8-bit uC, I suggest queueing an index onto a buffer array pool, where the number of buffers is <256. That means that only one byte needs to be queued up and so, with an appropriate queue class that stores the value before updating internal byte-size indexes, it is possible to safely communicate buffers into a tx handler without excessive interrupt-disabling.
Access to the pool array should be thread-safe and 'insertion/deletion' should be quick - I have 'succ/pred' byte-fields in each buffer struct, so forming a double-linked list, access protected by a mutex. As well as I/O, I use this pool of buffers for all inter-thread comms.
For tx, get a buffer struct from teh pool, fill with data, push the index onto a tx queue, disable interrupts for only long enough to determine whether the tx interrupt needs 'primimg'. If priming is required, shove in a FIFO-full of data before re-enabling interrupts.
When the tx interrupt-handler has sent the buffer, it can push the 'used' index back onto a 'scavenge' queue and signal a semaphore to make a handler thread run. This thread can then take the entry from the scavenge queue and return it to the pool.
This scheme only works if interrupt-handlers do not re-enable higher-priority interrupts using the same buffering scheme.
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.