完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
DMA
简介 DMA(Direct Memory Access,直接存储器访问) ,DMA 传输是将数据从一个地址空间复制到另外一个地址空间。CPU 只负责初始化这个传输动作,传输动作本身是由 DMA 控制器来实行和完成。 原理 一个完整的DMA传输过程必须经过DMA请求、DMA响应、DMA传输、DMA结束4个步骤。
DMA初始化结构体 结构体定义 typedef struct { //指定DMAy信道的外设基址 uint32_t DMA_PeripheralBaseAddr; //指定DMAy信道的内存基地址。 uint32_t DMA_MemoryBaseAddr; //指定这个外设是作为数据传输的目的地还是数据传输的来源 uint32_t DMA_DIR; //指定信道缓存的大小 uint32_t DMA_BufferSize; //指定外设地址寄存器是否递增 uint32_t DMA_PeripheralInc; //指定内存地址寄存器是否递增 uint32_t DMA_MemoryInc; //指定外设数据宽度。 uint32_t DMA_PeripheralDataSize; //指定内存数据宽度。 uint32_t DMA_MemoryDataSize; //指定DMAy信道x的操作模式。 uint32_t DMA_Mode; //指定DMAy信道x的软件优先级 uint32_t DMA_Priority; //指定DMAy通道x是否将在内存到内存传输中使用 uint32_t DMA_M2M; } DMA_InitTypeDef; 结构体参数取值 DMA_DIR 指定这个外设是作为数据传输的目的地还是数据传输的来源 //外设作为数据传输的目的地 #define DMA_DIR_PeripheralDST ((uint32_t)0x00000010) //外设作为数据传输的来源 #define DMA_DIR_PeripheralSRC ((uint32_t)0x00000000) DMA_PeripheralInc 指定外设地址寄存器是否递增 //递增 #define DMA_PeripheralInc_Enable ((uint32_t)0x00000040) //不递增 #define DMA_PeripheralInc_Disable ((uint32_t)0x00000000) DMA_MemoryInc 指定内存地址寄存器是否递增 //递增 #define DMA_MemoryInc_Enable ((uint32_t)0x00000080) //不递增 #define DMA_MemoryInc_Disable ((uint32_t)0x00000000) DMA_PeripheralDataSize 指定外设数据宽度。 //一个字节(8位) #define DMA_PeripheralDataSize_Byte ((uint32_t)0x00000000) //半个字(16位) #define DMA_PeripheralDataSize_HalfWord ((uint32_t)0x00000100) //一个字(32位) #define DMA_PeripheralDataSize_Word ((uint32_t)0x00000200) DMA_MemoryDataSize 指定内存数据宽度。 //一个字节(8位) #define DMA_MemoryDataSize_Byte ((uint32_t)0x00000000) //半个字(16位) #define DMA_MemoryDataSize_HalfWord ((uint32_t)0x00000400) //一个字(32位) #define DMA_MemoryDataSize_Word ((uint32_t)0x00000800) DMA_Mode 指定DMAy信道x的操作模式。 //循环模式 #define DMA_Mode_Circular ((uint32_t)0x00000020) //普通模式 #define DMA_Mode_Normal ((uint32_t)0x00000000) DMA_Priority 指定DMAy信道x的软件优先级 //很高 #define DMA_Priority_VeryHigh ((uint32_t)0x00003000) //高 #define DMA_Priority_High ((uint32_t)0x00002000) //中等 #define DMA_Priority_Medium ((uint32_t)0x00001000) //低 #define DMA_Priority_Low ((uint32_t)0x00000000) DMA_M2M 指定DMAy通道x是否将在内存到内存传输中使用 //是 #define DMA_M2M_Enable ((uint32_t)0x00004000) //不是 #define DMA_M2M_Disable ((uint32_t)0x00000000) DMA串口编程的一般步骤
根据以上步骤书写代码,因为分文件书写这样看着会有点麻烦,所以我就将示例写在一个文件中,望各位大佬见谅 说明: USART1与蓝牙进行连接,用于输入数据 USART2与PC进行连接,用于显示输入的数据 #include #include #include //缓冲区的大小 #define Buff_Size 1024 //此次接收结束的标志 volatile char rec_end_flag; //接收二级缓存中的数量 volatile unsigned short count; //一级缓存,即DMA直接搬运数据的目的地 volatile char usart1_recv_buff[Buff_Size]; /*二级缓存,即某次传输结束后将一级缓存的数据拷贝到此, 方便继续开启DMA使用一级缓存接收数据而数据不会被覆盖*/ volatile char recv_buff[Buff_Size]; //设置NVIC的优先级分组 void NVIC_config(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); } //初始化串口以及GPIO void usart1_init(void) { //用于GPIO初始化的结构体 GPIO_InitTypeDef GPIO_InitStructure; //用于串口初始化的结构体 USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStruct; //1. 初始化结构体 NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2; //应用NVIC结构体 NVIC_Init(&NVIC_InitStruct); //2. 使能相应的串口以及对应的GPIO时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE); //3. 配置GPIO结构体并应用 //初始化Usart1的Txd脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化Usart1的Rxd脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //应用GPIO结构体 GPIO_Init(GPIOA, &GPIO_InitStructure); //4. 配置Usart结构体并应用 //初始化Usart1结构体 USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; USART_Init(USART1, &USART_InitStructure); //5. 使能串口中断(看你需要什么中断,eg:使用USART_IT_IDLE) USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); //USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //6. 开启DMA接收 USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); //7. 使能串口 USART_Cmd(USART1, ENABLE); } //使用Debug的显示 void usart2_init(void) { //用于GPIO初始化的结构体 GPIO_InitTypeDef GPIO_InitStructure; //用于串口初始化的结构体 USART_InitTypeDef USART_InitStructure; //开启该串口以及其对应GPIO的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //初始化Usart2的Txd脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化Usart2的Rxd脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化Usart2结构体 USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; USART_Init(USART2, &USART_InitStructure); //使能Usart1 USART_Cmd(USART2, ENABLE); } //初始化DMA void DMA1_init(void) { DMA_InitTypeDef DMA_InitStructure; //1.使能DMA1时钟 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //将DMA的通道5的寄存器重设为缺省值 DMA_DeInit(DMA1_Channel5); //2. 配置DMA结构体并应用 //设置DMA源地址 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) & (USART1->DR); //内存地址基地址 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)usart1_recv_buff; //数据传输方向,从外设读取发送到内存 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = 1024; //DMA通道的DMA缓存的大小 //外设地址寄存器不递增 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //内存地址寄存器递增 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //外设数据宽度为8位 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //内存数据宽度为8位 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //工作模式为正常模式 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //DMA通道 x拥有中等优先级 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //此传输不是内存到内存传输 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //根据DMA_InitStruct中指定的参数初始化DMAy通道x。 DMA_Init(DMA1_Channel5, &DMA_InitStructure); //3. 使能DMA DMA_Cmd(DMA1_Channel5, ENABLE); } //串口1中断函数 void USART1_IRQHandler(void) { //判断是否为空闲中断 if (USART_GetITStatus(USART1, USART_IT_IDLE) == SET) { //数据接收完毕标志置1 rec_end_flag = 1; //关闭DMA,准备重新配置 DMA_Cmd(DMA1_Channel5, DISABLE); //clear DMA1 Channel5 global interrupt. DMA_ClearITPendingBit(DMA1_IT_GL5); //计算接收数据长度 count = 1024 - DMA_GetCurrDataCounter(DMA1_Channel5); memcpy((void *)recv_buff, (void *)usart1_recv_buff, count); //重新配置 DMA_SetCurrDataCounter(DMA1_Channel5, 1024); DMA_Cmd(DMA1_Channel5, ENABLE); //清除IDLE标志位 USART1->SR; USART1->DR; } } //串口发送一个字符(调试使用) void usart_send_ch(USART_TypeDef *USARTx, char ch) { while (RESET == USART_GetFlagStatus(USARTx, USART_FLAG_TC)) ; USART_SendData(USARTx, ch); } //串口发送字符串(调试使用) void usart_send_str(USART_TypeDef *USARTx, char *str, int length) { int i; for (i = 0; i < length; i++) { usart_send_ch(USARTx, *(str + i)); } //字符串末尾加换行rn char enter_str[] = {'r', 'n'}; for (i = 0; i < 2; i++) { usart_send_ch(USARTx, enter_str); } } int main() { // 1. 配置NVIC分组 NVIC_config(); // 2. 初始化串口以及GPIO usart1_init(); //使用Debug的显示 usart2_init(); // 3. 初始化DMA DMA1_init(); // 4. 编写串口中断函数 //见USART1_IRQHandler while (1) { //如此次传输完成 if (rec_end_flag == 1) { //将二级缓存的数据发送到串口2进行显示输出 usart_send_str(USART2, (char *)recv_buff, count); //处理完数据后将标志置0等待下次传输结束 rec_end_flag = 0; } } } 结果展示 手机端发送 发送了36个字符 PC端接收 36个字符加rn刚好38个字符,成功 接着又发送了长一点的数据,也没有任何问题 总结 使用DMA的好处就是可以不用占用CPU的资源,另外CPU进入串口的空闲中断(USART_IT_IDLE)会比串口的接收中断(USART_IT_RXNE)的次数要少。而且使用空闲中断完美的解决了接收任意长度的数据,如若大佬们认为以上代码有任何Bug或错请在评论指出,我也会不断的完善。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1800 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1629 浏览 1 评论
1096 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
735 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1684 浏览 2 评论
1944浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
744浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
578浏览 3评论
601浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
564浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-27 02:44 , Processed in 0.959801 second(s), Total 77, Slave 61 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号