串口通讯
串口
串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方式的扩展接口。串行接口(SerialInterface)是指数据一位一位地顺序传送,其特点是通信线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传送速度较慢。
区别:
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-VkffEvL6-1632828582303)(https://raw.githubusercontent.com/Nepqiu/gallery/master/img/image-20210815155528305.png)]
USART 中断事件被连接到相同的中断向量:
- 发送期间:发送完成、清除以发送或发送数据寄存器为空中断。
- 接收期间:空闲线路检测、上溢错误、接收数据寄存器不为空、奇偶校验错误、LIN 断路 检测、噪声标志(仅限多缓冲区通信)和帧错误(仅限多缓冲区通信)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IbZhqtpL-1632828582305)(https://raw.githubusercontent.com/Nepqiu/gallery/master/img/image-20210811170712630.png)]
串口模式配置
使用 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 位
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NpkQgBln-1632828582308)(https://raw.githubusercontent.com/Nepqiu/gallery/master/img/image-20210811171131932.png)]
多缓冲区通信中的错误标志和中断生成
在多缓冲区通信中,如果事务中发生任何错误,都会在当前字节后放置错误标志。如果中断使 能置 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;
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;
Len = USART1->DR;
Len = DMA_GetCurrDataCounter(DMA2_Stream5);
if (pfifo_1 != 0)
{
pfifo_1->in += ((pfifo_1->last_cnt - Len) & (pfifo_1->size - 1));
pfifo_1->last_cnt = Len;
if ((pfifo_1->in - pfifo_1->out) > pfifo_1->size)
{
pfifo_1->out = pfifo_1->in;
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)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
{
GPIO_InitTypeDef GPIO_InitStruct;
NVIC_InitTypeDef NVIC_InitStructure;
USART_InitTypeDef USART1_InitStruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStruct);
USART_DeInit(USART1);
USART1_InitStruct.USART_BaudRate = 115200;
USART1_InitStruct.USART_WordLength = USART_WordLength_8b;
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);
USART_Cmd(USART1, ENABLE);
USART_ClearFlag(USART1, USART_FLAG_TC);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_ITConfig(USART1, USART_IT_TC, DISABLE);
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
}
{
DMA_InitTypeDef DMA_InitStruct;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
DMA_Cmd(DMA2_Stream7, DISABLE);
DMA_DeInit(DMA2_Stream7);
DMA_InitStruct.DMA_Channel = DMA_Channel_4;
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t) & (USART1->DR);
DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)(Usart1_Tx);
DMA_InitStruct.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_InitStruct.DMA_BufferSize = USART1_TX_LEN;
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
DMA_InitStruct.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream7, &DMA_InitStruct);
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream7_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
DMA_ITConfig(DMA2_Stream7, DMA_IT_TC, ENABLE);
DMA_Cmd(DMA2_Stream7, DISABLE);
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
if (fifo_init(&fifo_usart_tx_1, Usart1_Tx_Buffer, USART1_TX_LEN) == -1)
{
}
}
{
DMA_InitTypeDef DMA_InitStruct;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
DMA_Cmd(DMA2_Stream5, DISABLE);
while (DMA_GetCmdStatus(DMA2_Stream5) != DISABLE)
;
DMA_DeInit(DMA2_Stream5);
DMA_InitStruct.DMA_Channel = DMA_Channel_4;
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t) & (USART1->DR);
DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)(Usart1_Rx);
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStruct.DMA_BufferSize = USART1_RX_LEN;
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;
DMA_InitStruct.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
DMA_InitStruct.DMA_MemoryBurst = DMA_Mode_Normal;
DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream5, &DMA_InitStruct);
DMA_Cmd(DMA2_Stream5, ENABLE);
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
if (fifo_init(pfifo_1, Usart1_Rx, USART1_RX_LEN) == -1)
{
}
}
}
发送流程
在设计串口驱动过程中,要遵循的准则是:
- 尽量的减少程序运行时间
- 尽量的减少程序所占的内存
开启串口发送完成中断
void USART1_IRQHandler(void)
{
__IO uint8_t Len = 0;
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;
}
}
开启DMA发送完成中断
void DMA2_Stream7_IRQHandler(void)
{
if (DMA_GetITStatus(DMA2_Stream7, DMA_IT_TCIF7) != RESET)
{
DMA_ClearFlag(DMA2_Stream7, DMA_IT_TCIF7);
DMA_Cmd(DMA2_Stream7, DISABLE);
USART_ITConfig(USART1, USART_IT_TC, ENABLE);
}
}
DMA发送函数
uint32_t usart1_dma_send(uint8_t *data, uint16_t len)
{
uint32_t result = fifo_write_buff(&fifo_usart_tx_1, data, len);
if (DMA2_Stream7_working == 0 && result != 0)
{
len = fifo_read_buff(&fifo_usart_tx_1, Usart1_Tx, USART1_TX_LEN);
DMA_SetCurrDataCounter(DMA2_Stream7, len);
DMA_Cmd(DMA2_Stream7, ENABLE);
DMA2_Stream7_working = 1;
}
if (result == len)
{
return len;
}
else
{
return result;
}
}
解析
https://www.amobbs.com/thread-4516795-1-1.html
stm32使用dma传输串口数据时,当dma中断发送完成
程序1
void USART1WriteDataToBuffer(*ptr,u8 count)
{
while(count--)
{
USART1SendByte(*ptr++);
while(USART_GetFlagStatus(USART1, USART_FLAG_TC);
}
}
这段程序会在实际应用中产生灾难性的后果。首先,但发送数据送到寄存器启动后,CPU就一直在等待这个数据发送完成,这样,直到所有要发送的数据完成,CPU才能做其他事情。相对于CPU内核运行的速度而言,串口外设的运行速度是非常快的,让一个非常快的设备去等待相对很慢的设备,程序的效率是非常底下的。
所以尽量采取中断的方式去发送数据
程序2
void USART1WriteDataToBuffer(*ptr,u8 count)
{
while (count != '\0')
{
USART1SendTCB[Index++] = *ptr++;
Count = count;
}
}
void USART1SendUpdate(void)
{
USART1SendByte( *ptr++ );
}
这样,当调用USART1WriteDataToBuffer 这个函数的时候,我们将要发送的数据写入发送缓冲区,CPU就可以执行其他任务了,待一个数据发送完成以后,中断ISR就会触发,在中断服务程序中将下一个数据写入发送寄存器,启动下一次发送,直到完全发送完毕。
但是在实际工程应用中,经常会遇到这种类似的情况,串口显示屏需要显示1000个点,通过串口发送这1000个点的颜色的RGB年度数值。将这1000个数据写入发送寄存器以后,启动发送。在115200的波特率,一位起始位,一位停止位,在无校验位的情况下,至少需要(10*1000*2)/115200=0.1736 秒,在这段期以内,时钟更新了,需要再发送一串时间更新数据,这个数据大约有100个,这样这串数据需要写入到发送缓冲区的发送字节后面。
同样的道理,在这个时候如果有显示任务更新的话,将会有其他的数据写入到缓冲区。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JxeVlfbL-1632828582309)(https://raw.githubusercontent.com/Nepqiu/gallery/master/img/ourdev_611415V21OP9.jpg)]
从图上可以看出,程序2虽然满足了时间上的要求,却没有满空间上的要求,它的数据缓冲区的单向的,这样,当发送缓冲区撑满了以后才能将发送发送缓冲区的数据清空,以便下次的缓冲数据。这样内存较小的嵌入式系统来说是不能容忍的。
因此,也可以将发送缓冲区建立一个环形缓冲区,在这个缓冲区内,通过头指针(HostIndex)和尾指针(HostIndex)来定位空白区和数据区。
- 头指针:指向有数据区的顶部,每次写入数据,都更新头指针,如果到了缓冲区的末端,就自动返回到缓冲区的起始处(StartIndex),直到写入到尾指针为止,这时缓冲区已经被装满,不能再装入数据。
- 尾指针:指向有数据区的尾部,当数据发送完毕后,更新尾指针的位置,如果到了缓冲区的末端(EndIndex),就自动返回到缓冲区的起始处(StartIndex),直到遇到头指针为止,这是证明所有的数据已经发送完毕。
这样就实现了发送缓冲区的动态调整空白区和数据区,刚刚发送完毕的数据,马上就被开辟出来用于存放下一个数据,最大可能的节省了宝贵的发送缓冲区的空间,提高了效率。
fifo_buff 代码
fifo_buff.c
#include "fifo_buff.h"
#include <limits.h>
#define IS_POWER_OF_2(x) ((x) != 0 && (((x) & ((x)-1)) == 0))
static inline uint32_t min_fifo(uint32_t X, uint32_t Y)
{
return ((X) > (Y) ? (Y) : (X));
}
uint32_t roundup_pow_of_two(uint32_t Num)
{
uint32_t result = 1;
if (IS_POWER_OF_2(Num) || Num == 0)
return Num;
else if (Num > LONG_MAX)
return (LONG_MAX ^ ULONG_MAX);
while (Num)
{
Num >>= 1;
result <<= 1;
}
return result;
}
uint32_t rounddown_pow_of_two(uint32_t Num)
{
uint32_t result = 1;
if (IS_POWER_OF_2(Num) || Num == 0)
return Num;
else if (Num > LONG_MAX)
return (LONG_MAX ^ ULONG_MAX);
while (Num)
{
Num >>= 1;
result <<= 1;
}
return result >> 1;
}
int32_t fifo_init(fifo_rx_def *pfifo, uint8_t *buff, uint32_t size)
{
assert_param(pfifo != NULL || buff != NULL);
if (!IS_POWER_OF_2(size))
{
return -1;
}
pfifo->in = 0;
pfifo->out = 0;
pfifo->buffer = buff;
pfifo->size = size;
pfifo->last_cnt = size;
return 0;
}
uint32_t fifo_read_buff(fifo_rx_def *pfifo, uint8_t *buffer, uint32_t len)
{
uint32_t length;
assert_param(pfifo != NULL || pfifo->buffer != NULL || buffer != NULL);
len = min_fifo(len, pfifo->in - pfifo->out);
length = min_fifo(len, pfifo->size - (pfifo->out & (pfifo->size - 1)));
memcpy(buffer, pfifo->buffer + (pfifo->out & (pfifo->size - 1)), length);
memcpy(buffer + length, pfifo->buffer, len - length);
pfifo->out += len;
return len;
}
uint32_t fifo_write_buff(fifo_rx_def *pfifo, uint8_t *buffer, uint32_t len)
{
uint32_t length;
len = min_fifo(len, (pfifo->size - (pfifo->in - pfifo->out)));
length = min_fifo(len, pfifo->size - (pfifo->in & (pfifo->size - 1)));
memcpy(pfifo->buffer + (pfifo->in & pfifo->size - 1), buffer, length);
memcpy(pfifo->buffer, buffer + length, len - length);
pfifo->in += len;
return len;
}
unsigned int fifo_get_free(fifo_rx_def *pfifo)
{
return ((pfifo->size > 0) ? (pfifo->size - (pfifo->in - pfifo->out)) : 0);
}
unsigned int fifo_get_full(fifo_rx_def *pfifo)
{
return (pfifo->in - pfifo->out);
}
unsigned int fifo_is_empty(fifo_rx_def *pfifo)
{
return ((pfifo->size > 0) && (pfifo->in == pfifo->out));
}
unsigned int fifo_is_full(fifo_rx_def *pfifo)
{
return ((pfifo->size == 0) || (pfifo->size == (pfifo->in - pfifo->out)));
}
fifo_buff.h
#ifndef __NEPQIU_FIFO_BUFF_H__
#define __NEPQIU_FIFO_BUFF_H__
#include <string.h>
#include <stdint.h>
#include "struct_typedef.h"
#define FIFO_DMA_ERROR_RX_NOT_IDLE (0x1 << 0)
#define FIFO_DMA_ERROR_RX_POINT_NULL (0x1 << 1)
#define FIFO_DMA_ERROR_RX_FULL (0x1 << 2)
typedef struct
{
uint8_t *buffer;
uint32_t in;
uint32_t out;
uint16_t size;
uint16_t error;
uint16_t last_cnt;
} fifo_rx_def;
int32_t fifo_init(fifo_rx_def *pfifo, uint8_t *buff, uint32_t size);
uint32_t fifo_read_buff(fifo_rx_def *pfifo, uint8_t *buffer, uint32_t len);
uint32_t fifo_write_buff(fifo_rx_def *pfifo, uint8_t *buffer, uint32_t len);
unsigned int fifo_get_free(fifo_rx_def *pfifo);
unsigned int fifo_get_full(fifo_rx_def *pfifo);
unsigned int fifo_is_empty(fifo_rx_def *pfifo);
unsigned int fifo_is_full(fifo_rx_def *pfifo);
#endif
完整代码和使用示例
#include "usart1.h"
#include "fifo_buff.h"
#define USART1_IMU_RX_LEN 32
#define USART1_IMU_TX_LEN 32
uint8_t Usart1_IMU_Rx[USART1_IMU_RX_LEN] = {0};
uint8_t Usart1_IMU_Tx[USART1_IMU_TX_LEN] = {0};
uint8_t Usart1_IMU_Tx_Buffer[USART1_IMU_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)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
{
GPIO_InitTypeDef GPIO_InitStruct;
NVIC_InitTypeDef NVIC_InitStructure;
USART_InitTypeDef USART1_InitStruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStruct);
USART_DeInit(USART1);
USART1_InitStruct.USART_BaudRate = 115200;
USART1_InitStruct.USART_WordLength = USART_WordLength_8b;
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);
USART_Cmd(USART1, ENABLE);
USART_ClearFlag(USART1, USART_FLAG_TC);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_ITConfig(USART1, USART_IT_TC, DISABLE);
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
}
{
DMA_InitTypeDef DMA_InitStruct;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
DMA_Cmd(DMA2_Stream7, DISABLE);
DMA_DeInit(DMA2_Stream7);
DMA_InitStruct.DMA_Channel = DMA_Channel_4;
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t) & (USART1->DR);
DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)(Usart1_IMU_Tx);
DMA_InitStruct.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_InitStruct.DMA_BufferSize = USART1_IMU_TX_LEN;
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
DMA_InitStruct.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream7, &DMA_InitStruct);
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream7_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
DMA_ITConfig(DMA2_Stream7, DMA_IT_TC, ENABLE);
DMA_Cmd(DMA2_Stream7, DISABLE);
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
if (fifo_init(&fifo_usart_tx_1, Usart1_IMU_Tx_Buffer, USART1_IMU_TX_LEN) == -1)
{
}
}
{
DMA_InitTypeDef DMA_InitStruct;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
DMA_Cmd(DMA2_Stream5, DISABLE);
while (DMA_GetCmdStatus(DMA2_Stream5) != DISABLE)
;
DMA_DeInit(DMA2_Stream5);
DMA_InitStruct.DMA_Channel = DMA_Channel_4;
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t) & (USART1->DR);
DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)(Usart1_IMU_Rx);
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStruct.DMA_BufferSize = USART1_IMU_RX_LEN;
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;
DMA_InitStruct.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
DMA_InitStruct.DMA_MemoryBurst = DMA_Mode_Normal;
DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream5, &DMA_InitStruct);
DMA_Cmd(DMA2_Stream5, ENABLE);
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
if (fifo_init(pfifo_1, Usart1_IMU_Rx, USART1_IMU_RX_LEN) == -1)
{
}
}
}
void USART1_IRQHandler(void)
{
__IO uint8_t Len = 0;
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;
Len = USART1->DR;
Len = DMA_GetCurrDataCounter(DMA2_Stream5);
if (pfifo_1 != 0)
{
pfifo_1->in += ((pfifo_1->last_cnt - Len) & (pfifo_1->size - 1));
pfifo_1->last_cnt = Len;
if ((pfifo_1->in - pfifo_1->out) > pfifo_1->size)
{
pfifo_1->out = pfifo_1->in;
pfifo_1->error |= FIFO_DMA_ERROR_RX_FULL;
}
}
else
{
pfifo_1->error |= FIFO_DMA_ERROR_RX_POINT_NULL;
}
}
}
void DMA2_Stream7_IRQHandler(void)
{
if (DMA_GetITStatus(DMA2_Stream7, DMA_IT_TCIF7) != RESET)
{
DMA_ClearFlag(DMA2_Stream7, DMA_IT_TCIF7);
DMA_Cmd(DMA2_Stream7, DISABLE);
USART_ITConfig(USART1, USART_IT_TC, ENABLE);
}
}
uint32_t usart1_dma_send(uint8_t *data, uint16_t len)
{
uint32_t result = fifo_write_buff(&fifo_usart_tx_1, data, len);
if (DMA2_Stream7_working == 0 && result != 0)
{
len = fifo_read_buff(&fifo_usart_tx_1, Usart1_IMU_Tx, USART1_IMU_TX_LEN);
DMA_SetCurrDataCounter(DMA2_Stream7, len);
DMA_Cmd(DMA2_Stream7, ENABLE);
DMA2_Stream7_working = 1;
}
if (result == len)
{
return len;
}
else
{
return result;
}
}
无论在用于接收什么数据都可以用一样的配置,需要更改的只有 USART1_IMU_RX_LEN 和 USART1_IMU_TX_LEN 这两个宏
只用在需要的地方循环调用
extern fifo_rx_def fifo_usart_rx_1;
fifo_rx_def *pfifo_x = &fifo_usart_rx_1;
void usart_data_receive(void)
{
uint8_t buff_read[32];
uint32_t length = fifo_read_buff(pfifo_x, buff_read, 32);
if (length)
{
}
else
{
}
if (pfifo_x->error)
{
pfifo_x->error = 0;
}
}
要更换串口也非常简单,只需要将上面的
extern fifo_rx_def fifo_usart_rx_1;
fifo_rx_def *pfifo_x = &fifo_usart_rx_1;
改为对应的串口,比如串口二的
extern fifo_rx_def fifo_usart_rx_2;
fifo_rx_def *pfifo_x = &fifo_usart_rx_2;
剩下的配置完全一致
参考
|