你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32单片机 ADC、DAC和DMA数据传输

[复制链接]
STMCU小助手 发布时间:2022-4-8 21:18
对于STM32的ADC模数转换器的介绍以及配置在文章《STM32单片机(六). 传感器的使用》中已经详细介绍,在本章节中主要介绍DAC数模转换器以及DMA的使用。

1、DAC转换
1.1 数模转换器的介绍

DAC(Digital to analog converter),数字模拟转换器,可以将数字信号转换为模拟信号。DAC可以输出电压模拟信号,用来去驱动其它器件。STM32F1中的DAC模块是由12位电压输出数模转换器,可以配置为8位或12位模式,也可以与DMA控制器配合使用。在12位模式下,数据可使用左对齐或右对齐方式;8位模式下,数据只有右对齐方式。DAC含有两个输出通道,每个通道各对应一个转换器。在双通道模式下,每个通道可进行单独的转换;当两个通道组合在一起同步执行更新操作时,也可同时进行转换。DAC可通过输入参考电压引脚VREF+来提高转换后的数据精度,对于DAC通道的模块框图如下所示:

QY76((WZ09RLK[T~P941YMW.png

1.2 DAC的配置步骤

使用库函数对DAC进行配置需要使用到库文件stm32f10x_dac.h和stm32f10x_dac.c,详细的步骤如下:

1、 使能端口及DAC时钟,设置引脚为模拟输入模式;
在STM32F103中DAC两个通道对应的是PA4和PA5引脚,因此需要使能GPIOA以及DAC时钟,DAC是挂载在APB1总线上的设备,GPIOA是挂载在APB2总线上的设备。调用函数:RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能GPIOA时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);//使DA时钟

2、 初始化DAC,设置DAC工作模式;
调用函数:void DAC_Init(uint32_t DAC_Channel, DAC_InitTypeDef* DAC_InitStruct);
第一个参数选取DAC通道(DAC_Channelx),第二个参数结构体DAC_InitTypeDef的成员变量如下:

  1. typedef struct
  2. {
  3. uint32_t DAC_Trigger;                                                         //DAC触发选择
  4. uint32_t DAC_WaveGeneration;                                        //DAC波形发生
  5. uint32_t DAC_LFSRUnmask_TriangleAmplitude;        //屏蔽/幅值选择器
  6. uint32_t DAC_OutputBuffer;                                                //DAC输出缓存
  7. }DAC_InitTypeDef;
复制代码

3、 使能DAC输出通道;
调用函数:void DAC_Cmd(uint32_t DAC_Channel, FunctionalState NewState);
4、 设置DAC输出值;
在使用12位数据右对齐的工作模式下,通过设置DHR12R1就可已在DAC输出引脚得到不同的电压值,调用函数:DAC_SetChannel1Data(DAC_Align_12b_R, 0);
读取DAC对应通道最后一次转换值调用函数:uint16_t DAC_GetDataOutputValue(uint32_t DAC_Channel);

1.3 应用示例
STM32使用按键控制DAC输出电压的大小,并将该数据通过串口输出,详细的代码模块如下:
dac.h
  1. #ifndef _dac_H
  2. #define _dac_H

  3. #include "system.h"
  4. void DAC1_Init(void);

  5. #endif
复制代码

dac.c
  1. #include "dac.h"

  2. void DAC1_Init(void)
  3. {
  4. GPIO_InitTypeDef GPIO_InitStructure;
  5. DAC_InitTypeDef DAC_InitStruct;

  6. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
  7. RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC,ENABLE);

  8. GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;
  9. GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4;
  10. GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
  11. GPIO_Init(GPIOA,&GPIO_InitStructure);

  12. DAC_InitStruct.DAC_Trigger=DAC_Trigger_None;         //不使用触发
  13. DAC_InitStruct.DAC_WaveGeneration=DAC_WaveGeneration_None;        //不使用波形输出
  14. DAC_InitStruct.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;
  15. DAC_InitStruct.DAC_OutputBuffer=DAC_OutputBuffer_Disable;        //关闭输出缓存
  16. DAC_Init(DAC_Channel_1,&DAC_InitStruct);

  17. DAC_Cmd(DAC_Channel_1, ENABLE);
  18. DAC_SetChannel1Data(DAC_Align_12b_R,0);                        //初始电压值设置为0
  19. }
复制代码

main.c
  1. #include "system.h"
  2. #include "led.h"
  3. #include "SysTick.h"
  4. #include "usart.h"
  5. #include "key.h"
  6. #include "dac.h"

  7. int main()
  8. {
  9.         u8 i=0;
  10.         u8 key;
  11.         u16 dacVal;
  12.         u16 dac_value;
  13.         float U;
  14.         SysTick_Init(72);
  15.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  16.         LED_Init();
  17.         USART1_Init(9600);
  18.         DAC1_Init();
  19.   KEY_Init();
  20.         
  21.         while(1)
  22.         {
  23.                 key=KEY_Scan(0);
  24.                 if(key==KEY_UP)
  25.                 {
  26.                         dacVal+=400;
  27.                         if(dacVal>=4095)
  28.                         {
  29.                                 dacVal=4095;
  30.                         }
  31.                         DAC_SetChannel1Data(DAC_Align_12b_R,dacVal);                //更新电压
  32.                 }
  33.                 else if(key==KEY_DOWN)
  34.                 {
  35.                         dacVal-=400;
  36.                         if(dacVal<=0)
  37.                         {
  38.                                 dacVal=0;
  39.                         }
  40.                         DAC_SetChannel1Data(DAC_Align_12b_R,dacVal);
  41.                 }
  42.                 i++;
  43.                 if(i%20==0)
  44.                 {
  45.                         led1=!led1;
  46.                 }
  47.                 if(i%50==0)
  48.                 {
  49.                         dac_value=DAC_GetDataOutputValue(DAC_Channel_1);
  50.                         U=(float)dac_value*(3.3/4096);
  51.                         printf("输出电压为:%.2f\n",U);
  52.                 }
  53.                 delay_ms(10);
  54.                
  55.         }
  56. }
复制代码
2、DMA数据传输
2.1 DMA的介绍

DMA,Direct Memory Access,直接存储器访问。STM32中DMA可以实现外设与寄存器之间,存储器与存储器之间高效的数据传输,DMA传输数据的过程不需要CPU直接操作可以节省对CPU的占用。DMA是RAM和IO设备之间数据传输的通路,外设和存储器之间,存储器和存储器之间可以直接在通道上进行数据传输。在STM32F1中最多有2个DMA控制器(DMA2仅存在于大容量产品系列),DMA1有7个通道,DMA2有5个通道,每个通道可管理来自于一个或者多个外设对存储器访问的请求,面对这些请求通过一个仲裁器来协调其优先权。对于DMA的结构框图如下所示:
ZS4IWT0XW_K80(Y{BW7.png

DMA和Cortex-M3内核共享系统数据总线,当CPU和DMA同时访问相同的目标时,DMA会请求暂停CPU访问系统总线若干个周期,总线仲裁器执行循环调度,以保证CPU至少可以得到一半的系统总线带宽。
DMA的处理过程: 当发生一个事件后,外设向DMA控制器发送一个请求信号,DMA控制器根据通道优先权处理请求。当DMA控制器开始访问发出请求的外设时,DMA控制器立即给其发送一个应答信号。外设从DMA得到应答信号后,外设立即释放请求,请求一旦被释放,DMA控制器同时撤销硬打信号。当有更多的请求时,外设可以启动下一个周期。
DMA的数据配置: 外设到存储器,使用外设到存储器传输时,DMA外设寄存器的地址为外设的数据寄存器地址,DMA存储器的地址为用户自定义变量的地址;存储器到外设,使用存储器到外设的传输时,DMA外设寄存器地址对应的是其数据寄存器地址,DMA存储器地址为用户自定义变量(缓冲区,存储通过外设的数据)的地址;存储器到存储器,DMA外设寄存器地址为内部存储器(如Flash,可将其看作是外设)的地址,DMA存储器地址为用户自定义的变量(缓冲区,存储来自内部Flash的数据)的地址。DMA的传输完成分为两种模式,一次传输和循环传输。如果使用一次传输后DMA停止,若要在此传输,必须失能DMA后在重新配置方可继续传输;当使用循环传输时,一次传输完成后又再次恢复第一次传输时的配置进行数据传输,不断重复。

2.2 DMA的配置步骤
对于DMA的配置,需要使用到库函数文件stm32f10x_dma.h和stm32f10x_dma.c,详细的步骤如下所示:
1、 使能DMA控制器时钟;
使能DMA时钟,需要通过AHB1ENR寄存器控制,调用函数:void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState);
2、 初始化DMA通道;
需要配置DMA的通道、外设和内存地址、通道优先级以及传输数据量等,调用函数:void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);
其中第一个参数用于选择哪个DMA的通道x,第二个参数是一个结构体变量包含的成员变量如下:

  1. typedef struct{
  2.                 uint32_t DMA_PeripheralBaseAddr;                // 外设地址
  3.                 uint32_t DMA_MemoryBaseAddr;                 // 存储器地址
  4.                 uint32_t DMA_DIR;                                         // 传输方向
  5.                 uint32_t DMA_BufferSize;                                // 传输数目
  6.                 uint32_t DMA_PeripheralInc;                        // 外设地址增量模式
  7.                 uint32_t DMA_MemoryInc;                                 // 存储器地址增量模式
  8.                 uint32_t DMA_PeripheralDataSize;                 // 外设数据宽度
  9.                 uint32_t DMA_MemoryDataSize;                 // 存储器数据宽度
  10.                 uint32_t DMA_Mode;                                         // 模式选择
  11.                 uint32_t DMA_Mode;                                         // 模式选择
  12.                 uint32_t DMA_M2M;                                         // 存储器到存储器模式
  13. } DMA_InitTypeDef;
复制代码

3、 使能外设DMA功能;
配置好DMA后,需要使能外设的DMA功能,根据使用外设的不同调用相应的函数,如使能串口DMA的发送功能需要调用:USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
4、 开启DMA通道传输;
调用函数:void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Chammelx, FunctionalState NewState);
5、 查询DMA传输状态。
查询状态调用函数:FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);
获取当前传输剩余数据量大小调用函数:uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
设置传输数据量大小调用函数:void DMA_SeyCurrDatatCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber);

