What is the proper way to process interrupts? - c

I am working on a project that requires USB communication.
I'm using a Nuvoton NUC123 which runs an ARM cortex M0 core, with a speed of 48MHz, 20kb RAM and 64kb flash memory.
The microcontroller implements a hardware interrupt whenever a USB endpoint gets data transmitted to it from the host, whether it's an Ack, Nak or a setup packet.
The sample code supplied by the manufacturer is rather dirty, it involves switch-case-ing the endpoint to which the interrupt belongs and if it is a setup packet containing a class-specific request, it make a switch-case for every interface or endpoint that may be the target of the request.
I figured I can make things prettier by defining an array of structure:
typedef void UsbEventCallback(uint32_t u32IntFlag, uint32_t u32EPSTS);
typedef uint32_t UsbClassReqCallback(void);
typedef struct
{
uint8_t ep_address;
uint32_t config;
uint32_t buff_offset;
UsbClassReqCallback *usb_classreq_cb;
UsbEventCallback *usb_event_cb;
} ATTR_PACKED EP_Config_Struct;
typedef struct
{
uint8_t interface_id;
UsbClassReqCallback *usb_classreq_cb;
} ATTR_PACKED Interface_Config_Struct;
extern const EP_Config_Struct EP_config_settings[TOTAL_NUM_ENDPOINTS];
extern const Interface_Config_Struct interfaces_config_settings[TOTAL_NUM_INTERFACES];
and then, in the interrupt callback I do:
switch( req_destination )
{
case 1: //interface
for ( uint8_t interface_index = 0 ; interface_index < TOTAL_NUM_INTERFACES ; interface_index++ )
{
if ( interfaces_config_settings[interface_index].interface_id == UsbDev.Setup.wIndex )
{
if ( interfaces_config_settings[interface_index].usb_classreq_cb == NULL )
return FALSE;
else
return (*interfaces_config_settings[interface_index].usb_classreq_cb)();
}
}
break;
case 2: //endpoint
for ( uint8_t ep_index = 0 ; ep_index < TOTAL_NUM_ENDPOINTS ; ep_index++ )
{
if ( EP_config_settings[ep_index].ep_address == UsbDev.Setup.wIndex )
{
if ( EP_config_settings[ep_index].usb_classreq_cb == NULL )
return FALSE;
else
return (*EP_config_settings[ep_index].usb_classreq_cb)();
}
}
break;
}
return FALSE;
My questions are:
Is it better to not actually make all these decisions and calling all these other functions in interrupt time? Am I better to just save the interrupt data and switch some flag on requiring the main thread to process the interrupt?
How important is it to return from the callback as soon as possible?
What do you think is the correct architecture for such a program?
Thank you

It is hard to say without knowing precisely your application, but your interrupt handler looks quite reasonable.
Generally for multi-tasks systems it is advised to do the least possible in interrupt handlers, because while an interrupt is being handled the different tasks on the systems are not being scheduled any more. This can be a lot more complicated than that, especially when using interrupt priorities and interrupt nesting, but still the general idea is to avoid staying too long in interrupt handlers.
For your USB driver, I would select the appropriate endpoint/interface in the interrupt handler, then write the data received in the appropriate queue/array and finally trigger a flag/semaphore to signal that some data has been received. I would then parse the data received in a normal task rather than directly in the interrupt handler, to keep the interrupt handler minimal.

Not sure whether it's critical to keep busy ISR in your project but in principle interrupt handler should return as soon as possible. If i were you I would do following regarless of the situation.
Parse protocol in ISR and then feed data to a ring buffer as a parsed packets. Ring buffer may require the ability of variable length data peek/push/pop according the protocol. Then proceed time consuming work in main.

Related

How HAL_UART_Transmit_IT manages sending data on serial on the STM32F091VB

