STM32系列——DAC双通道(PA4、PA5)输出不同电压值,串口可控电压输出的大小

之前想写个程序能够控制STM32F103VET6的DAC双通道输出不同的电压值,并且能够通过串口控制两个通道电压实时可变化,但是在网上一直没找到资源,就反复的查看野火给的范例和数据手册DAC那块输出的讲解,最后整明白了分享给大家。
跟着我进行对比(野火的范例——双通道输出正弦波)的学习。
1、bsp_dac.h文件的修改
首先看一下野火在bsp_dac.h文件中如何定义的如下图
图1
在这里野火定义了一个双DAC的 12 位右对齐数据保持寄存器(DAC_DHR12RD) ,我的理解是“双”字应该是说明这个寄存器是被双通道(PA4、PA5共用的),所以要想让两个通道输出不同的电压值,就必须重新定义这两个通道(PA4、PA5)的地址——定义成能一对一的模式,即一个单独的寄存器控制单独的一个。查看STM32官方中文参考手册的P194-P197页就会发现这两个通道(PA4、PA5)的确各自有控制自己的寄存器。下图为例。
图2
图3

#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来更新输出的)时,不是所有的定时触发都可用,仅限一下
图4
接着我们来看一下DMA配置是几个比较重要的参数,先看野火范例
图5
这里面我觉得一般使用时需要修改的,部分就是
①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那章去看了)
图6
然后定时器配置那块不讲了,你可以照葫芦画瓢在写一个定时器,一个定时器触发一个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) = 72M
  TIM_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 ;				//内存数据地址 DualSine12bit
  DMA_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) = 72M
  TIM_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 ;				//内存数据地址 DualSine12bit
  DMA_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之间变化),实现实时变化。

个人学习心得,有什么讲的不对的地方请指出。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值