2020-12-22 08:49 AM
I'm trying to teach myself C++ on STM32. Unfortunately the books on C++ I have and most websites I found do not mention interrupt handling. The websites where it was explained (embedded.com, stackexchange.com, etc) did it in a way I simply do not (yet?) understand.
So I tried to do it in my own way with a ringbuffer and usart, and I was hoping to get some critisism on it, here goes:
File stm32f0xx_it.cpp contains
void USART1_IRQHandler(void)
{
USART_Callback_Handler();
}
File main.cpp contains (only the relevant parts)
RINGBUFFER ring_buff;
int main(void)
{
char buffer[32];
uint8_t teller = 0;
while (1)
{
sprintf(buffer, "%d\t", teller++);
ring_buff.put_str(buffer);
LL_mDelay(499);
}
}
void USART_Callback_Handler()
{
ring_buff.USART_IRQ_Callback_Handler();
}
File ringbuffer.hpp contains (also only the parts I think are relevant)
class RINGBUFFER
{
private:
volatile uint8_t tx_head;
volatile uint8_t tx_tail;
volatile uint8_t rx_head;
volatile uint8_t rx_tail;
volatile char tx_buff[TXBUFF_SIZE];
volatile char rx_buff[RXBUFF_SIZE];
public:
void put_ch(char data);
void put_str(char *s);
void USART_IRQ_Callback_Handler(void);
RINGBUFFER()
{
uint16_t i;
tx_head = 0;
tx_tail = 0;
rx_head = 0;
rx_tail = 0;
for (i = 0; i < TXBUFF_SIZE; i++) tx_buff[i] = '\0';
for (i = 0; i < RXBUFF_SIZE; i++) rx_buff[i] = '\0';
}
};
and the file ringbuffer.cpp contains (relevant parts only)
void RINGBUFFER::put_ch(char data)
{
uint8_t tx_tmp;
tx_tmp = (tx_head + 1) & TXBUFF_MASK;
while(tx_tmp == tx_tail); //blocking wait for free space in tx buffer
tx_buff[tx_tmp] = data;
tx_head = tx_tmp;
LL_USART_EnableIT_TXE(USART1); //enable TXE interrupt
}
void RINGBUFFER::put_str(char * data)
{
while (*data)
{
put_ch(*(data++));
}
}
void RINGBUFFER::USART_IRQ_Callback_Handler(void)
{
uint8_t tx_tmp;
if (LL_USART_IsActiveFlag_TXE(USART1))
{
if (tx_head == tx_tail)
{
while (LL_USART_IsActiveFlag_TC(USART1) != 1); //wait until tx complete
LL_USART_DisableIT_TXE(USART1); //disable TXE interrupt
LL_USART_ClearFlag_TC(USART1);
}
else
{
tx_tmp = (tx_tail + 1) & TXBUFF_MASK;
tx_tail = tx_tmp;
LL_USART_TransmitData8(USART1, tx_buff[tx_tmp]); //tx data
}
}
}
And it does work, The "real" USART interrupt routine in stm32f0xx_it.cpp_calls the USART callback function in main.c.
In main.cpp the instance ring_buff exists so it can call the actual code in the CLASS (RINGBUFFER::USART_IRQ_Callback_Handler(void)) to handle the transfer of data from the ringbuffer to the USART.
But it feels a bit clunky to have this "inbetween" function in main.c Any comments? Positive and negative, both welcome.
Wilko
2020-12-22 11:55 AM
Haven't your C++ or CS course teach about dependency injection?
Just think of this as of dependency injection.
-- pa
2020-12-22 07:27 PM
> But it feels a bit clunky to have this "inbetween" function in main.c Any comments?
So you want the interrupt to call ring_buff.USART_IRQ_Callback_Handler directly? Given that you can have multiple RINGBUFFER objects, how would that work exactly?
There is likely a way to hotwire it but it's probably going to hurt readability more than help it. Interrupts are single-point entry and I don't see a way around that.
2020-12-23 12:51 AM
So you are saying that this way isn't so bad after all... I still think it is odd that one has to jump through hoops to do something simple as handling an interrupt in C++, while it comes so naturally in C.
2020-12-23 12:54 AM
Well, no. As I said, I'm teaching myself C++ and I haven't seen anything about "dependency injection". But I found some things about it on the web that I'm going to study today. Thanks.
2020-12-23 05:47 AM