2.3 应用示例
STM32使用按键控制DMA串口USART1的数据传送,详细的代码模块如下:

dma.h
  1. #ifndef _dma_H
  2. #define _dma_H

  3. #include "system.h"
  4. void DMAx_Init(u32 paddr,u32 maddr,u16 buffsize,DMA_Channel_TypeDef* DMAy_Channelx);
  5. void DMAx_Enable(DMA_Channel_TypeDef* DMAy_Channelx,u16 ndtr);
  6. #endif
复制代码

dma.c

  1. #include "dma.h"

  2. void DMAx_Init(u32 paddr,u32 maddr,u16 buffsize,DMA_Channel_TypeDef* DMAy_Channelx)
  3. {
  4.         
  5.         DMA_InitTypeDef DMA_InitStruct;
  6.         
  7.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
  8.         
  9.         DMA_InitStruct.DMA_PeripheralBaseAddr=paddr;                                                //外设地址
  10.         DMA_InitStruct.DMA_MemoryBaseAddr=maddr;                                                //内存地址
  11.         DMA_InitStruct.DMA_DIR=DMA_DIR_PeripheralDST;                                        //存储器到外设
  12.         DMA_InitStruct.DMA_BufferSize=buffsize;                                                        //传输数据量大小
  13.         DMA_InitStruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable;                        //外设地址不增
  14.         DMA_InitStruct.DMA_MemoryInc=DMA_MemoryInc_Enable;                        //内存地址自增
  15.         DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;        //外设数据单位
  16.         DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;                //内存数据单位
  17.         DMA_InitStruct.DMA_Mode=DMA_Mode_Normal;                                                //一次传输
  18.         DMA_InitStruct.DMA_Priority=DMA_Priority_Medium;                                        //通道优先级中
  19.         DMA_InitStruct.DMA_M2M=DMA_M2M_Disable;                                                //存储器到存储器失能
  20.         
  21.         DMA_Init(DMAy_Channelx,&DMA_InitStruct);
  22.         
  23. }
  24. void DMAx_Enable(DMA_Channel_TypeDef* DMAy_Channelx,u16 ndtr)
  25. {
  26.         DMA_Cmd(DMAy_Channelx, DISABLE);
  27.         DMA_SetCurrDataCounter(DMAy_Channelx,ndtr);
  28.         DMA_Cmd(DMAy_Channelx, ENABLE);
  29. }
