1、中心思想
接收和发送在启动DMA之后会自动实现,如何连续接收和发送,什么时候接收和发送是关键。
CNDTR寄存器可以告诉你还需要等待接收多少数据,以及你还需要等待发送多少数据。
接收到的数据 =启动时指定接收的数据 -?接收DMA的CNDTR
发送出去的数据 = 发送DMA的CNDTR
2、缓冲接收实现:
接收DMA采用循环方式接收,缓冲区长度为 N。当接收满N后从头覆盖接收。
3、缓冲发送实现
发送DMA采用单次发送的方式,理论上CNDTR寄存器为0时代表缓冲区的数据发送完可以再起启动下一轮发送了,其实不是,CNDTR为0并不代表USART->DR数据发送的结束,有一定延迟。
4、程序实现
#define FIFO_SIZE 32
uint8_t usart1_rxfifo[FIFO_SIZE];
int com1_read_byte()
{
static uint16_t tail = 0;
int data = -1;
uint16_t head = (FIFO_SIZE)-huart1.hdmarx->Instance->CNDTR;
if (tail != head)
{
data = usart1_rxfifo[tail];
tail++;
if (tail >= FIFO_SIZE)
{
tail = 0;
}
}
return data;
}
#define TX_FIFO_SIZE 200
uint8_t usart1_txfifo[TX_FIFO_SIZE];
uint16_t tx_head = 0;
uint16_t tx_tail = 0;
void send_loop() //最好放在中断里实现
{
if (tx_tail != tx_head)
{
if (huart1.gState == HAL_UART_STATE_READY) //保证串口发送空闲
{
if ((huart1.hdmatx->Instance->CNDTR) == 0) //发送结束
{
if (tx_tail < tx_head)
{
HAL_UART_Transmit_DMA(&huart1, usart1_txfifo + tx_tail, tx_head - tx_tail);
tx_tail = tx_head;
}
else
{
HAL_UART_Transmit_DMA(&huart1, usart1_txfifo + tx_tail, TX_FIFO_SIZE - tx_tail);
tx_tail = 0;//还有数据、下次再发
}
}
}
}
}
void usart1_write(uint8_t data[], uint16_t len)
{
for (int i = 0; i < len; i++)
{
if (tx_head + 1 == tx_tail) //咬到尾巴了
{
return; //放弃剩余发送
}
usart1_txfifo[tx_head++] = data[i];
if (tx_head >= TX_FIFO_SIZE)
tx_head = 0;
}
}
#include "stdarg.h"
void print_x(void *format, ...)
{
va_list ap;
va_start(ap, format);
char buff[100];
int n = vsnprintf(buff, 100, format, ap);
va_end(ap);
if (n > 0)
{
usart1_write((uint8_t *)buff, n);
}
}
int main(void)
{
// ...
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_DMA(&huart1, usart1_rxfifo, FIFO_SIZE);
uint32_t tick = 0;
/* USER CODE END 2 */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
send_loop(); //可以放在定时中断里面 间断查询发送数据
if (HAL_GetTick() > tick)
{
print_x("send test %d\r\n", HAL_GetTick());
tick = HAL_GetTick() + 100;
}
int data = com1_read_byte();
if (data >= 0)
{
//处理接收到的数据
}
// ...
}
}
5、STM32CubeMx配置
?
?
?
?
?
?
?
?
|