STM32学习笔记之DMA使用

时间:2024-11-16 07:56:50

    实验:控制串口一以DMA方式发送(TX)数据

 

一、初始化DMA

       对STM32任何模块使用前都要对其初始化、首先就是初始化外设时钟,查看时钟

数可知DMA时钟由AHB得来。

初始化时钟:RCC->AHBENR|=1<<0;

        在读数据手册可知:直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。 两个DMA控制器有12个通道(DMA1有7个通道,DMA2有5个通道),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。我们实验用的是串口一、查看外设与通道的对应关系如下:

所以我们初始化DMA1的第四通道。关于通道配置过程:

其中几个简单个人理解:

CPARx:就是串口发送数据的寄存器地址;

CMARx:就是DMA传输的数据的地址;

CMDTRx:就是传输的数据大小 ,按字节传输,传输后值递减;

  1. <span style="background-color: rgb(240, 240, 240);">void MYDMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)</span>{
  2. RCC->AHBENR|=1<<0; //开启DMA1时钟
  3. delay_ms(5); //等待DMA时钟稳定
  4. DMA_CHx->CPAR=cpar; //DMA1 外设地址
  5. DMA_CHx->CMAR=(u32)cmar; //DMA1,存储器地址
  6. DMA1_MEM_LEN=cndtr; //保存DMA传输数据量
  7. DMA_CHx->CNDTR=cndtr; //DMA1,传输数据量
  8. DMA_CHx->CCR=0X00000000; //复位
  9. DMA_CHx->CCR|=1<<4; //从存储器读
  10. DMA_CHx->CCR|=0<<5; //普通模式
  11. DMA_CHx->CCR|=0<<6; //外设地址非增量模式
  12. DMA_CHx->CCR|=1<<7; //存储器增量模式
  13. DMA_CHx->CCR|=0<<8; //外设数据宽度为8位
  14. DMA_CHx->CCR|=0<<10; //存储器数据宽度8位
  15. DMA_CHx->CCR|=1<<12; //中等优先级
  16. DMA_CHx->CCR|=0<<14; //非存储器到存储器模式
  17. }
  1. //开启一次DMA传输
  2. void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
  3. {
  4. DMA_CHx->CCR&=~(1<<0); //关闭DMA传输
  5. DMA_CHx->CNDTR=DMA1_MEM_LEN; //DMA1,传输数据量
  6. DMA_CHx->CCR|=1<<0; //开启DMA传输
  7. }

初始化就基本完成。


在上面标识红色字体可知,开始DMA传输必须要有外设请求:



那么问题来了,请求信号是什么样的呢?


我们查看数据手册串口找到如下内容:




请求信号:就是配置USART_CR3,在实验中配置USART1->CR3=1<<7;


主函数如下:

  1. #define SEND_BUF_SIZE 1200
  2. u8 SendBuff[SEND_BUF_SIZE]; //发送数据缓冲区
  3. const u8 TEXT_TO_SEND[]={"STM32F1 DMA 串口实验"};
  4. int main(void)
  5. {
  6. u16 i;
  7. u8 t=0;
  8. u8 j,mask=0;
  9. Stm32_Clock_Init(9); //系统时钟设置
  10. uart_init(72,115200); //串口初始化为115200
  11. delay_init(72); //延时初始化
  12. KEY_Init(); //按键初始化
  13. MYDMA_Config(DMA1_Channel4,(u32)&USART1->DR,(u32)SendBuff,SEND_BUF_SIZE);//DMA1通道4,外设为串口1,存储器为SendBuff,长度SEND_BUF_SIZE.
  14. j=sizeof(TEXT_TO_SEND);
  15. for(i=0;i<SEND_BUF_SIZE;i++)//填充数据到SendBuff
  16. {
  17. if(t>=j)//加入换行符
  18. {
  19. if(mask)
  20. {
  21. SendBuff[i]=0x0a;
  22. t=0;
  23. }else
  24. {
  25. SendBuff[i]=0x0d;
  26. mask++;
  27. }
  28. }else//复制TEXT_TO_SEND语句
  29. {
  30. mask=0;
  31. SendBuff[i]=TEXT_TO_SEND[t];
  32. t++;
  33. }
  34. }
  35. i=0;
  36. while(1)
  37. {
  38. t=KEY_Scan(0);
  39. if(t==KEY0_PRES)//KEY0按下
  40. {
  41. USART1->CR3=1<<7; //使能串口1的DMA发送
  42. MYDMA_Enable(DMA1_Channel4);//开始一次DMA传输!
  43. //等待DMA传输完成,此时我们来做另外一些事,点灯
  44. //实际应用中,传输数据期间,可以执行另外的任务
  45. while(1)
  46. {
  47. <span style="color:#ff0000;"> if(DMA1->ISR&(1<<13))//等待通道4传输完成
  48. {
  49. DMA1->IFCR|=1<<13;//清除通道4传输完成标志
  50. break;</span>
  51. }
  52. }
  53. }
  54. }
  55. }
 

如程序红色部分 传输过程出现错误或者传送完成我们可以设置标志位来提示:


这些标志位都在中断寄存器中:

我们用的是第四通道,所以是13位完成标志位,完成后再清掉中断可以接受下一次中断,清中断寄存器DMA_IFCR与ISR的位对应,这里就不解释了


关于DMA依据数据手册的简单实验到这里就结束了