复制代码

main.c

  1. #include "system.h"
  2. #include "led.h"
  3. #include "SysTick.h"
  4. #include "usart.h"
  5. #include "key.h"
  6. #include "dma.h"
  7. #define buf_len 5000


  8. u8 sendBuffer[buf_len];

  9. void send_Data(u8 *p)
  10. {
  11.         u16 i;
  12.         for(i=0;i<buf_len;i++)
  13.         {
  14.                 *p='5';
  15.                 p++;
  16.         }
  17. }

  18. int main()
  19. {
  20.         u8 i=0;
  21.         u8 key;
  22.         
  23.         SysTick_Init(72);
  24.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  25.         LED_Init();
  26.         USART1_Init(9600);
  27.   KEY_Init();
  28.         DMAx_Init((u32)&USART1->DR,(u32)sendBuffer,buf_len,DMA1_Channel4);
  29.         send_Data(sendBuffer);
  30.         
  31.         while(1)
  32.         {
  33.                 key=KEY_Scan(0);
  34.                 if(key==KEY_UP)
  35.                 {
  36.                         USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);        //开启外设DMA功能
  37.                         DMAx_Enable(DMA1_Channel4,buf_len);
  38.                         
  39.                         while(1)
  40.                         {
  41.                                 if(DMA_GetFlagStatus(DMA1_FLAG_TC4)!=0)        //判断DMA数据是否发送完成
  42.                                 {
  43.                                         DMA_ClearFlag(DMA1_FLAG_TC4);
  44.                                         break;
  45.                                 }
  46.                                 led2=!led2;
  47.                                 delay_ms(300);
  48.                         }
  49.                 }
  50.                 i++;
  51.                 if(i%20==0)
  52.                 {
  53.                         led1=!led1;
  54.                 }
  55.                 delay_ms(10);
  56.         }
  57. }
复制代码










收藏 评论0 发布时间:2022-4-8 21:18

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版