STM32F103VET6——DAC双通道(PA4、PA5)输出不同电压值,串口可控电压输出的大小
之前想写个程序能够控制STM32F103VET6的DAC双通道输出不同的电压值,并且能够通过串口控制两个通道电压实时可变化,但是在网上一直没找到资源,就反复的查看野火给的范例和数据手册DAC那块输出的讲解,最后整明白了分享给大家。
跟着我进行对比(野火的范例——双通道输出正弦波)的学习。
1、bsp_dac.h文件的修改
首先看一下野火在bsp_dac.h文件中如何定义的如下图
在这里野火定义了一个双DAC的 12 位右对齐数据保持寄存器(DAC_DHR12RD) ,我的理解是“双”字应该是说明这个寄存器是被双通道(PA4、PA5共用的),所以要想让两个通道输出不同的电压值,就必须重新定义这两个通道(PA4、PA5)的地址——定义成能一对一的模式,即一个单独的寄存器控制单独的一个。查看STM32官方中文参考手册的P194-P197页就会发现这两个通道(PA4、PA5)的确各自有控制自己的寄存器。下图为例。
#ifndef __DAC_H
#define __DAC_H
#include "stm32f10x.h"
//DAC 两个通道的寄存器地址,12位、右对齐、双通道
#define DAC_DHR12R1_ADDRESS (DAC_BASE+0x08)
#define DAC_DHR12R2_ADDRESS (DAC_BASE+0x14)
void DAC_OutVoltage(uint16_t VoltageLeft,uint16_t VoltageRight);
void DAC_Channel2_Config(void);
void DAC_Channel2_TIM_Config(void);
void DAC_Channel1_Config(void);
void DAC_Channel1_TIM_Config(void);
#endif /* __DAC_H */
请主要看我的两个地址的重新定义,其他是功能函数没什么好看的
2、bsp_dac.c文件的修改
在修改这块之前一定要看好这张图,当我们采用软件定时触发的方式更新寄存器内的值和DAC的输出(范例和我的过程都是通过刷新DAC的DMA来更新输出的)时,不是所有的定时触发都可用,仅限一下
接着我们来看一下DMA配置是几个比较重要的参数,先看野火范例
这里面我觉得一般使用时需要修改的,部分就是
①DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR12RD_ADDRESS; //外设数据地址
②DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&DualSine12bit ; //内存数据地址 DualSine12bit
③DMA_InitStructure.DMA_BufferSize = POINT_NUM; //缓存大小为POINT_NUM字节
④DMA_Init(DMA2_Channel4, &DMA_InitStructure);
/* 使能DMA2-14通道 */
DMA_Cmd(DMA2_Channel4, ENABLE);
这四个部分,第一个就是我们在头文件中修改的重新定义的地址
第二个是要更新的数据,就是我们DAC要转换的那个数字
第三个是DMA要缓存更新的数据的数量(在野火范例中是要不断更新输出32的正弦波不同时候的值大小,所以范例中采用宏定义的方式数量写成32,如果要实时更新,宏定义把这里数量改成1进行)。
第四个是DMA的通道,DAC的两个输出通道(PA4、PA5)都有属于自己的DMA通道,因此要想实时控制双通道输出不同的值就要使能他们各自的DMA。如下图(这里就需要到手册的DMA那章去看了)
然后定时器配置那块不讲了,你可以照葫芦画瓢在写一个定时器,一个定时器触发一个DAC通道,一对一的触发,我就是这么干的,至于只用一个定时器触发行不行我没尝试。注意的是用两个定时触发两个通道(一对一的方式)的话,定时扫描设置的时间一定要不同,相同的话会出bug。
下面我把我写的,准确说修改的bsp_dac.c文件分享如下
#include "bsp_dac.h"
//正弦波单个周期的点数
#define POINT_NUM_Channel2 1
#define POINT_NUM_Channel1 1
uint32_t DualSine12bit_Channel2[POINT_NUM_Channel2];
uint32_t DualSine12bit_Channel1[POINT_NUM_Channel1];
/*** @brief 使能DAC的时钟,初始化GPIO,初始化DAC的两个通道DAC_Channel_1、DAC_Channel_2* @param 无* @retval 无*/
void DAC_Channel2_Config(void)
{GPIO_InitTypeDef GPIO_InitStructure;DAC_InitTypeDef DAC_InitStructure;/* 使能GPIOA时钟 */RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); /* 使能DAC时钟 */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE); /* DAC的GPIO配置,模拟输入 */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_Init(GPIOA, &GPIO_InitStructure);/* 配置DAC 通道2 */DAC_InitStructure.DAC_Trigger = DAC_Trigger_T2_TRGO; //使用TIM2作为触发源DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; //不使用波形发生器DAC_InitStructure.DAC_OutputBuffer=DAC_OutputBuffer_Enable;/* 配置DAC 通道2 */DAC_Init(DAC_Channel_2, &DAC_InitStructure);/* 使能通道2 由PA5输出 */DAC_Cmd(DAC_Channel_2, ENABLE);/* 使能DAC通道2的DMA请求 */DAC_DMACmd(DAC_Channel_2, ENABLE);
}
/*** @brief 配置TIM2* @param 无* @retval 无*/
void DAC_Channel2_TIM_Config(void)
{TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;/* 使能TIM2时钟,TIM2CLK 为72M */RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); /* TIM2基本定时器配置 */// TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); TIM_TimeBaseStructure.TIM_Period = (2-1); //定时周期 20 TIM_TimeBaseStructure.TIM_Prescaler = 0x0; //预分频,不分频 72M / (0+1) = 72MTIM_TimeBaseStructure.TIM_ClockDivision = 0x0; //时钟分频系数TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);/* 配置TIM2触发源 */TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);/* 使能TIM2 */TIM_Cmd(TIM2, ENABLE);
}
/*** @brief 配置DMA* @param 无* @retval 无*/
static void DAC_Channel2_DMA_Config(void)
{ DMA_InitTypeDef DMA_InitStructure;/* 使能DMA2时钟 */RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);/* 配置DMA2 */DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR12R2_ADDRESS; //外设数据地址DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&DualSine12bit_Channel2 ; //内存数据地址 DualSine12bitDMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //数据传输方向内存至外设DMA_InitStructure.DMA_BufferSize = POINT_NUM_Channel2; //缓存大小为POINT_NUM字节 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通道优先级DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //非内存至内存模式 DMA_Init(DMA2_Channel4, &DMA_InitStructure); /* 使能DMA2-14通道 */DMA_Cmd(DMA2_Channel4, ENABLE);
}
/***************************************************************通道1*********************************************/
void DAC_Channel1_Config(void)
{GPIO_InitTypeDef GPIO_InitStructure;DAC_InitTypeDef DAC_InitStructure;/* 使能GPIOA时钟 */RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); /* 使能DAC时钟 */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE); /* DAC的GPIO配置,模拟输入 */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_Init(GPIOA, &GPIO_InitStructure);/* 配置DAC 通道2 */DAC_InitStructure.DAC_Trigger = DAC_Trigger_T4_TRGO; //使用TIM2作为触发源DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; //不使用波形发生器DAC_InitStructure.DAC_OutputBuffer=DAC_OutputBuffer_Enable;/* 配置DAC 通道1 */DAC_Init(DAC_Channel_1, &DAC_InitStructure);/* 使能通道2 由PA5输出 */DAC_Cmd(DAC_Channel_1, ENABLE);/* 使能DAC通道1的DMA请求 */DAC_DMACmd(DAC_Channel_1, ENABLE);
}
/*** @brief 配置TIM4* @param 无* @retval 无*/
void DAC_Channel1_TIM_Config(void)
{ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; /* 使能TIM2时钟,TIM2CLK 为72M */RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); /* TIM2基本定时器配置 */// TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); TIM_TimeBaseStructure.TIM_Period = (10-1); //定时周期 20 TIM_TimeBaseStructure.TIM_Prescaler = 0x0; //预分频,不分频 72M / (0+1) = 72MTIM_TimeBaseStructure.TIM_ClockDivision = 0x0; //时钟分频系数TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);/* 配置TIM4触发源 */TIM_SelectOutputTrigger(TIM4, TIM_TRGOSource_Update);/* 使能TIM4 */TIM_Cmd(TIM4, ENABLE);
}
/*** @brief 配置DMA* @param 无* @retval 无*/
static void DAC_Channel1_DMA_Config(void)
{ DMA_InitTypeDef DMA_InitStructure;/* 使能DMA2时钟 */RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);/* 配置DMA2 */DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR12R1_ADDRESS; //外设数据地址DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&DualSine12bit_Channel1 ; //内存数据地址 DualSine12bitDMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //数据传输方向内存至外设DMA_InitStructure.DMA_BufferSize = POINT_NUM_Channel1; //缓存大小为POINT_NUM字节 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通道优先级DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //非内存至内存模式 DMA_Init(DMA2_Channel3, &DMA_InitStructure); /* 使能DMA2-13通道 */DMA_Cmd(DMA2_Channel3, ENABLE);
}
/*************************************************************电压输出函数********************************/
/*** @brief DAC电压输出函数* @param Voltage* @retval 无*/
void DAC_OutVoltage(uint16_t VoltageLeft,uint16_t VoltageRight)
{uint32_t Idx = 0,Idy=0; DualSine12bit_Channel1[Idy] = (VoltageRight << 16) + (VoltageRight);DualSine12bit_Channel2[Idx] = (VoltageLeft << 16) + (VoltageLeft);DAC_Channel2_DMA_Config();DAC_Channel1_DMA_Config();
}
这个.C文件总结来说就是每一个DAC输出通道配一个DMA通道和一个软件定时触发。
至于怎么通过串口实时修改输出的值,代码如下,虽然只是串口接收.C文件的一部分,但我想看过野火WIFI那章节的人一看就明白了,不多说。
else if( ( pCh = strstr ( strEsp8266_Fram_Record .Data_RX_BUF, "Speed_" ) ) != 0 ) {SpeedTemp1 = * ( pCh + 6 )-0x30;SpeedTemp2 = * ( pCh + 7 )-0x30;SpeedTemp3 = * ( pCh + 8 )-0x30;SpeedTemp4 = * ( pCh + 9 )-0x30;SpeedTemp5 = * ( pCh + 10 )-0x30;SpeedTemp6 = * ( pCh + 11 )-0x30;SpeedTemp7 = * ( pCh + 12 )-0x30;SpeedTemp8 = * ( pCh + 13 )-0x30;SpeedLeft = SpeedTemp1*1000+SpeedTemp2*100+SpeedTemp3*10+SpeedTemp4;SpeedRight = SpeedTemp5*1000+SpeedTemp6*100*SpeedTemp7*10+SpeedTemp8;DAC_OutVoltage(SpeedLeft,SpeedRight); }
这样就能通过串口改变STM32F103VET6的两个DAC输出通道的电压输出大小(0-3.3V之间变化),实现实时变化。
个人学习心得,有什么讲的不对的地方请指出。
STM32F103VET6——DAC双通道(PA4、PA5)输出不同电压值,串口可控电压输出的大小相关推荐
- STM32配置DAC输出固定电压和方波
STM32F103VCT6自带两个12位DAC,DAC的转换速度一直没有查到,网上有人说是1MHZ的频率,那就是1us了.ADC的转换时间在56MHZ工作频率下为1us,在72MHZ工作频率下为1.1 ...
- STM32F103VET6基于HAL库和标准库下DAC 心形波形输出
STM32F103VET6基于HAL库和标准库下DAC 心形波形输出
- DAC双通道输出电压实验
更多交流欢迎关注作者抖音号:81849645041 目的 了解DAC数模转换工作原理,利用DAC两个通道输出电压,结合ADC读取引脚电压. 原理 DAC 为数字/模拟转换模块,它的作用就是把输入的数字 ...
- STM32CubeMX学习笔记(20)——DAC接口使用(输出正弦波)
一.DAC简介 DAC(Digital-to-Analog Converter),即数字/模拟转换模块,故名思议,它的作用就是把输入的数字编码,转换成对应的模拟电压输出,它的功能与 ADC 相反.在常 ...
- 嵌入式实验(四):用STM32输出PWM波形/DAC功能完成波形输出
0 实验目标 用STM32F103输出一路PWM波形,建议采用定时器方法.野火和网上大多数资源采用此方法,有完整源码.用示波器观察输出波形. 用STM32F103的DAC功能完成以下波形输出,用示波器 ...
- T46 Keithley 2612如何输出高电压?
Keithley 2612如何输出高电压? Error Code: 802 OUTPUT blocked by interlock 硬件 软件 Voltage compliance Source> ...
- STM32F103的DAC功能实现音频输出
本次实验主要内容:用Adobe audition或goldwave等音频编辑软件录制"您好欢迎光临!"的几秒钟的声音(8khz采样.8bit量化编码的单声道wav格式),确保音频数 ...
- 【硬件】如何使用直流稳压源输出负电压?
创作时间:2020-11-07 如何使用直流稳压源输出负电压? 1)先看看三脚插头 经过测量这个地和稳压源的三脚供电插头的EARTH通 2)看看直流稳压源的输出. 稳压源上有3个接线柱 红色 : + ...
- 手机常用的快充QC2.0/3.0协议,你都了解吗?设备(比如支持快充的手机)通过USB数据通讯口D+/D-输出电压信号给充电器,充电器内置USB输入解码芯片,然后充电器会输出目标电压。
手机常用的快充QC2.0/3.0协议,你都了解吗? 零是起源 资料共享- 公众号:零是起源 15 人赞同了该文章 何谓快充QC2.0/3.0协议? 1.QC2.0协议 QC2.0就是Quick Cha ...
最新文章
- 中one_气场不比BBA差,续航里程远、加速快的理想ONE值得买吗?
- php 小数末尾进1,PHP处理浮点数进一、去尾(不进行四舍五入) | 剑花烟雨江南...
- 2021年3月12日 北京格灵深瞳Java开发实习面试(一面)
- Eclipse将android 类编译为jar类库
- 华为“燃料”助力荣耀向上,全力冲击高端市场
- Programming WCF Services 学习笔记四、Instance Management
- 10.1 分别通过函数和重载运算符来实现复数相加
- python3.6 django部署_Centos7 django+uwsgi+nginx+python3.6.8部署
- matlab红外图像温度提取,一种基于红外热图的温度提取方法.pdf
- 组合式应用新利器?SaaS新时代事件网格如何解决集成标准化问题
- 2017.6.6 数表 思考记录
- mysql 5.6 没死_MySQL 5.6不删空用户的影响
- 带宽和下载速率的关系
- 付出与收获对等的自然法则,执行力离开去做马上去做
- 关于单页面应用一些随想
- Git Your branch is ahead of ‘origin/master‘ by X commits解决方法
- stosb, stosw, stosd 汇编指令
- 2021年苹果手机绕ID解锁教程-附使用工具
- 在 Word 中插入 Latex 公式
- 2023年天津天狮学院专升本市场营销专业《管理学》考试大纲