决定开始在CSDN上记录学习单片机的过程了
想讲讲我对串口空闲中断加DMA的理解
1.DMA传输的好处
一般的数据通过串口传输都会经过CPU的读取 当模块多起来(比如五六个激光测距模块读取再加上MPU的解算)CPU会负荷过重而致使程序卡顿造成干扰??
而DMA可以当成传输数据的桥梁 (介于外设到存储器 /存储器到外设 /存储器到存储器?)
桥梁的作用是自身开辟一个通道使数据传输无需经过CPU从而减少CPU消耗
2.串口空闲中断
串口空闲中断顾名思义 在串口空闲时(发完一帧数据)产生中断
3.串口空闲中断加DMA接受不定长度数据过程
- 0、开启串口DMA接收
- 1、串口收到数据,DMA不断传输数据到存储buf(在DMA配置了外设地址与存储器地址,如果是外设到存储器,则这个存储buf就是存储器地址)
- 2、一帧数据发送完毕,串口暂时空闲,触发串口空闲中断
- 3、在中断服务函数中,可以计算刚才收到了多少个字节的数据(收到字节长度? =? DMA设定的数据接受量-DMA_GetCurrDataCounter(dma中剩余长度))
- 4、解码存储buf,清除标志位,开始下一帧接收
?
unsigned char ucRxFinish=0; unsigned char ucRxData[100]; int uiData; u8 usart6_rx_buf[DMA_REC_LEN] = {0}; u8 dma_rec_buff[DMA_REC_LEN] = {0}; u16 uart6_rec_cnt = 0;?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?//串口接收数据长度 ? u8 data_backup[DMA_REC_LEN] = {0}; ?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?//数据备份 _Bool receiveOK_flag = 0;?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?//接收完成标志位
//DMAx的各通道配置 //这里的传输形式是固定的,这点要根据不同的情况来修改 //从存储器->外设模式/8位数据宽度/存储器增量模式 //DMA_Streamx:DMA数据流,DMA1_Stream0~7/DMA2_Stream0~7 //chx:DMA通道选择,@ref DMA_channel DMA_Channel_0~DMA_Channel_7?
void MYDMA_Config(DMA_Stream_TypeDef *DMA_Streamx) ?//DMA配置
{? ? ?? ?DMA_InitTypeDef ?DMA_InitStructure; ?? ? ?? ?if((u32)DMA_Streamx>(u32)DMA2)//得到当前stream是属于DMA2还是DMA1 ?? ?{ ?? ? ?RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2时钟使能? ?? ??? ? ?? ?}else? ?? ?{ ?? ? ?RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);//DMA1时钟使能? ?? ?} ? DMA_DeInit(DMA_Streamx); ?? ? ?? ?while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}//等待DMA可配置? ?? ? ? /* 配置 DMA Stream */ ? DMA_InitStructure.DMA_Channel = DMA_Channel_5; ?//通道选择 ?5 ? DMA_InitStructure.DMA_PeripheralBaseAddr = ( u32 )&USART6->DR;//DMA外设地址 ? DMA_InitStructure.DMA_Memory0BaseAddr = ( u32 )dma_rec_buff;//DMA 存储器0地址 ? DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;//外设模式到存储器 ? DMA_InitStructure.DMA_BufferSize = DMA_REC_LEN;//数据传输量? ? DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式 ? DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器增量模式 ? DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据长度:8位 ? DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//存储器数据长度:8位 ? DMA_InitStructure.DMA_Mode = DMA_Mode_Circular ;// 使用普通模式? ? DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//中等优先级 ? DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; ? ? ? ?? ? DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; ? DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//存储器突发单次传输 ? DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发单次传输 ? DMA_Init(DMA_Streamx, &DMA_InitStructure);//初始化DMA Stream ?? ?DMA_Cmd(DMA_Streamx,ENABLE);?? ??? ?
}?
//开启一次DMA传输 //DMA_Streamx:DMA数据流,DMA1_Stream0~7/DMA2_Stream0~7? //ndtr:数据传输量 ? void MYDMA_Enable(DMA_Stream_TypeDef *DMA_Streamx,u16 ndtr) ? ? //使能DMA { ? ?? ?DMA_Cmd(DMA_Streamx, DISABLE); ? ? ? ? ? ? ? ? ? ? ?//关闭DMA传输? ?? ? ?? ?while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}?? ?//确保DMA可以被设置 ? ?? ??? ? ?? ?DMA_SetCurrDataCounter(DMA_Streamx,ndtr); ? ? ? ? ?//数据传输量 ? ? ?? ?DMA_Cmd(DMA_Streamx, ENABLE); ? ? ? ? ? ? ? ? ? ? ?//开启DMA传输? }?? ? ?
void USART6_Init(uint32_t bound)//DMA2_Stream2 { ?? ?GPIO_InitTypeDef GPIO_Initstructure; ?? ?USART_InitTypeDef USART_Initstructure; ?? ?NVIC_InitTypeDef NVIC_Initstrcuture; ?? ? ?? ?RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART6,ENABLE ); ?? ?RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG ,ENABLE ); ?? ? ?? ?GPIO_PinAFConfig(GPIOG,GPIO_PinSource14,GPIO_AF_USART6); ?? ?GPIO_PinAFConfig(GPIOG,GPIO_PinSource9,GPIO_AF_USART6); ?? ? ? GPIO_Initstructure.GPIO_Pin = GPIO_Pin_9; ?? ?GPIO_Initstructure.GPIO_Mode = GPIO_Mode_AF; ?? ?GPIO_Initstructure.GPIO_OType = GPIO_OType_PP; ?? ?GPIO_Initstructure.GPIO_PuPd = GPIO_PuPd_UP; ?? ?GPIO_Initstructure.GPIO_Speed = GPIO_Speed_100MHz; ?? ?GPIO_Init(GPIOG,&GPIO_Initstructure); ?? ?GPIO_Initstructure.GPIO_Pin = GPIO_Pin_14; ?? ?GPIO_Initstructure.GPIO_Mode = GPIO_Mode_AF; ?? ?GPIO_Initstructure.GPIO_OType = GPIO_OType_PP; ?? ?GPIO_Initstructure.GPIO_PuPd = GPIO_PuPd_UP; ?? ?GPIO_Initstructure.GPIO_Speed = GPIO_Speed_100MHz; ?? ?GPIO_Init(GPIOG,&GPIO_Initstructure); ?? ? ?? ?USART_Initstructure.USART_BaudRate = bound; ?? ?USART_Initstructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; ?? ?USART_Initstructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; ?? ?USART_Initstructure.USART_Parity = USART_Parity_No; ?? ?USART_Initstructure.USART_StopBits = USART_StopBits_1; ?? ?USART_Initstructure.USART_WordLength = USART_WordLength_8b; ?? ?USART_Init(USART6,&USART_Initstructure); ?? ? ?? ?NVIC_Initstrcuture.NVIC_IRQChannel = USART6_IRQn; ?? ?NVIC_Initstrcuture.NVIC_IRQChannelPreemptionPriority=1; ?? ?NVIC_Initstrcuture.NVIC_IRQChannelSubPriority =2;?? ??? ? ?? ?NVIC_Initstrcuture.NVIC_IRQChannelCmd = ENABLE;?? ??? ? ?? ?NVIC_Init(&NVIC_Initstrcuture);?? ? ?? ? ?? ?USART_ITConfig(USART6, USART_IT_IDLE, ENABLE);//开启空闲中断 ?? ?USART_DMACmd(USART6, USART_DMAReq_Rx, ENABLE);//开启DMA接收 ?? ?USART_Cmd(USART6, ENABLE); ?? ? ?? ?//initialize the DMA channel. ?? ?MYDMA_Config(DMA2_Stream1);? ?? ?
}
void USART6_IRQHandler(void)? { ?? ?uint8_t rc_tmp; ?? ?uint16_t rc_len;
?? ?if(USART_GetITStatus(USART6,USART_IT_IDLE)!=RESET) ?? ?{ ?? ? ? ?rc_tmp=USART6->SR; ? ? ? rc_tmp=USART6->DR;//软件序列清除IDLE标志位 ? ? ? DMA_Cmd(DMA2_Stream1, DISABLE); //关闭DMA,准备重新配置 ? ? ? DMA_ClearITPendingBit(DMA2_Stream1, DMA_IT_TCIF1);?? ?// Clear Transfer Complete flag ? ? ? DMA_ClearITPendingBit(DMA2_Stream1, DMA_IT_TEIF1);?? ?// Clear Transfer error flag?? ? ? ? ? rc_len = DMA_REC_LEN - DMA_GetCurrDataCounter(DMA2_Stream1);//计算接收数据长度
// ? ? ?Data_Decode(USART1_Rx_Buffer);//解码收到的数据 ?? ? ?} ?? ? MYDMA_Enable(DMA2_Stream1,DMA_REC_LEN);//开启下一次DMA接收
}
这份代码是用在激光测距上的
最重要的调试
代码csdn上都有其实就难在调试
1.首先要注意的是串口的配置? 第一次调试怎么也读不到值困惑了好久 后边把串口2换成串口6就行了
因为用的是正点原子的模板 串口1和串口6的时钟配置是一样的 而串口2345时钟又是另外的配置 我没有仔细去深究有兴趣的可以自己去查查看
2.移植程序时一定要细心 如果你串口的DR寄存器一直没值或者有个1 emmmm你可以看看是不是外设地址( u32 )&USART6->DR 这? 串口没有改成你用的串口
3.还要注意主程序初始化的顺序? 在CSDN上偶然看到一位博主说 DMA的初始化要在串口6之前
反过来会读不到第一位数据
OOOOOO
有点小感言? 调试这份代码用了很久 是比赛要用的代码 队长一直在催进度哈哈? 其实那会配置都已经好啦 但我太粗心了有个地方疏忽了然后一直没好 ,而没好=不会 ........
所以真正厉害的人不止是会写代码啊 调试也很厉害 (苦哈哈) ?
|