目前状况是32连接ttl转usb到电脑串口调试助手,发送数据完全正常,接收数据也可以进串口的idle空闲中断,但是DMA_GetCurrDataCounter(DMA2_Stream5);获得的数据一直是最大值,也就是没有数据进来.
这是我串口的调试信息:
![image.png image.png](data/attachment/forum/202407/30/151501zrrdcycymyzarrdf.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300)
发送数据后这三行打印数据为中断内部的打印.显示是没有数据
uart.c
//uart.c
#include "uart.h"
//指定串口2接收数据计数 - 最开始接收数据个数为0
u32 UART1_RxCounter = 0;
//定义串口2接收缓冲区
u8 UART1_RxBuff[UART1_RXBUFF_SIZE];
//定义串口2发送缓冲区
u8 UART1_TxBuff[UART1_TXBUFF_SIZE];
u8 UART1_CMD[UART1_RXBUFF_SIZE];
void USART1_Init(void){
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
/* 使能时钟 */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);//使能DMA2时钟
/* GPIO相关配置 */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF ;//复用功能
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP ;//推挽输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;//无上拉 无下拉
GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed ;//高速
GPIO_InitStructure.GPIO_Pin = USART1_TX_PIN;//配置发送引脚
GPIO_Init (USART1_TX_GPIO_PORT,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = USART1_RX_PIN;//配置接收引脚
GPIO_Init (USART1_RX_GPIO_PORT,&GPIO_InitStructure);
/* 将对应的IO口连接到外设,开始启动复用功能 */
GPIO_PinAFConfig(USART1_TX_GPIO_PORT,USART1_TX_SOURCE,USART1_TX_AF);
GPIO_PinAFConfig(USART1_RX_GPIO_PORT,USART1_RX_SOURCE,USART1_RX_AF);
/* USART1的相关配置 */
USART_InitStructure.USART_BaudRate = USART1_BAUDRATE;//波特率115200
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//不使用硬件流
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx ;//USART模式控制:同时使能接收和发送
USART_InitStructure.USART_Parity = USART_Parity_No;//校验位选择:不使用校验
USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位:1个停止位
USART_InitStructure.USART_WordLength = USART_WordLength_8b ;//字长(数据位+校验位):8
USART_Init(USART1,&USART_InitStructure);
/* 配置USART为中断源 */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStructure);
/* 配置发送 */
DMA_DeInit(USART1_TX_DMA_STREAM);
DMA_InitStructure.DMA_BufferSize = UART1_TXBUFF_SIZE;//随便配置,因为在接收到数据后我们会重新给这值赋值
DMA_InitStructure.DMA_Channel = USART1_TX_DMA_CHANNEL;//串口发送通道
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;//DMA搬运方向:存储器到外设
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)UART1_TxBuff;//存储器地址
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//存储器突发模式选择:单次模式
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//存储器数据宽度:字节
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable ;//使能存储器地址自动递增功能
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//DMA 传输模式选择:一次传输
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;//外设地址
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发模式选择:单次模式
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据宽度:字节
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//禁止外设地址自动递增功能
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//软件设置数据流的优先级:中等
/* FIFO不用随便配置 */
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_Init(USART1_TX_DMA_STREAM, &DMA_InitStructure);
// DMA_ClearITPendingBit(USART1_TX_DMA_STREAM,DMA_IT_TCIF7); //清除标志位
DMA_Cmd(USART1_TX_DMA_STREAM, DISABLE);//发送先失能
DMA_ITConfig(USART1_TX_DMA_STREAM,DMA_IT_TC,ENABLE); //使能发送完成中断
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream7_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* 配置接收 */
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)UART1_RxBuff;//存储器地址
DMA_InitStructure.DMA_BufferSize = UART1_RXBUFF_SIZE;//接收数据的长度
DMA_InitStructure.DMA_Channel = USART1_RX_DMA_CHANNEL;//串口发送通道
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;//DMA搬运方向:外设到存储器
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//DMA 传输模式选择:一次传输
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;//软件设置数据流的优先级:高
/* 其余配置与上面一样 */
DMA_Init(USART1_RX_DMA_STREAM, &DMA_InitStructure);
USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);//开启空闲中断
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
USART_Cmd(USART1,ENABLE);//使能串口
DMA_Cmd(USART1_RX_DMA_STREAM, ENABLE);//接收使能
}
///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int c, FILE *fp) {
//首先判断发送数据寄存器是否还有数据,如果有数据目前采用轮询方式死等,知道数据发送完毕
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
//发送字符
USART_SendData(USART1, c);
return c;
}
void UART1_Putc(u8 c) {
//首先判断发送数据寄存器是否还有数据,如果有数据目前采用轮询方式死等,知道数据发送完毕
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
//发送字符
USART_SendData(USART1, c);
}
//定义串口1发送字符串函数
//pstr = "hello,world\n"
void UART1_Puts(u8 *pstr) {
while(*pstr) {
UART1_Putc(*pstr);
pstr++;
}
}
uart.h
//uart.h
#ifndef __UART_H
#define __UART_H
//包含头文件
#include "public.h"
typedef struct __FILE FILE;
#define T_Pri printf
//#define T_Pri
#define UART1_TXBUFF_SIZE 128 //串口1发送缓冲区大小
#define UART1_RXBUFF_SIZE 128 //串口1接收缓冲区大小
extern u32 UART1_RxCounter;//指定串口1接收数据计数
//声明串口1接收缓冲区
extern u8 UART1_RxBuff[UART1_RXBUFF_SIZE];
//声明串口1发送缓冲区
extern u8 UART1_TxBuff[UART1_TXBUFF_SIZE];
extern u8 UART1_CMD[UART1_RXBUFF_SIZE];
#define USART1_BAUDRATE 115200
/* 串口Tx——PA9 */
#define USART1_TX_PIN GPIO_Pin_9
#define USART1_TX_GPIO_PORT GPIOA
#define USART1_TX_SOURCE GPIO_PinSource9
#define USART1_TX_AF GPIO_AF_USART1
/* 串口Rx——PA10 */
#define USART1_RX_PIN GPIO_Pin_10
#define USART1_RX_GPIO_PORT GPIOA
#define USART1_RX_SOURCE GPIO_PinSource10
#define USART1_RX_AF GPIO_AF_USART1
/* DMA */
#define USART1_TX_DMA_CHANNEL DMA_Channel_4
#define USART1_TX_DMA_STREAM DMA2_Stream7
#define USART1_RX_DMA_CHANNEL DMA_Channel_4
#define USART1_RX_DMA_STREAM DMA2_Stream5
extern void USART1_Init(void);
extern void UART1_Puts(u8 *pstr);
#endif
USART1_IRQHandler中断处理函数:
void USART1_IRQHandler(void)
{
uint16_t temp;
if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)
{
DMA_Cmd(DMA2_Stream5, DISABLE);
USART1->SR; //先读状态寄存器
USART1->DR; //后读数据寄存器
//首先要获取此次串口1利用DMA接收到的数据大小=DMA总的内存大小-剩余DMA缓冲区大小
UART1_RxCounter = UART1_RXBUFF_SIZE - DMA_GetCurrDataCounter(DMA2_Stream5);
printf("UART1_RxBuff = [%s]\r\n", UART1_RxBuff);
printf("UART1_RxCounter = [%d]\r\n", UART1_RxCounter);
printf("DMA_GetCurrDataCounter(DMA2_Stream5) = [%d]\r\n", DMA_GetCurrDataCounter(DMA2_Stream5));
DMA2_Stream5->NDTR = UART1_RXBUFF_SIZE;
DMA_Cmd(DMA2_Stream5, ENABLE);
}
}
vTaskForCmd命令任务:
void vTaskForCmd(void *arg) {
const TickType_t xTicksToWait = pdMS_TO_TICKS(500);
while(1){
//采用DMA+IDLE,如果下位机将上位机发送来的命令接收完毕了,下位机才能去查找命令
if(UART1_RxCounter != 0) {
//下位机先给上位机发送一个请求输入数据的提示信息
UART1_Puts("\n Receive command is: ");
UART1_Puts(UART1_RxBuff);
UART1_Puts("\n");
//下位机接收到上位机发送来的控制命令操作对应的硬件
pcmd = find_cmd((char *)UART1_RxBuff); //根据上位机发送来的命令名称找到对应的命令对象
if(pcmd != 0)
pcmd->callback(); //调用匹配成功的命令的处理函数
else
UART1_Puts("invalid command\n");
UART1_RxCounter = 0; //重新等待下一个命令
}
vTaskDelay(xTicksToWait);
}
}
有些串口外设是以 IDLE 帧开始的。
现在没收到数据,重点检查DMA接收这块的配置。
如果每次读到的数据剩余都是最大值,说明DMA没有发生接收。或者每次刚好收满重装了,我看你配置的是DMA接收循环模式,
但即使这样,可以通过接受缓冲看到数据。
另外,建议你先把FIFO关闭,即DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;