写入安全的 UART 中断缓冲区
Writing safe UART interrupt buffer
我知道易失性键盘,它不能确保同步安全。
以下代码用于中断例程,我大量使用了该函数
主循环中的 GetCharUART。
这样写代码安全稳定吗?或者我必须像互斥锁一样进行低级同步,如果这是真的,我将如何保护那个 rbuf?
volatile char rbuf[5][UART_BUFSIZE];
vu16 rin[5] = { 0, 0, 0, 0, 0 };
vu16 rout[5] = { 0, 0, 0, 0, 0 };
void USART_IRQHandler(char Channel, USART_TypeDef *USARTx)
{
volatile unsigned int IIR;
IIR = USARTx->SR;
if (IIR & USART_FLAG_RXNE)
{ // read interrupt
USARTx->SR &= ~USART_FLAG_RXNE; // clear interrupt
rbuf[Channel][rin[Channel]] = USART_ReceiveData(USARTx);
rin[Channel]++;
if(rin[Channel]>=UART_BUFSIZE) rin[Channel]=0;
}
if (IIR & USART_FLAG_TXE)
{
USARTx->SR &= ~USART_FLAG_TXE; // clear interrupt
}
}
int GetCharUART (char Channel)
{
int result;
if (rin[Channel]==rout[Channel]) {
return EMPTY;
}
result=rbuf[Channel][rout[Channel]];
rout[Channel]++;
if(rout[Channel]>=UART_BUFSIZE)
rout[Channel]=0;
return result;
}
修改后的代码:
void USART_IRQHandler(char Channel, USART_TypeDef *USARTx)
{
volatile unsigned int IIR;
IIR = USARTx->SR;
if (IIR & USART_FLAG_RXNE)
{ // read interrupt
USARTx->SR &= ~USART_FLAG_RXNE; // clear interrupt
rbuf[Channel][rin[Channel]% UART_BUFSIZE] = USART_ReceiveData(USARTx);
rin[Channel]++;
}
if (IIR & USART_FLAG_TXE)
{
USARTx->SR &= ~USART_FLAG_TXE; // clear interrupt
}
}
/******************************************************************************/
int GetCharUART (char Channel)
{
int result;
if (rin[Channel]==rout[Channel]) {
return EMPTY;
}
result=rbuf[Channel][rout[Channel]% UART_BUFSIZE];
rout[Channel]++;
return result;
}
您的代码存在功能错误。
在 ISR 中,您不检查“缓冲区已满”情况。代码只是递增 rin[Channel]
而没有查看 rout[Channel]
。所以整个缓冲区的数据可能会丢失。
示例:如果 rout[Channel]
等于零且 rin[Channel]
等于 UART_BUFSIZE-1,则 ISR 会将 rin[Channel]
设置为零。换句话说,缓冲区现在将显示为空并且数据丢失。
所以第一步是修复代码。
与其尝试制作可以与中断同步工作的数据结构,不如使用实际上可以安全用于中断的不同数据结构。
circular/ring 缓冲区通常用于需要在常规代码和中断代码之间共享的数据结构。
https://en.wikipedia.org/wiki/Circular_buffer
一个循环缓冲区是用两个索引实现的,每一方只修改一个索引,有一种reader/writer风格。这允许它被并发执行推入和弹出。
由于双索引以及索引总是在一个方向递增的事实,可能发生的最坏情况是中断在缓冲区快满时丢弃一个字节,因为它没有在最近对另一个索引的更改,这没什么大不了的。
如果你自己实现,记得测试一下,因为它很容易搞砸。
我知道易失性键盘,它不能确保同步安全。
以下代码用于中断例程,我大量使用了该函数
主循环中的 GetCharUART。
这样写代码安全稳定吗?或者我必须像互斥锁一样进行低级同步,如果这是真的,我将如何保护那个 rbuf?
volatile char rbuf[5][UART_BUFSIZE];
vu16 rin[5] = { 0, 0, 0, 0, 0 };
vu16 rout[5] = { 0, 0, 0, 0, 0 };
void USART_IRQHandler(char Channel, USART_TypeDef *USARTx)
{
volatile unsigned int IIR;
IIR = USARTx->SR;
if (IIR & USART_FLAG_RXNE)
{ // read interrupt
USARTx->SR &= ~USART_FLAG_RXNE; // clear interrupt
rbuf[Channel][rin[Channel]] = USART_ReceiveData(USARTx);
rin[Channel]++;
if(rin[Channel]>=UART_BUFSIZE) rin[Channel]=0;
}
if (IIR & USART_FLAG_TXE)
{
USARTx->SR &= ~USART_FLAG_TXE; // clear interrupt
}
}
int GetCharUART (char Channel)
{
int result;
if (rin[Channel]==rout[Channel]) {
return EMPTY;
}
result=rbuf[Channel][rout[Channel]];
rout[Channel]++;
if(rout[Channel]>=UART_BUFSIZE)
rout[Channel]=0;
return result;
}
修改后的代码:
void USART_IRQHandler(char Channel, USART_TypeDef *USARTx)
{
volatile unsigned int IIR;
IIR = USARTx->SR;
if (IIR & USART_FLAG_RXNE)
{ // read interrupt
USARTx->SR &= ~USART_FLAG_RXNE; // clear interrupt
rbuf[Channel][rin[Channel]% UART_BUFSIZE] = USART_ReceiveData(USARTx);
rin[Channel]++;
}
if (IIR & USART_FLAG_TXE)
{
USARTx->SR &= ~USART_FLAG_TXE; // clear interrupt
}
}
/******************************************************************************/
int GetCharUART (char Channel)
{
int result;
if (rin[Channel]==rout[Channel]) {
return EMPTY;
}
result=rbuf[Channel][rout[Channel]% UART_BUFSIZE];
rout[Channel]++;
return result;
}
您的代码存在功能错误。
在 ISR 中,您不检查“缓冲区已满”情况。代码只是递增 rin[Channel]
而没有查看 rout[Channel]
。所以整个缓冲区的数据可能会丢失。
示例:如果 rout[Channel]
等于零且 rin[Channel]
等于 UART_BUFSIZE-1,则 ISR 会将 rin[Channel]
设置为零。换句话说,缓冲区现在将显示为空并且数据丢失。
所以第一步是修复代码。
与其尝试制作可以与中断同步工作的数据结构,不如使用实际上可以安全用于中断的不同数据结构。
circular/ring 缓冲区通常用于需要在常规代码和中断代码之间共享的数据结构。
https://en.wikipedia.org/wiki/Circular_buffer
一个循环缓冲区是用两个索引实现的,每一方只修改一个索引,有一种reader/writer风格。这允许它被并发执行推入和弹出。
由于双索引以及索引总是在一个方向递增的事实,可能发生的最坏情况是中断在缓冲区快满时丢弃一个字节,因为它没有在最近对另一个索引的更改,这没什么大不了的。
如果你自己实现,记得测试一下,因为它很容易搞砸。