I'm trying to understand how the STM32F091VB manages the send of data via serial protocol with the function HAL_UART_Transmit_IT()
At the moment I've a function called in the main() that creates the packet and send it via serial; it is something like this:
tx1[0] = STX;
tx1[1] = 0xFF;
tx1[2] = 0x80;
tx1[3] = 0x80;
DE_TAST_HIGH;
HAL_UART_Transmit_IT(&huart3, tx1, 8);
Now, the data I'm sending is quite small so the code run pretty fast and I'm trying to understand what's going to happen if I try to send a huge packet via serial protocol.
For istance, if my tx1[] is 100byte the HAL_UART_Transmit_IT() function block the CPU waiting while the full packet is sent to the serial port or it works more like a separate process where I tell the micro to send that packet and, while sending it it also process the remaining part of my code/main function?
I've tried to search on the micro datasheet to see if there was something about this process but I had no luck. I've read the stm32f0xx_hal_uart.c and it confirms that it is sent via interrupt in a non blocking mode but I would like to have some more in depth documentation about it
First of all you need to understand how the HAL_UART_Transmit_IT is meant to be used. We can get some help from STM FAQ.
The function is "non blocking" because when you call it it will do some configuration of the interrupts and then return. The buffer will not be transmitted during the call to your function, instead the heavy lifting is deferred to a later stage.
We can further have a look at the source code, to get a proof from what I said (note I kept only the juicy parts).
Blocking
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
uint16_t* tmp;
uint32_t tickstart = 0U;
// [ ... ]
huart->TxXferSize = Size;
huart->TxXferCount = Size;
while(huart->TxXferCount > 0U)
{
// [ ... ]
// This is were the actual HW regs are accessed, starting the transfer
huart->Instance->DR = (*pData++ & (uint8_t)0xFF);
}
}
// [ ... ]
return HAL_OK
}
Non Blocking
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
huart->pTxBuffPtr = pData;
huart->TxXferSize = Size;
huart->TxXferCount = Size;
/* Enable the UART Transmit data register empty Interrupt */
// This is the only part were HW regs are accessed. What is happening here??
SET_BIT(huart->Instance->CR1, USART_CR1_TXEIE);
return HAL_OK;
}
The _IT function only activates one interrupt, based also on the datasheet:
This means we will receive an interrupt whenever the TX buffer is free. Who is actually sending the data then?
With the help of the FAQs and reading the source code, we find that void HAL_UART_IRQHandler(UART_HandleTypeDef *huart) does something like this:
/* UART in mode Transmitter ------------------------------------------------*/
if(((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
{
UART_Transmit_IT(huart);
return;
}
Which in turn calls the UART_Transmit_IT
static HAL_StatusTypeDef UART_Transmit_IT(UART_HandleTypeDef *huart)
{
uint16_t* tmp;
/* Check that a Tx process is ongoing */
if(huart->gState == HAL_UART_STATE_BUSY_TX)
{
huart->Instance->DR = (uint8_t)(*huart->pTxBuffPtr++ & (uint8_t)0x00FF);
if(--huart->TxXferCount == 0U)
{
/* Disable the UART Transmit Complete Interrupt */
CLEAR_BIT(huart->Instance->CR1, USART_CR1_TXEIE);
/* Enable the UART Transmit Complete Interrupt */
SET_BIT(huart->Instance->CR1, USART_CR1_TCIE);
}
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
This function transmits only one byte! It then decrements the counter for the transmission (remember, all the information was set into the uart handler) and if it reaches 0, finally the complete interrupt is called.
Interrupts
Note that StmCube does the peripheral initialization and interrupt linking for you, but if you program from scratch you need to remember to write and register UART_IRQ_Handler
You can find here the code navigator to review my snippets and investigate further.

avr-gcc: jump to arbitrary address after ISR has been invoked

I'm working with an ATmega168p and compiling with avr-gcc.
Specifically, I have an RS485 slave that receives bytes via UART and writes them to a buffer in an ISR. If an end character is received, a flag is set in the ISR. In my main loop this flag is checked and the input buffer is processed if necessary. However, there is the problem that some time can pass between the arrival of the end byte and the time when the handler in the main loop processes the input buffer, because of the other "stuff".
This results in a latency which can be up to several milliseconds, because e.g. sensors are read in every n-th iterations.
ISR(UART_RX_vect) {
write_byte_to_buffer();
if (byte==endbyte) // return to <HERE>
}
void main(){
init();
for(;;){
// <HERE> I want my program to continue after the ISR received an end byte
handle_buffer();
do_stuff(); // "stuff" may take a while
}
I want to get rid of this latency, as it is the bottleneck for the higher-level system.
I would like that after the ISR received the end byte, the program returns to the beginning of my main loop, where the input buffer would be processed immediately. I could of course process the input buffer directly in the ISR, but I am aware that this is not a good practice. This would also overwrite packets when the ISR gets invoked while processing a packet.
So, is there a way to overwrite an ISR's return address? Does C include such a feature, maybe something like goto?
Or am I completely on the wrong track?
Edit: Below is a reduced version of my code which also causes the described latency.
#define F_CPU 8000000UL
#define BAUD 38400
#define BUFFER_LENGTH 64
#include <util/setbaud.h>
#include <avr/interrupt.h>
#include <stdbool.h>
volatile char input_buffer[BUFFER_LENGTH + 1] = "";
volatile uint8_t input_pointer = 0;
volatile bool packet_started=false;
volatile bool packet_available = false;
ISR (USART_RX_vect) {
unsigned char nextChar;
nextChar = UDR0;
if (nextChar=='<') {
input_pointer=0;
packet_started=true;
}
else if (nextChar=='>' && packet_started) {
packet_started=false;
packet_available=true;
}
else {
if (input_pointer>=BUFFER_LENGTH) {
input_pointer=0;
packet_started=false;
packet_available=false;
}
else {
input_buffer[input_pointer++]=nextChar;
}
}
}
bool ADC_handler () {
ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
ADCSRA |= (1<<ADSC);
while (ADCSRA & (1<<ADSC)); // this loop blocks and causes latency
// assigning conversion result to a variable (not shown)
}
void ADC_init(void) {
ADMUX = (1<<REFS1)|(1<<REFS0)|(1<<MUX3);
ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
}
void process_buffer() {
// this function does something with the buffer
// but it takes "no" time and is not causing latency
return;
}
void UART_handler () {
if (packet_available) process_buffer();
}
void UART_init (void) {
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
UCSR0B |= (1<<RXCIE0)|(1<<RXEN0)|(1<<TXEN0);
UCSR0C |= (1<<UCSZ01)|(1<<UCSZ00);
}
int main(void){
UART_init();
ADC_init();
// initializing some other things
sei();
for(;;){
UART_handler();
ADC_handler();
// other handlers like the ADC_handler follow
}
return 0;
}
I'm aware that the latency is due to blocking code, in this case the while loop in the ADC_handler() that waits for the conversion to finish. I could check for packet_available in the ADC handler and make this funtion return if the flag is set or I could even retrieve the conversion result with an ADC interrupt. That's all nice because I'm the one who implements the ADC_handler(). But if I wanted to use third party libraries (e.g. sensor libraries provided by manufacturers) I would depend on how those libraries are implemented. So what I'm looking for is a way to handle the problem "on my side"/in the UART implementation itself.
Don't try to use setjmp()/longjmp() to re-enter a main-level function from an ISR. This calls for disaster, because the ISR is never finished correctly. You might like to use assembly to work around, but this is really fragile. I'm not sure that this works at all on AVRs.
Since your baudrate is 38400, one byte needs at least some 250µs to transfer. Assumed that your message has a minimum of 4 bytes, the time to transfer a message is at least 1ms.
There are multiple possible solutions; your question might be closed because they are opinion-based...
However, here are some ideas:
Time-sliced main tasks
Since a message can arrive only once per millisecond or less, your application don't need to be much faster than that.
Divide your main tasks into separated steps, each running faster than 1 ms. You might like to use a state machine, for example to allow slower I/O to finish.
After each step, check for a completed message. Using a loop avoids code duplication.
Completely interrupt-based application
Use a timer interrupt to do the repeated work. Divide it in short tasks, a state machine does magic here, too.
Use an otherwise unused interrupt to signal the end of the message. Its ISR may run a bit longer, because it will not be called often. This ISR can handle the message and change the state of the application.
You need to think about interrupt priorities with much care.
The endless loop in main() will effectively be empty, like for (;;) {}.

MSP430 I2C slave holding clock line low

I'm more of a high level software guy but have been working on some embedded projects lately so I'm sure there's something obvious I'm missing here, though I have spent over a week trying to debug this and every 'MSP' related link in google is purple at this point...
I currently have an MSP430F5529 set up as an I2C slave device whose only responsibility currently is to receive packets from a master device. The master uses industry grade I2C and has been heavily tested and ruled out as the source of my problem here. I'm using Code composer as my IDE using the TI v15.12.3.LTS compiler.
What is currently happening is the master queries how many packets (of size 62 bytes) the slave can hold, then sends over a few packets which the MSP is just currently discarding. This is happening every 100ms on the master side and for the minimal example below the MSP will always just send back 63 when asked how many packets it can hold. I have tested the master with a Total Phase Aardvark and everything is working fine with that so I'm sure it's a problem on the MSP side. The problem is as follows:
The program will work for 15-20 minutes, sending over tens of thousands of packets. At some point the slave starts to hold the clock line low and when paused in debug mode, is shown to be stuck in the start interrupt. The same sequence of events is happening every single time to cause this.
1) Master queries how many packets the MSP can hold.
2) A packet is sent successfully
3) Another packet is attempted but < 62 bytes are received by the MSP (counted by logging how many Rx interrupts I receive). No stop condition is sent so master times out.
4) Another packet is attempted. A single byte is sent before the stop condition is sent.
5) Another packet is attempted to be sent. A start interrupt, then a Tx interrupt happens and the device hangs.
Ignoring the fact that I'm not handling the timeout errors on the master side, something very strange is happening to cause that sequence of events, but that's what happens every single time.
Below is the minimal working example which is reproducing the problem. My particular concern is with the SetUpRx and SetUpTx functions. The examples that the Code Composer Resource Explorer gives only has examples of Rx or Tx, I'm not sure if I'm combining them in the right way. I also tried removing the SetUpRx completely, putting the device into transmit mode and replacing all calls to SetUpTx/Rx with mode = TX_MODE/RX_MODE, which did work but still eventually holds the clock line low. Ultimately I'm not 100% sure on how to set this up to receive both Rx and Tx requests.
#include "driverlib.h"
#define SLAVE_ADDRESS (0x48)
// During main loop, set mode to either RX_MODE or TX_MODE
// When I2C is finished, OR mode with I2C_DONE, hence upon exit mdoe will be one of I2C_RX_DONE or I2C_TX_DONE
#define RX_MODE (0x01)
#define TX_MODE (0x02)
#define I2C_DONE (0x04)
#define I2C_RX_DONE (RX_MODE | I2C_DONE)
#define I2C_TX_DONE (TX_MODE | I2C_DONE)
/**
* I2C message ids
*/
#define MESSAGE_ADD_PACKET (3)
#define MESSAGE_GET_NUM_SLOTS (5)
static volatile uint8_t mode = RX_MODE; // current mode, TX or RX
static volatile uint8_t rx_buff[64] = {0}; // where to write rx data
static volatile uint8_t* rx_data = rx_buff; // used in rx interrupt
static volatile uint8_t tx_len = 0; // number of bytes to reply with
static inline void SetUpRx(void) {
// Specify receive mode
USCI_B_I2C_setMode(USCI_B0_BASE, USCI_B_I2C_RECEIVE_MODE);
// Enable I2C Module to start operations
USCI_B_I2C_enable(USCI_B0_BASE);
// Enable interrupts
USCI_B_I2C_clearInterrupt(USCI_B0_BASE, USCI_B_I2C_TRANSMIT_INTERRUPT);
USCI_B_I2C_enableInterrupt(USCI_B0_BASE, USCI_B_I2C_START_INTERRUPT + USCI_B_I2C_RECEIVE_INTERRUPT + USCI_B_I2C_STOP_INTERRUPT);
mode = RX_MODE;
}
static inline void SetUpTx(void) {
//Set in transmit mode
USCI_B_I2C_setMode(USCI_B0_BASE, USCI_B_I2C_TRANSMIT_MODE);
//Enable I2C Module to start operations
USCI_B_I2C_enable(USCI_B0_BASE);
//Enable master trasmit interrupt
USCI_B_I2C_clearInterrupt(USCI_B0_BASE, USCI_B_I2C_RECEIVE_INTERRUPT);
USCI_B_I2C_enableInterrupt(USCI_B0_BASE, USCI_B_I2C_START_INTERRUPT + USCI_B_I2C_TRANSMIT_INTERRUPT + USCI_B_I2C_STOP_INTERRUPT);
mode = TX_MODE;
}
/**
* Parse the incoming message and set up the tx_data pointer and tx_len for I2C reply
*
* In most cases, tx_buff is filled with data as the replies that require it either aren't used frequently or use few bytes.
* Straight pointer assignment is likely better but that means everything will have to be volatile which seems overkill for this
*/
static void DecodeRx(void) {
static uint8_t message_id = 0;
message_id = (*rx_buff);
rx_data = rx_buff;
switch (message_id) {
case MESSAGE_ADD_PACKET: // Add some data...
// do nothing for now
tx_len = 0;
break;
case MESSAGE_GET_NUM_SLOTS: // How many packets can we send to device
tx_len = 1;
break;
default:
tx_len = 0;
break;
}
}
void main(void) {
//Stop WDT
WDT_A_hold(WDT_A_BASE);
//Assign I2C pins to USCI_B0
GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P3, GPIO_PIN0 + GPIO_PIN1);
//Initialize I2C as a slave device
USCI_B_I2C_initSlave(USCI_B0_BASE, SLAVE_ADDRESS);
// go into listening mode
SetUpRx();
while(1) {
__bis_SR_register(LPM4_bits + GIE);
// Message received over I2C, check if we have anything to transmit
switch (mode) {
case I2C_RX_DONE:
DecodeRx();
if (tx_len > 0) {
// start a reply
SetUpTx();
} else {
// nothing to do, back to listening
mode = RX_MODE;
}
break;
case I2C_TX_DONE:
// go back to listening
SetUpRx();
break;
default:
break;
}
}
}
/**
* I2C interrupt routine
*/
#pragma vector=USCI_B0_VECTOR
__interrupt void USCI_B0_ISR(void) {
switch(__even_in_range(UCB0IV,12)) {
case USCI_I2C_UCSTTIFG:
break;
case USCI_I2C_UCRXIFG:
*rx_data = USCI_B_I2C_slaveGetData(USCI_B0_BASE);
++rx_data;
break;
case USCI_I2C_UCTXIFG:
if (tx_len > 0) {
USCI_B_I2C_slavePutData(USCI_B0_BASE, 63);
--tx_len;
}
break;
case USCI_I2C_UCSTPIFG:
// OR'ing mode will let it be flagged in the main loop
mode |= I2C_DONE;
__bic_SR_register_on_exit(LPM4_bits);
break;
}
}
Any help on this would be much appreciated!
Thank you!

