文章目录
串口通讯
串口
串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方式的扩展接口。串行接口(SerialInterface)是指数据一位一位地顺序传送,其特点是通信线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传送速度较慢。
-
USART(universal synchronous asynchronous receiver and transmitte): 通用同步异步收发器
- USART是一个串行通信设备,可以灵活地与外部设备进行全双工数据交换。
-
UART(universal asynchronous receiver and transmitter): 通用异步收发器
- 异步串行通信口(UART)就是我们在嵌入式中常说的串口,它还是一种通用的数据通信议。

区别:
USART是指单片机的一个端口模块,可以根据需要配置成同步模式(SPI,I2C),也可以将其配置为异步模式,后者就是UART。所以说UART姑且可以称之为一个与SPI,I2C对等的“协议”,而USART则不是一个协议,而是更应该理解为一个实体。
相比于同步通讯,UART不需要统一的时钟线,接线更加方便。但是,为了正常的对信号进行解码,使用UART通讯的双方必须事先约定好波特率,即单位事件内传输码元的个数。
补充:
在电子通信领域,波特(Baud)即调制速率,指的是有效数据讯号调制载波的速率,即单位时间内载波调制状态变化的次数。它是对符号传输速率的一种度量,1波特即指每秒传输1个符号,而透过不同的调制方式,可以在一个码元符号上负载多个bit位信号。[1]“波特”(Baud)本身已是速率,所以不需要写成 Baud Rate(Rate 是赘字)。单位“波特”本身就已经是代表每秒的调制数,以“波特每秒”(Baud per second)为单位是一种常见的错误,但是在一般中文口语化的沟通上还是常以“波特率”来描述“波特”(Baud)。
USART 中断
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U3IHHDBa-1633055683417)(https://raw.githubusercontent.com/Nepqiu/gallery/master/img/image-20210815155528305.png)]](https://i-blog.csdnimg.cn/blog_migrate/79b8be2bd156fce430ab122966a9f366.png#pic_center)
USART 中断事件被连接到相同的中断向量:
- 发送期间:发送完成、清除以发送或发送数据寄存器为空中断。
- 接收期间:空闲线路检测、上溢错误、接收数据寄存器不为空、奇偶校验错误、LIN 断路 检测、噪声标志(仅限多缓冲区通信)和帧错误(仅限多缓冲区通信)

串口模式配置

使用 DMA 进行连续通信
USART 能够使用 DMA 进行连续通信。接收缓冲区和发送缓冲区的 DMA 请求是独立的。
使用 DMA 进行发送
使用 DMA 进行发送 将 USART_CR3 寄存器中的 DMAT 位置 1 可以使能 DMA 模式进行发送。当 TXE 位置 1 时,可将数据从 SRAM 区(通过 DMA 配置,参见 DMA 部分)加载到 USART_DR 寄存器。要映射一个 DMA 通道以进行 USART 发送,请按以下步骤操作(x 表示通道编号):
-
在 DMA 控制寄存器中写入 USART_DR 寄存器地址,将其配置为传输的目标地址。每次发生 TXE 事件后,数据都会从存储器移动到此地址。
-
在 DMA 控制寄存器中写入存储器地址,将其配置为传输的源地址。每次发生 TXE 事件后,数据都会从这个存储区域加载到 USART_DR 寄存器中。
-
在 DMA 控制寄存器中配置要传输的总字节数。
-
在 DMA 寄存器中配置通道优先级。
-
根据应用的需求,在完成一半或全部传输后产生 DMA 中断。
-
向 SR 寄存器中的 TC 位写入 0,将其清零。
-
在 DMA 寄存器中激活该通道。
当达到在 DMA 控制器中设置的数据传输量时,DMA 控制器会在 DMA 通道的中断向量上产生一个中断。
在发送模式下,DMA 对所有要发送的数据执行了写操作(DMA_ISR 寄存器中的 TCIF 标志置 1)后,可以对 TC 标志进行监视,以确保 USART 通信已完成。在禁止 USART 或进入停止模式前必须执行此步骤,以避免损坏最后一次发送。软件必须等待直到 TC=1。TC 标志在所有数据发送期间都必须保持清零状态,然后在最后一帧发送结束后由硬件置 1。

使用 DMA 进行接收
将 USART_CR3 寄存器中的 DMAR 位置 1 可以使能 DMA 模式进行接收。接收数据字节时,数据会从 USART_DR 寄存器加载到 SRAM 区域中(通过 DMA 配置,参见 DMA 规范)。要映射一个 DMA 通道以进行 USART 接收,请按以下步骤操作:
-
在 DMA 控制寄存器中写入 USART_DR 寄存器地址,将其配置为传输的源地址。每次发生 RXNE 事件后,数据都会从此地址移动到存储器。
-
在 DMA 控制寄存器中写入存储器地址,将其配置为传输的目标地址。每次发生 RXNE 事件后,数据都会从 USART_DR 寄存器加载到此存储区。
-
在 DMA 控制寄存器中配置要传输的总字节数。
-
在 DMA 控制寄存器中配置通道优先级。
-
根据应用的需求,在完成一半或全部传输后产生中断。
-
在 DMA 控制寄存器中激活该通道。
当达到在 DMA 控制器中设置的数据传输量时,DMA 控制器会在 DMA 通道的中断向量上产生一个中断。在中断子程序中,USART_CR3 寄存器中的 DMAR 位应由软件清零。注意: 如果 DMA 用于接收,则不要使能 RXNEIE 位

多缓冲区通信中的错误标志和中断生成
在多缓冲区通信中,如果事务中发生任何错误,都会在当前字节后放置错误标志。如果中断使 能置 1,则会产生中断。在单字节接收过程中,与 RXNE 一同置位的帧错误、上溢错误和噪声 标志具有单独的错误标志中断使能位(USART_CR3 寄存器中的 EIE 位);如果该位置 1, 则会因其中任何一个错误而在当前字节后产生中断。
编程
接收的方式:
- DMA接收中断接收
- DMA+串口+DMA空闲中断接收
- DMA双缓冲区+串口+DMA空闲中断接收
- DMA+串口+DMA空闲中断+环形队列接收
发送的方式:
- DMA+串口发送
- 单串口发送
- DMA+串口发送+环形队列(双缓冲)
- 动态内存分配的FIFIO
下面主要用 环形队列+DMA+非动态内存分配+IDLE中断
建议先看最下面的参考文章
接收流程
USART1 + DMA + IDLE中断 +无锁队列
主函数:
int main(void)
{
uint8_t buff_read[32];
uint32_t length;
usart1_init();
while (1)
{
length = fifo_read_buff(pfifo_x, buff_read, 32);
if (length)
{
printf("lengtt = %d", length); // 实际接收的数据长度
//对接收的数据进行处理
}
else
{
printf("no data rx");// 没有数据
}
if (pfifo_x->error)
{
printf("fifo error %d", pfifo_x->error);// 接收错误
pfifo_x->error = 0;
}
}
}
中断处理函数:
void USART1_IRQHandler(void) // 接收数据中断
{
__IO uint8_t Len = 0;
//发送完成中断
/*
* DMA中断时,只表示需要传送的所有数据字节全部传送到串口的发送数据寄存器中了。
* 此时串口实际上还有2个字节并未发送完成,数据寄存器和移位寄存器中的2个字节还需要发送,并不能关闭串口发送。
* 同理,如果是485切换方向,必须要等到发送完成,也就是移位寄存器发送完成-TC标志置位。
*
* TXE指的是发送缓冲器DR空,TC指的是SHIFT移位寄存器空。
* DMA完成只是代表把最后一个字节送到DR寄存器里面了,此时SHIFT移位寄存器有1个字节正在开始发送,
* DR寄存器里面有一个字节等待发送,所以就是2个字节未发送完成。
*/
if (USART_GetITStatus(USART1, USART_IT_TC) == SET)
{
USART_ClearITPendingBit(USART1, USART_IT_TC);
USART_ITConfig(USART1, USART_IT_TC, DISABLE);
DMA2_Stream7_working = 0;
}
//总线空闲中断
if (USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) //触发中断标志位
{
Len = USART1->SR; //清除RXNE标志
Len = USART1->DR; //清USART_IT_IDLE标志
Len = DMA_GetCurrDataCounter(DMA2_Stream5); //获取当前剩余数据量大小的函数
if (pfifo_1 != 0)
{
// Len为当前接收索引
pfifo_1->in += ((pfifo_1->last_cnt - Len) & (pfifo_1->size - 1)); //更新in
pfifo_1->last_cnt = Len;
if ((pfifo_1->in - pfifo_1->out) > pfifo_1->size)
{
pfifo_1->out = pfifo_1->in; // 清空缓存,注意赋值顺序,pfifo->in = pfifo->out 是错误的
pfifo_1->error |= FIFO_DMA_ERROR_RX_FULL;
}
}
else
{
pfifo_1->error |= FIFO_DMA_ERROR_RX_POINT_NULL;
}
}
}
初始化(标准库)
#define USART1_RX_LEN 32
#define USART1_TX_LEN 32
uint8_t Usart1_Rx[USART1_RX_LEN] = {
0};
uint8_t Usart1_Tx[USART1_TX_LEN] = {
0};
uint8_t Usart1_Tx_Buffer[USART1_TX_LEN] = {
0};
fifo_rx_def fifo_usart_rx_1;
fifo_rx_def *pfifo_1 = &fifo_usart_rx_1;
fifo_rx_def fifo_usart_tx_1;
uint8_t DMA2_Stream7_working = 0;
void usart1_init(void)
{
/* -------------- Enable Module Clock Source ----------------------------*/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //使能USART1时钟
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1); //GPIOA9复用为USART1
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1); //GPIOA10复用为USART1
/* -------------- Configure GPIO ---------------------------------------*/
{
GPIO_InitTypeDef GPIO_InitStruct;
NVIC_InitTypeDef NVIC_InitStructure;
USART_InitTypeDef USART1_InitStruct;
//USART1端口配置
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA, &GPIO_InitStruct); //初始化PA9,PA10
//USART1 初始化设置
USART_DeInit(USART1);
USART1_InitStruct.USART_BaudRate = 115200; //波特率设置
USART1_InitStruct.USART_WordLength = USART_WordLength_8b; //字长为8位数据格式
USART1_InitStruct.USART_StopBits = USART_StopBits_1; //一个停止位
USART1_InitStruct.USART_Parity = USART_Parity_No; //无奇偶校验位
USART1_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件数据流控制
USART1_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART1, &USART1_InitStruct); //初始化串口1
USART_Cmd(USART1, ENABLE); //使能串口1
USART_ClearFlag(USART1, USART_FLAG_TC);
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //串口1中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //开启相关中断
USART_ITConfig(USART1, USART_IT_TC

最低0.47元/天 解锁文章
1万+





