目录
前言
最近一部电视剧《点燃我,温暖你》可火啦,大家纷纷在写男主的同款爱心代码,作为一个点灯工程师,当然得玩点不同的,下面就来使用STM32单片机的DAC功能输出一个爱心的波形吧。
水平有限,如有错漏之处,敬请指正
1.DAC简介
所谓DAC,也就是数模转换,其功能就是将单片机中的数字量转换为模拟量(这里的电压0-VREF+的参考电压3.3V),STM32F103VET就内置了两路12位的DAC的模块,我们通过对电压的不断变化也就可以在示波器里读出我们想要的一些波形啦
2.DAC初始化
2-1.GPIO初始化
DAC需要一个管脚来输出它,在手册上我们可以看到,可以使用的管脚为PA4或PA5
static void DAC_Config(void)
{
GPIO_InitTypeDef GPIO_typeDef;
DAC_InitTypeDef DAC_typeDef;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC,ENABLE);
GPIO_typeDef.GPIO_Pin = GPIO_Pin_4;
GPIO_typeDef.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA,&GPIO_typeDef);
DAC_typeDef.DAC_Trigger = DAC_Trigger_T2_TRGO; //触发模式配置为定时器2
DAC_typeDef.DAC_WaveGeneration = DAC_Wave_Noise; //不使用波形发生器
DAC_typeDef.DAC_OutputBuffer = DAC_OutputBuffer_Disable; //不使用输出缓冲区
DAC_Init(DAC_Channel_1,&DAC_typeDef); //初始化DAC通道1
DAC_Cmd(DAC_Channel_1,ENABLE); //使能DAC通道1
}
2-2.定时器配置
上面我们配置的触发模式为定时器2触发DAC,所以接下来要配置定时器2,这里配置的是1Ms更新一次DAC的值
static void DAC_TIM_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimetypeDef;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //配置TIM时钟
TIM_TimetypeDef.TIM_Prescaler = (72-1); //预分频72 1us +1
TIM_TimetypeDef.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
TIM_TimetypeDef.TIM_Period = (1000-1); //计数1000次重新开始,1Ms
TIM_TimetypeDef.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimetypeDef.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimetypeDef); //初始化定时器
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); //配置TIM2触发源
TIM_Cmd(TIM2,ENABLE); //使能定时器2
}
2-3.DMA配置
配置完DMA后我们只需要给它一个通道和需要更改的DAC的数字量值,CPU就会自动更新输出了,这样大大提高了程序的效率
static void DAC_DMA_Config(void)
{
DMA_InitTypeDef DMA_typeDef;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2,ENABLE);
DMA_typeDef.DMA_PeripheralBaseAddr = 0x40007420; //DAC外设地址
DMA_typeDef.DMA_MemoryBaseAddr = (uint32_t)DAC_Sin;
DMA_typeDef.DMA_DIR = DMA_DIR_PeripheralDST; //数据传输方向内存至外设
DMA_typeDef.DMA_BufferSize = 256; //需要传128文数据
DMA_typeDef.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址固定
DMA_typeDef.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存数据地址自增
DMA_typeDef.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;//外设数据以字为单位
DMA_typeDef.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; //内存数据以字为单位
DMA_typeDef.DMA_Mode = DMA_Mode_Circular; //循环模式
DMA_typeDef.DMA_Priority = DMA_Priority_High; //高DMA通道优先级
DMA_typeDef.DMA_M2M = DMA_M2M_Disable; //非内存至内存模式
DMA_Init(DMA2_Channel3,&DMA_typeDef); //DMA2通道3初始化
DMA_Cmd(DMA2_Channel3, ENABLE); //DMA2通道3使能
DAC_DMACmd(DAC_Channel_1, ENABLE); //DAC的DMA请求使能
}
2-4.DAC初始化
为了我们主函数调用不那么混乱,我们这里再写一个函数来调用上面对DAC的配置
void User_DAC_Init(void)
{
uint16_t i;
DAC_Config();
DAC_TIM_Config();
DAC_DMA_Config();
for(i=0; i<256; i++)
{
DAC_Sin[i] = CH_1[i];
}
}
这里有两个数组,DAC_Sin是用于DMA输出的,CH_1就是用来存放我们要输出的波形的数组的
uint32_t DAC_Sin[128];
uint16_t CH_1[] = {3089,3089,3089,3089,3089,3089,3089,3089,
3089,3089,3089,3089,3089,3089,3089,3089,
3089,3089,3089,3089,3089,3089,3089,3089,
3089,3089,3089,3089,3089,3089,3089,3089,
3089,3089,3089,3089,3089,3089,3089,2873,
2606,2556,2826,3267,3567,3475,2995,2409,
2107,2316,2930,3557,3766,3382,2618,1955,
1833,2352,3191,3800,3761,3066,2140,1578,
1752,2565,3496,3929,3560,2600,1655,1334,
1853,2885,3759,3894,3179,2055,1239,1258,
2106,3237,3912,3676,2660,1506,950,1354,
2458,3545,3902,3279,2060,1021,826,1601,
2843,3742,3704,2735,1446,657,873,1950,
3187,3773,3316,2094,881,450,1072,2336,
3418,3605,2761,1414,419,405,1369,2677,
3469,3217,2065,736,65,461,1619,2918,
396,2794,1526,431,232,1075,2430,3440,
3471,2518,1213,409,635,1755,3058,3716,
3321,2143,956,533,1153,2423,3533,3781,
3024,1766,829,821,1751,3022,3827,3661,
2651,1461,867,1251,2366,3498,3929,3401,
2273,1285,1076,1777,2934,3808,3853,3062,
1964,1274,1434,2333,3390,3934,3637,2717,
1781,1436,1894,2848,3684,3883,3343,2441,
1769,1753,2386,3247,3789,3689,3049,2307,
1946,2177,2826,3462,3693,3413,2849,2390,
2320,2642,3104,3395,3366,3121,2926,3089,
3089,3089,3089,3089,3089,3089,3089,3089,
3089,3089,3089,3089,3089,3089,3089,3089,
3089,3089,3089,3089,3089,3089,3089,3089,
3089,3089,3089,3089,3089,3089,3089,3089,
3089,3089,3089,3089,3089,3089,3089,3089};
也就是每隔1Ms,单片机的DAC管脚就会按顺序输出这个数组的一位并且将这个数字量转换为模拟量,其对应关系是VREF+ = 4096的线性关系
2-5.调用初始化
int main(void)
{
User_DAC_Init();
while(1)
{
}
return 0;
}
3.计算爱心波形的数组
这里使用了matlab生成的一个数组,当然也可以用32来计算
clear; %清空工作区变量
clc; %清空命令窗口
%%画心
x = -2:1/50:2;
y = abs(x.^(2/3))+(0.99*(pi-x.^2).^(1/2)).*sin(13.14520*pi*x);
%%线性比例放大
Calculation = (y+1.7)* 952.3810;
OutData = int16(Calculation);
csvwrite('CH1.c',OutData); %输出这个数组
%%测试验证
plot(x,OutData,'r');
波形如下
将其数组粘贴至DAC需要输出的数组中,也就是上面的CH_1;
4.效果展示
将程序编译下载到单片机中间,开始运行,调整示波器后就可以得到一颗由模拟量生成的爱心啦