Simple Interrupt Handling/ Multi-threading program?

I'm new to Embedded programming and multi-threading and I'm trying to understand how Interrupt handlers work in different contexts/scenarios. For the current question, I just want to know how a interrupt handler would work in the following scenario.
We have a data stream coming from a RS232 interface that is processed by some microcontroller. An interrupt handler(of void type) has a read() function which reads the incoming data bytes. If a character is detected then the interrupt handler invokes a function called detectString() which returns TRUE if the string matches the reference string which is "ON". If detectString() returns boolean TRUE it invokes a function called LED_ON() which should turn on an LED for 1 minute. If it returns false it should turn off the LED. Lets say the microcontroller has a clock frequency of 20MHz and an addition operation taken 5 clock cycles.
My questions are as follows
How do we approach this problem with an FSM?
The RS232 interface keeps transmitting data even after the LED is turned on. So am I correct in assuming that the interrupt handler should work with a one thread and the functions that it invokes should work from a different threads?
How would a skeletal program implementing this FSM look like? (a C pseudocode might really help to understand the backbone of the design)
If you are doing this in an interrupt handler, why would you need different threads? It shouldn't matter what else you're doing, as long as interrupts are enabled.
As for FSM, I wouldn't call a "detect_string". RS232 is going to give you one character at a time. It's possible your UART interrupts you only when you've received more than one, but there's usually a time component as well so it would be unwise to count on that. Make your FSM take one input character at a time. Your states would be something like:
=> new state = [Init] (turn LED off if on)
Init: (Get 'O') => new state = [GotO]
Init: (Get anything else) => new state = [Init]
Init: (Timer expires) => who cares? new state = [Init]
GotO: (Get 'N') => new state = [GotON] (turn on LED, set timer)
GotO: (Get anything else) => new state = [Init]
GotO: (Timer expires) => who cares? new state = [GotO]
GotON: (Get anything) => who cares? new state = [GotON]
GotON: (Timer expires) => turn LED off, new state = [Init]
Obviously lots of tinkering you could do with details, but that's the general idea.
A preemptive kernel will usually provide the ability for an interrupt to set an event that a higher priority thread is pending on.
As for the interrupts, one way of implementing something like a state machine is to use nested pointers to function, similar to an asynchronous callback, but with optional nesting: For example:
typedef void (*PFUN)(void);
/* ... */
PFUN pFunInt = UnexpectedInt; /* ptr to function for interrupt */
PFUN pFunIntSeqDone;
/* ... */
void DoSeq(void)
{
pFunIntSeqDone = IntSeqDone;
pFunInt = IntStep0;
/* enable interrupt, start I/O */
}
void IntStep0(void)
{
pFunInt = IntStep1;
/* handle interrupt */
}
void IntStep1(void)
{
pFunInt = IntStep2;
/* handle interrupt */
}
void IntStep2(void)
{
/* done with sequence, disable interrupt */
pFunInt = UnexpectedInt;
pFunIntSeqDone(); /* call end action handler */
}
void IntSeqDone(void)
{
/* interrupt sequence done handling code */
/* set event for pending thread */
}
void UnexpectedInt(void)
{
/* ... error handling code */
}

