历史上的今天
今天是:2025年08月01日(星期五)
2019年08月01日 | STM32实战八 DAC/ADC
2019-08-01 来源:eefocus
这一章编写DAC和ADC程序,即数模/模数转换。程序中封装了两个DAC,各1个独立通道,对应输出脚为PA4和PA5,提供两个方法,ADDA::daDMA(Timer & tim)成员方法以DMA方式按预定数据生成两个正弦波,通道1(PA4)是半幅波形,通道2(PA5)是全幅波形。 ADDA::da()成员方法把指定内存的数据转换成模拟信号,未使用DMA,因为已经是一一对应。
模数转换使用ADC1转换器,共有10个通道,采用硬件存储DMA,不占用CPU时间,包括8个端口通道,对应输入为PA0-7,其中PA4和PA5与DAC共用,可以从内部检测DA/AD的正确性。还有两个内部通道,CPU温度和基准电压。
值得一提的是,参考代码中没有 “DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0; // 不限幅值” 这一行,造成daDMA程序有时能用,有时不能用,费了好多时间才找出问题。
ADDA.h
#ifndef __ADDA__
#define __ADDA__
extern "C" { // 兼容C,按C语言编译,Keil5中的包含文件已经加入了C++兼容,不用再加这一段
#pragma diag_remark 368 //消除 warning: #368-D: class " #include "stm32f10x.h" #include "stm32f10x_dma.h" #pragma diag_default 368 // 恢复368号警告 } #include "IO.h" #include "Timer.h" #define DAC_DHR12RD_Address 0x40007420 // DAC地址 #define ADC1_DR_Address ((uint32_t)0x4001244C) // ADC地址 //ADC_DR(ADC规则数据寄存器),偏移量=0x4c ADC1(0x40012400-0x400127ff) // 设置通道1数据 #define SETDAC1( v ) *((__IO uint32_t *)(DAC_BASE + (u32)0x00000008 + DAC_Align_12b_R)) = (uint32_t)v // 设置通道2数据 #define SETDAC2( v ) *((__IO uint32_t *)(DAC_BASE + (u32)0x00000014 + DAC_Align_12b_R)) = (uint32_t)v class ADDA : public IO { // Construction public: ADDA(); // Properties public: u16 m_adData[10]; // 10个ADC数据 PA0-7,CPU内部温度,内部基准电压 private: // Methods public: void daDMA(Timer & tim); // 用DMA生成正弦波 inline void ad(void); // ADc inline void da(void); // DAC // Overwrite public: }; #endif ADDA.cpp /** ****************************************************************************** * @file ADDA.cpp * @author Mr. Hu * @version V1.0.0 STM32F103VET6 * @date 06/07/2019 * @brief DMA,AD,DA * @IO * DMA2_Channel4 传送ADC1转换数据 * ADC1 数模转换器1 * ADC通道0-7 外部模拟量输入,对应PA0-5,其中PA4-5与DAC共用 * ADC通道16 CPU内部温度 * ADC通道17 内部基准电压值 * DAC通道1-2 模数转换器1-2,对应PA4-5与ADC共用 * PA0-7 ADC1的0-7通道 * PA4-5 ADC1的4-5通道,同时也是DAC的1-2通道 ****************************************************************************** * @remarks * 基于DMA的ADC,硬件转换和存储,不占CPU时间 * DAC有两个方法,一个是单一数据转换,未用DMA,因为本来就是直接写内存。另一个方法是按 * 指定数据生成正弦波,使用DMA。 * 实现8个模拟量输入,2个模拟量输出,其中PA4-5两个IO口既是输入,又是输出,AD/DA共用, * 可以从软件中监视AD/DA的数据,实际应用用时只能用于DA输出 * * 参考资料 * https://blog.csdn.net/qq_38410730/article/details/80071349 ADC有端口分配图 * https://blog.csdn.net/weixin_42653531/article/details/81123770 * https://blog.csdn.net/iteye_3759/article/details/82547927 * https://www.cnblogs.com/zhoubatuo/p/6118897.html */ /* Includes ------------------------------------------------------------------*/ extern "C" { // 兼容C,按C语言编译,Keil5中的包含文件已经加入了C++兼容,不用再加这一段 #pragma diag_remark 368 //消除 warning: #368-D: class " #include "stm32f10x_adc.h" #include "stm32f10x_dac.h" #include "stm32f10x_dma.h" #include "stm32f10x_tim.h" #include #pragma diag_default 368 // 恢复368号警告 } #include "IO.h" #include "ADDA.h" /** * @date 06/07/2019 * @brief 数模/模数转换 * IO(GPIOA, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7, GPIO_Mode_AIN, 2) * 占用PA0-PA7共8个ADC端口,其中PA4-5与DAC共用。 * @param None * @retval None */ ADDA::ADDA() : IO(GPIOA, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7, GPIO_Mode_AIN, 2) // GPIOx, nPin, GPIO_Mode_IPU 上拉, 2 输入时无效 { for(u16 i = 0; i < sizeof(m_adData)/sizeof(m_adData[0]); i++) m_adData[i] = 0; /* Enable peripheral clocks ------------------------------------------------*/ /* DMA2 clock enable */ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE); /* DAC Periph clock enable */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE); da(); // 启动DAC ad(); // 启动ADC } /** * @date 06/07/2019 * @brief 数模转换,DMA方式按预定数据生成两个正弦波,通道1(PA4)是半幅波形,通道2(PA5)是全幅波形。 * @param tim 触发源 * @retval None */ void ADDA::daDMA(Timer & tim) { TIM_TypeDef * ti = tim.m_pTIMx; assert_param( ti == TIM2 || ti == TIM4 || ti == TIM5 || ti == TIM6 || ti == TIM7 || ti == TIM8 ); /* TIMx TRGO selection */ TIM_SelectOutputTrigger(ti, TIM_TRGOSource_Update); u32 trigger = // 触发源 ti != TIM2 ? ti != TIM4 ? ti != TIM5 ? ti != TIM6 ? ti != TIM7 ? ti != TIM8 ? 0 // 异常 : DAC_Trigger_T8_TRGO : DAC_Trigger_T7_TRGO : DAC_Trigger_T6_TRGO : DAC_Trigger_T5_TRGO : DAC_Trigger_T4_TRGO : DAC_Trigger_T2_TRGO ; /* DAC channel1 Configuration */ // 特别提示,参考代码中没有给DAC_LFSRUnmask_TriangleAmplitude赋值, // 造成有时能用,有时不能用,赋值以后正常 DAC_InitTypeDef DAC_InitStructure; DAC_InitStructure.DAC_Trigger = trigger; // 设置触发源 DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; // 不产生波形 DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable; // 关闭输出缓存 DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0; // 不限幅值 DAC_Init(DAC_Channel_1, &DAC_InitStructure); // 初始化通道1 PA4 DAC_Init(DAC_Channel_2, &DAC_InitStructure); // 初始化通道2 PA5 // 正弦波数据 const uint16_t Sine12bit[32] = { // 全幅 2047, 2447, 2831, 3185, 3498, 3750, 3939, 4056, 4095, 4056, 3939, 3750, 3495, 3185, 2831, 2447, 2047, 1647, 1263, 909, 599, 344, 155, 38, 0, 38, 155, 344, 599, 909, 1263, 1647}; const uint16_t Sine12bitlow[32] = { // 半幅 1024, 1224, 1415,1592,1749,1875,1970,2028,2048,2028, 1969, 1875, 1748,1592,1415,1224,1024,824,632,454, 300,172,78,19,0,19,78,172,300,454,632,824}; uint32_t DualSine12bit[32]; //* Fill Sine32bit table * for (u8 Idx = 0; Idx < 32; Idx++) { DualSine12bit[Idx] = (Sine12bit[Idx] << 16) + (Sine12bitlow[Idx]); } //* DMA2 channel4 configuration * // DMA通道与DAC对应,不能换 DMA_DeInit(DMA2_Channel4); DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR12RD_Address; // 外设地址,双通道模式,其他模式请看手册 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&DualSine12bit; // 原始数据指针,正弦数据 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // 传输方向外设到内存 DMA_InitStructure.DMA_BufferSize = 32; // 缓存大小32个数据,每个数据对应两个通道 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址固定 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存地址自增 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; // 外设传输双字 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; // 内存传输双字 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 循环输出 DMA_InitStructure.DMA_Priority = DMA_Priority_High; // 优先级为高 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 禁止内存间传输 DMA_Init(DMA2_Channel4, &DMA_InitStructure); // 初始化 /* Enable DMA2 Channel4 */ DMA_Cmd(DMA2_Channel4, ENABLE); /* Enable DAC Channel1: Once the DAC channel1 is enabled, PA.04 is automatically connected to the DAC converter. */ DAC_Cmd(DAC_Channel_1, ENABLE); /* Enable DAC Channel2: Once the DAC channel2 is enabled, PA.05 is automatically connected to the DAC converter. */ DAC_Cmd(DAC_Channel_2, ENABLE); /* Enable DMA for DAC Channel2 */ DAC_DMACmd(DAC_Channel_2, ENABLE); } /** * @date 06/07/2019 * @brief 数模转换,工作方式是循环转换指定的数据 * SETDAC1和SETDAC2 分别设置两个通道数据,模拟量输出到PA4,PA5 * @param None * @retval None */ void ADDA::da() { RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE ); //使能DAC通道时钟 DAC_InitTypeDef DAC_InitType; DAC_InitType.DAC_Trigger=DAC_Trigger_None; //不使用触发功能 TEN1=0 DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;//不使用波形发生 DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;//屏蔽、幅值设置 DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ; //DAC1输出缓存关闭 BOFF1=1 DAC_Init(DAC_Channel_1,&DAC_InitType); //初始化DAC通道1 DAC_Init(DAC_Channel_2,&DAC_InitType); //初始化DAC通道1 DAC_Cmd(DAC_Channel_1, ENABLE); //使能DAC1 DAC_Cmd(DAC_Channel_2, ENABLE); //使能DAC1 DAC_SetChannel1Data(DAC_Align_12b_R, 0); //12位右对齐数据格式设置DAC值 DAC_SetChannel2Data(DAC_Align_12b_R, 0); //12位右对齐数据格式设置DAC值 } /** * @date 06/07/2019 * @brief 数模转换,用DMA方式,把结果存到m_adData[10]中 * m_adData[0]-[7]对应PA0-7的输入电压转换结果 * PA4,PA5与DAC共用 * m_adData[8]-[9]分别的CPU温度和内部基准电压 * @param None * @retval None */ // https://blog.csdn.net/nicholas_dlut/article/details/80937036 void ADDA::ad(void) { ADC_TempSensorVrefintCmd(ENABLE); //开启内部温度传感器 /* Enable peripheral clocks ------------------------------------------------*/ /* Enable DMA1 clock */ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); /* Enable ADC1 and GPIOC clock */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); // DMA通道与ADC对应,不能换 /* DMA1 channel1 configuration ----------------------------------------------*/ DMA_InitTypeDef DMA_InitStructure; DMA_DeInit(DMA1_Channel1); //选择DMA的通道1 //设定从ADC外设的数据寄存器(ADC1_DR_Address)转移到内存(ADCConcertedValue) //每次传输大小16位,使用DMA循环传输模式 DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; // 外设地址 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)m_adData;//数据缓冲区的地址 //外设为数据源 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //数据缓冲区,大小2半字 DMA_InitStructure.DMA_BufferSize = 10; // 外设地址固定 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //内存地址增加,多组adc时,使能,数据传输时,内存增加 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //半字 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //DMA循环传输 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //优先级高 DMA_InitStructure.DMA_Priority = DMA_Priority_High; //?? DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
上一篇:STM32实战七 数字滤波
下一篇:STM32实战九 编码器
史海拾趣
|
这几天折腾一个AD结果换算成某比值数的问题,终于有了结果,结果存放在一个字节内,不超过0X64(100d). 要将其转化为2位BCD码在七段LED上显示,搜了一些现成程序,发现计算时间消耗较多,于是想用查表法解决。以下是子程序,二进制数存在DATAA1里, ...… 查看全部问答> |
|
随着人们对汽车的操控性及舒适性需求不断升高,汽车车身中的电子设备越来越多,如电动后视镜、中控门锁、玻璃升降器、车灯乃至其它更多的高级功能等。 电源要求及方案选择 (BCM)设计重要 车身控制模块的一步是确定电源要求,以及选 ...… 查看全部问答> |
|
步骤如下: 俺先启动了一个小悬浮窗体,永远置顶 SetWindowPos(&(wndTopMost),10,10,25,25,SWP_DRAWFRAME); 然后启动我的视频程序(MFC对话框) 在OnInitDialog中 1.设定窗体位置 SetWindowPos(NULL, 0, 0, 800, 480, SWP_NOZORDER); 2.创 ...… 查看全部问答> |
|
请问tornado2.02有long long 这种类型么? 请问tornado2.02有long long 这种类型么? 即8个字节的整形数据? 我在c文件中定义了,对这个8字节整形变量做了除法,编译可以生成.o,但是链接时报错。 partialImage.o(.text+0x173510): undefined reference to `__divdi3\' partialImage.o(. ...… 查看全部问答> |
|
void Usart_init ( void ) { COM0= AT91C_BASE_US1; //* Define RXD and TXD as peripheral // Configure PIO controllers to periph mode AT91F_PIO_CfgPeriph( ...… 查看全部问答> |
|
用什么代码能开启pda的无线功能? wince高手们,小弟现在用的是华硕的626的一款pda,操作系统是windows mobile6.0,支持无线Wi-Fi; 小弟现有一个问题,我在程序中用什么函数能够自动启用无线功能?… 查看全部问答> |
|
向来以联发科为首的大陆手机芯片市场,进入同业相互厮杀的白热化阶段,联发科与展讯市占之争恐在第4季出现大幅改变,近期业界传出展讯第4季在台积电取得足够晶圆代工产能后,投片量大增60%,相较之下,联发科则降低在台积电投片量,集中于联电,近2 ...… 查看全部问答> |




