关于这个串行通信代码的问题? [皮质-M4]
Questions about this serial communication code? [Cortex-M4]
我正在查看来自 STMicroelectronics 的以下代码,了解如何通过中断实现 USART 通信
#include <stm32f10x_lib.h> // STM32F10x Library Definitions
#include <stdio.h>
#include "STM32_Init.h" // STM32 Initialization
/*----------------------------------------------------------------------------
Notes:
The length of the receive and transmit buffers must be a power of 2.
Each buffer has a next_in and a next_out index.
If next_in = next_out, the buffer is empty.
(next_in - next_out) % buffer_size = the number of characters in the buffer.
*----------------------------------------------------------------------------*/
#define TBUF_SIZE 256 /*** Must be a power of 2 (2,4,8,16,32,64,128,256,512,...) ***/
#define RBUF_SIZE 256 /*** Must be a power of 2 (2,4,8,16,32,64,128,256,512,...) ***/
/*----------------------------------------------------------------------------
*----------------------------------------------------------------------------*/
#if TBUF_SIZE < 2
#error TBUF_SIZE is too small. It must be larger than 1.
#elif ((TBUF_SIZE & (TBUF_SIZE-1)) != 0)
#error TBUF_SIZE must be a power of 2.
#endif
#if RBUF_SIZE < 2
#error RBUF_SIZE is too small. It must be larger than 1.
#elif ((RBUF_SIZE & (RBUF_SIZE-1)) != 0)
#error RBUF_SIZE must be a power of 2.
#endif
/*----------------------------------------------------------------------------
*----------------------------------------------------------------------------*/
struct buf_st {
unsigned int in; // Next In Index
unsigned int out; // Next Out Index
char buf [RBUF_SIZE]; // Buffer
};
static struct buf_st rbuf = { 0, 0, };
#define SIO_RBUFLEN ((unsigned short)(rbuf.in - rbuf.out))
static struct buf_st tbuf = { 0, 0, };
#define SIO_TBUFLEN ((unsigned short)(tbuf.in - tbuf.out))
static unsigned int tx_restart = 1; // NZ if TX restart is required
/*----------------------------------------------------------------------------
USART1_IRQHandler
Handles USART1 global interrupt request.
*----------------------------------------------------------------------------*/
void USART1_IRQHandler (void) {
volatile unsigned int IIR;
struct buf_st *p;
IIR = USART1->SR;
if (IIR & USART_FLAG_RXNE) { // read interrupt
USART1->SR &= ~USART_FLAG_RXNE; // clear interrupt
p = &rbuf;
if (((p->in - p->out) & ~(RBUF_SIZE-1)) == 0) {
p->buf [p->in & (RBUF_SIZE-1)] = (USART1->DR & 0x1FF);
p->in++;
}
}
if (IIR & USART_FLAG_TXE) {
USART1->SR &= ~USART_FLAG_TXE; // clear interrupt
p = &tbuf;
if (p->in != p->out) {
USART1->DR = (p->buf [p->out & (TBUF_SIZE-1)] & 0x1FF);
p->out++;
tx_restart = 0;
}
else {
tx_restart = 1;
USART1->CR1 &= ~USART_FLAG_TXE; // disable TX interrupt if nothing to send
}
}
}
/*------------------------------------------------------------------------------
buffer_Init
initialize the buffers
*------------------------------------------------------------------------------*/
void buffer_Init (void) {
tbuf.in = 0; // Clear com buffer indexes
tbuf.out = 0;
tx_restart = 1;
rbuf.in = 0;
rbuf.out = 0;
}
/*------------------------------------------------------------------------------
SenChar
transmit a character
*------------------------------------------------------------------------------*/
int SendChar (int c) {
struct buf_st *p = &tbuf;
// If the buffer is full, return an error value
if (SIO_TBUFLEN >= TBUF_SIZE)
return (-1);
p->buf [p->in & (TBUF_SIZE - 1)] = c; // Add data to the transmit buffer.
p->in++;
if (tx_restart) { // If transmit interrupt is disabled, enable it
tx_restart = 0;
USART1->CR1 |= USART_FLAG_TXE; // enable TX interrupt
}
return (0);
}
/*------------------------------------------------------------------------------
GetKey
receive a character
*------------------------------------------------------------------------------*/
int GetKey (void) {
struct buf_st *p = &rbuf;
if (SIO_RBUFLEN == 0)
return (-1);
return (p->buf [(p->out++) & (RBUF_SIZE - 1)]);
}
/*----------------------------------------------------------------------------
MAIN function
*----------------------------------------------------------------------------*/
int main (void) {
buffer_Init(); // init RX / TX buffers
stm32_Init (); // STM32 setup
printf ("Interrupt driven Serial I/O Example\r\n\r\n");
while (1) { // Loop forever
unsigned char c;
printf ("Press a key. ");
c = getchar ();
printf ("\r\n");
printf ("You pressed '%c'.\r\n\r\n", c);
} // end while
} // end main
我的问题如下:
- 在处理程序函数中,语句
((p->in - p->out) & ~(RBUF_SIZE-1))
什么时候求值为零以外的值?如果 RBUF_SIZE
如所示为 2 的幂,则 ~(RBUF_SIZE-1)
应始终为零。是否检查 p->in
> p->out
?即使这不是真的,条件无论如何都应该计算为零,对吧?
- 在接下来的行中,声明了
p->buf [p->in & (RBUF_SIZE-1)] = (USART1->DR & 0x1FF);
。为什么代码 AND p->in
与 RBUF_SIZE-1
?
- 我们在这段代码中使用了什么样的缓冲区?先进先出?
不是这样。例如,假设 32 位算术,if RBUF_SIZE == 0x00000100
then RBUF_SIZE-1 == 0x000000FF
and ~(RBUF_SIZE-1) == 0xFFFFFF00
(it's a bitwise NOT, not a logical NOT)。因此,您所指的检查实际上与 (p->in - p->out) < RBUF_SIZE
相同,并且不清楚为什么它更优越。 ARM GCC 7.2.1 为两者生成相同长度的代码 (-O1
).
p->in & (RBUF_SIZE-1)
当 p->in
无符号时,与 p->in % RBUF_SIZE
相同。同样,不确定为什么在后者更清楚时会使用前者;当然,它有效地强制编译器使用 AND
操作计算模数,但鉴于 RBUF_SIZE
在编译时已知是 2 的幂,我的猜测是大多数编译器可以解决这个问题(再次强调,ARM GCC 7.2.1 当然可以,我刚刚试过了 - 它以两种方式产生相同的指令)。
看起来像。 FIFO 实现为循环缓冲区。
我正在查看来自 STMicroelectronics 的以下代码,了解如何通过中断实现 USART 通信
#include <stm32f10x_lib.h> // STM32F10x Library Definitions
#include <stdio.h>
#include "STM32_Init.h" // STM32 Initialization
/*----------------------------------------------------------------------------
Notes:
The length of the receive and transmit buffers must be a power of 2.
Each buffer has a next_in and a next_out index.
If next_in = next_out, the buffer is empty.
(next_in - next_out) % buffer_size = the number of characters in the buffer.
*----------------------------------------------------------------------------*/
#define TBUF_SIZE 256 /*** Must be a power of 2 (2,4,8,16,32,64,128,256,512,...) ***/
#define RBUF_SIZE 256 /*** Must be a power of 2 (2,4,8,16,32,64,128,256,512,...) ***/
/*----------------------------------------------------------------------------
*----------------------------------------------------------------------------*/
#if TBUF_SIZE < 2
#error TBUF_SIZE is too small. It must be larger than 1.
#elif ((TBUF_SIZE & (TBUF_SIZE-1)) != 0)
#error TBUF_SIZE must be a power of 2.
#endif
#if RBUF_SIZE < 2
#error RBUF_SIZE is too small. It must be larger than 1.
#elif ((RBUF_SIZE & (RBUF_SIZE-1)) != 0)
#error RBUF_SIZE must be a power of 2.
#endif
/*----------------------------------------------------------------------------
*----------------------------------------------------------------------------*/
struct buf_st {
unsigned int in; // Next In Index
unsigned int out; // Next Out Index
char buf [RBUF_SIZE]; // Buffer
};
static struct buf_st rbuf = { 0, 0, };
#define SIO_RBUFLEN ((unsigned short)(rbuf.in - rbuf.out))
static struct buf_st tbuf = { 0, 0, };
#define SIO_TBUFLEN ((unsigned short)(tbuf.in - tbuf.out))
static unsigned int tx_restart = 1; // NZ if TX restart is required
/*----------------------------------------------------------------------------
USART1_IRQHandler
Handles USART1 global interrupt request.
*----------------------------------------------------------------------------*/
void USART1_IRQHandler (void) {
volatile unsigned int IIR;
struct buf_st *p;
IIR = USART1->SR;
if (IIR & USART_FLAG_RXNE) { // read interrupt
USART1->SR &= ~USART_FLAG_RXNE; // clear interrupt
p = &rbuf;
if (((p->in - p->out) & ~(RBUF_SIZE-1)) == 0) {
p->buf [p->in & (RBUF_SIZE-1)] = (USART1->DR & 0x1FF);
p->in++;
}
}
if (IIR & USART_FLAG_TXE) {
USART1->SR &= ~USART_FLAG_TXE; // clear interrupt
p = &tbuf;
if (p->in != p->out) {
USART1->DR = (p->buf [p->out & (TBUF_SIZE-1)] & 0x1FF);
p->out++;
tx_restart = 0;
}
else {
tx_restart = 1;
USART1->CR1 &= ~USART_FLAG_TXE; // disable TX interrupt if nothing to send
}
}
}
/*------------------------------------------------------------------------------
buffer_Init
initialize the buffers
*------------------------------------------------------------------------------*/
void buffer_Init (void) {
tbuf.in = 0; // Clear com buffer indexes
tbuf.out = 0;
tx_restart = 1;
rbuf.in = 0;
rbuf.out = 0;
}
/*------------------------------------------------------------------------------
SenChar
transmit a character
*------------------------------------------------------------------------------*/
int SendChar (int c) {
struct buf_st *p = &tbuf;
// If the buffer is full, return an error value
if (SIO_TBUFLEN >= TBUF_SIZE)
return (-1);
p->buf [p->in & (TBUF_SIZE - 1)] = c; // Add data to the transmit buffer.
p->in++;
if (tx_restart) { // If transmit interrupt is disabled, enable it
tx_restart = 0;
USART1->CR1 |= USART_FLAG_TXE; // enable TX interrupt
}
return (0);
}
/*------------------------------------------------------------------------------
GetKey
receive a character
*------------------------------------------------------------------------------*/
int GetKey (void) {
struct buf_st *p = &rbuf;
if (SIO_RBUFLEN == 0)
return (-1);
return (p->buf [(p->out++) & (RBUF_SIZE - 1)]);
}
/*----------------------------------------------------------------------------
MAIN function
*----------------------------------------------------------------------------*/
int main (void) {
buffer_Init(); // init RX / TX buffers
stm32_Init (); // STM32 setup
printf ("Interrupt driven Serial I/O Example\r\n\r\n");
while (1) { // Loop forever
unsigned char c;
printf ("Press a key. ");
c = getchar ();
printf ("\r\n");
printf ("You pressed '%c'.\r\n\r\n", c);
} // end while
} // end main
我的问题如下:
- 在处理程序函数中,语句
((p->in - p->out) & ~(RBUF_SIZE-1))
什么时候求值为零以外的值?如果RBUF_SIZE
如所示为 2 的幂,则~(RBUF_SIZE-1)
应始终为零。是否检查p->in
>p->out
?即使这不是真的,条件无论如何都应该计算为零,对吧? - 在接下来的行中,声明了
p->buf [p->in & (RBUF_SIZE-1)] = (USART1->DR & 0x1FF);
。为什么代码 ANDp->in
与RBUF_SIZE-1
? - 我们在这段代码中使用了什么样的缓冲区?先进先出?
不是这样。例如,假设 32 位算术,if
RBUF_SIZE == 0x00000100
thenRBUF_SIZE-1 == 0x000000FF
and~(RBUF_SIZE-1) == 0xFFFFFF00
(it's a bitwise NOT, not a logical NOT)。因此,您所指的检查实际上与(p->in - p->out) < RBUF_SIZE
相同,并且不清楚为什么它更优越。 ARM GCC 7.2.1 为两者生成相同长度的代码 (-O1
).p->in & (RBUF_SIZE-1)
当p->in
无符号时,与p->in % RBUF_SIZE
相同。同样,不确定为什么在后者更清楚时会使用前者;当然,它有效地强制编译器使用AND
操作计算模数,但鉴于RBUF_SIZE
在编译时已知是 2 的幂,我的猜测是大多数编译器可以解决这个问题(再次强调,ARM GCC 7.2.1 当然可以,我刚刚试过了 - 它以两种方式产生相同的指令)。看起来像。 FIFO 实现为循环缓冲区。