How to throttle Interrupt Driven UART transmission PIC24H?

I am transmitting data from my PIC24H microcontroller over 460Kbaud UART to a bluetooth radio module. Under most conditions, this flow works just fine and the bluetooth module uses CTS and RTS lines to manage flow control when its internal data buffers are full. However, there is a bug of some kind in the bluetooth module that resets it when data is continuously sent to it without any breaks, which happens if my data gets backed up in another bottleneck.
It would be nice if the module worked properly, but that's out of my control. So it seems that my only option is to do some data throttling on my end to make sure I don't exceed the data throughput limits (which I know roughly by experimentation).
My question is how to implement data rate throttling?
My current UART implementation is a RAM circular FIFO buffer 1024 bytes long that the main loop writes data to. A peripheral interrupt is triggered by the PIC when the last byte has been sent out by the UART hardware and my ISR reads the next byte from the buffer and sends it to the UART hardware.
Here's an idea of the source code:
uart_isr.c
//*************** Interrupt Service routine for UART2 Transmission
void __attribute__ ((interrupt,no_auto_psv)) _U2TXInterrupt(void)
{
//the UART2 Tx Buffer is empty (!UART_TX_BUF_FULL()), fill it
//Only if data exists in data buffer (!isTxBufEmpty())
while(!isTxBufEmpty()&& !UART_TX_BUF_FULL()) {
if(BT_CONNECTED)
{ //Transmit next byte of data
U2TXREG = 0xFF & (unsigned int)txbuf[txReadPtr];
txReadPtr = (txReadPtr + 1) % TX_BUFFER_SIZE;
}else{
break;
}
}
IFS1bits.U2TXIF = 0;
}
uart_methods.c
//return false if buffer overrun
BOOL writeStrUART(WORD length, BYTE* writePtr)
{
BOOL overrun = TRUE;
while(length)
{
txbuf[txWritePtr] = *(writePtr);
//increment writePtr
txWritePtr = (txWritePtr + 1) % TX_BUFFER_SIZE;
if(txWritePtr == txReadPtr)
{
//write pointer has caught up to read, increment read ptr
txReadPtr = (txReadPtr + 1) % TX_BUFFER_SIZE;
//Set overrun flag to FALSE
overrun = FALSE;
}
writePtr++;
length--;
}
//Make sure that Data is being transmitted
ensureTxCycleStarted();
return overrun;
}
void ensureTxCycleStarted()
{
WORD oldPtr = 0;
if(IS_UART_TX_IDLE() && !isTxBufEmpty())
{
//write one byte to start UART transmit cycle
oldPtr = txReadPtr;
txReadPtr = (txReadPtr + 1) % TX_BUFFER_SIZE;//Preincrement pointer
//Note: if pointer is incremented after U2TXREG write,
// the interrupt will trigger before the increment
// and the first piece of data will be retransmitted.
U2TXREG = 0xFF & (unsigned int)txbuf[oldPtr];
}
}
Edit
There are two ways that throttling could be implemented as I see it:
Enforce a time delay in between UART byte to be written that puts an upper limit on data throughput.
Keep a running tally of bytes transmitted over a certain time frame and if the maximum number of bytes is exceeded for that timespan create a slightly longer delay before continuing transmission.
Either option would theoretically work, its the implementation I'm wondering about.
Maybe a quota approach is what you want.
Using a periodic interrupt of relevant timescale, add a quota of "bytes to be transmitted" to a global variable to a point that you don't go over some level adjusted for the related deluge.
Then just check if there is quota before you come to send a byte. On new transmission there will be an initial deluge but later the quota will limit the transmission rate.
~~some periodic interrupt
if(bytes_to_send < MAX_LEVEL){
bytes_to_send = bytes_to_send + BYTES_PER_PERIOD;
}
~~in uart_send_byte
if(bytes_to_send){
bytes_to_send = bytes_to_send - 1;
//then send the byte
If you have a free timer, or if you can use an existing one, you could do some kind of "debounce" of the bytes sent.
Imagine you have this global var, byte_interval and you have a timer overflowing (and triggering the ISR) every microsecond. Then it could look something like this:
timer_usec_isr() {
// other stuff
if (byte_interval)
byte_interval--;
}
And then in the "putchar" function, you could have something like:
uart_send_byte(unsigned char b) {
if (!byte_interval) { // this could be a while too,
// depends on how you want to structure the code
//code to send the byte
byte_interval = AMOUNT_OF_USECS;
}
}
I'm sorry to not look much into your code so I could be more specific.
This is just an idea, I don't know if it fits for you.
First, there's two types of serial flow control in common use.
CTS/RTS handshaking ('hardware flow control')
XON/XOFF ('software flow control')
You say CTS is on, but you might want to see if XON/XOFF can be enabled in some way.
Another approach if you can configure it is simply to use a lower baud rate. This obviously depends on what you can configure on the other end of the link, but it's usually the easiest way of fixing problems when devices aren't able to cope with higher speed transfers.
Timer approach which adds delay to Tx at specific time:
Configure a free running timer at an appropriate periodic rate.
In the timer ISR, toggle a bit in a global state variable (delayBit)
In the UART ISR, if delayBit is high and delayPostedBit is low, then exit the TX ISR without clearing the TX interrupt flag and set a bit in a global state variable (delayPostedBit). If delayBit is low, then clear delayPostedBit. The result is to cause a delay equal to one ISR schedule latency, since the ISR will be entered again. This is not a busy-wait delay so won't affect the timing of the rest of the system.
Adjust the period of the timer to add latency at appropriate intervals.

Resources