历史上的今天
返回首页

历史上的今天

今天是: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 "" defines no constructor to initialize the following:

 

#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 "" defines no constructor to initialize the following:

 

#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;

推荐阅读

史海拾趣

问答坊 | AI 解惑

单字节BIN转BCD一法

这几天折腾一个AD结果换算成某比值数的问题,终于有了结果,结果存放在一个字节内,不超过0X64(100d). 要将其转化为2位BCD码在七段LED上显示,搜了一些现成程序,发现计算时间消耗较多,于是想用查表法解决。以下是子程序,二进制数存在DATAA1里, ...…

查看全部问答>

车身控制模块设计选择方案

随着人们对汽车的操控性及舒适性需求不断升高,汽车车身中的电子设备越来越多,如电动后视镜、中控门锁、玻璃升降器、车灯乃至其它更多的高级功能等。     电源要求及方案选择 (BCM)设计重要 车身控制模块的一步是确定电源要求,以及选 ...…

查看全部问答>

4X32M的SDRAM,大小是16MByte吗

RT啊,小白问题,希望高手不吝回答…

查看全部问答>

视频子窗体与置顶窗体的冲突问题,急!

步骤如下: 俺先启动了一个小悬浮窗体,永远置顶 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(. ...…

查看全部问答>

《电子设计从零开始》

谁有《电子设计从零开始》Word或txt格式的电子书 各位大哥大姐帮帮忙,要急用啊 拜托了…

查看全部问答>

请教有关atmel的at91sam7的串口程序问题。

void Usart_init ( void ) {     COM0= AT91C_BASE_US1;     //* Define RXD and TXD as peripheral     // Configure PIO controllers to periph mode      AT91F_PIO_CfgPeriph(   ...…

查看全部问答>

用什么代码能开启pda的无线功能?

用什么代码能开启pda的无线功能? wince高手们,小弟现在用的是华硕的626的一款pda,操作系统是windows   mobile6.0,支持无线Wi-Fi; 小弟现有一个问题,我在程序中用什么函数能够自动启用无线功能?…

查看全部问答>

手机芯片行业“厮杀”进入白热化

向来以联发科为首的大陆手机芯片市场,进入同业相互厮杀的白热化阶段,联发科与展讯市占之争恐在第4季出现大幅改变,近期业界传出展讯第4季在台积电取得足够晶圆代工产能后,投片量大增60%,相较之下,联发科则降低在台积电投片量,集中于联电,近2 ...…

查看全部问答>