一。 PWM DAC的原理
T= 定时器一个计数周期的时间,也就是它频率的倒数。
n = CCR计数器的值
任何一个连续信号都可以把它通过傅里叶变换成有直流分量+一次谐波+二次谐波+。。。 。n次谐波(n=无穷大)这种表示。
我们通过定时器产生一个PWM信号,是一系列方波输出到定时器的通道引脚,我们看到公式中有一个直流分量,然后有一次谐波,二次谐波。。。。。n次谐波,如果我们有办法先把谐波这一部分给去掉,那么只剩下直流分量,直流分量中 有几个常量,Vh一般是3.3,Vl一般是0,那么这个公式就可以表示成(n/N)*Vh,如果我们设置好了自动装载值N,那么输出的电压只与n有关,n越大,输出电压越大,n=0,输出电压就是0,这里这个n就是设置占空比CCR的值,通过设置n的值改变了输出电压。这里的前提是这个信号必须把后面的谐波去掉,这里就涉及到滤波的概念,如果在后面设计一个滤波器把谐波都给滤掉,就需要设计一个低通滤波器,使频率低的信号通过,去掉频率高的信号,当然如果简单的设计一个低通滤波器不能百分之百的把一次谐波,二次谐波等滤掉,如果把1次谐波很好的滤掉,则高次谐波就应该基本不存在了,精度还是可以接受的。
在讲解DAC的时候知道12位的DAC有2的12次方个值也就是4096个值,0--4095.那么通过PWMDAC产生的信号的分辨率怎么确定?
f(t)= ( n/N ) *Vh,这里n确定了幅度,那么最小值是n=0,n的最大值是n最大也不能超过N,即n = N-1,也就是说直接输出一个直流信号,所以分辨率=log2(N), 比如定时器设置ARR的值是256,N= 256,那么分辨率= log2(N)就是8位。
定时器是怎么来产生PWM信号的呢?
定时器开启之后,会从0开始往上计数,比如计数到自动装载值ARR-1,比如99,从0计数到99后又会从0计数到99,这样一个过程,或者向下计数,都是一样的,向下计数就是从自动装载值往下计数到0,然后又从装载值往下计数到0,这个过程。
怎么通过定时器产生PWM呢?还有一个输出比较寄存器CCR,定时器的控制器会把计数器中的值跟CCR中的值进行比较,比如我们设置了模式等相关参数后,当计数器的值比CCR中的值小的时候输出通道引脚输出高电平,当计数器计数到CCR后再往上计数,这个值大于CCR中的值,输出电平。到ARR-1后又从0开始往上计数,又比CCR小,所以又输出高电平,这样一直循环就产生了一个PWM波。
所以STM32定时器产生PWM波,它的周期是由ARR决定的,占空比由CCR来决定。
二。PWM DAC硬件连接
1. 需要设计一个滤波器,这里设计的二阶滤波,计算出滤波器的截止频率。
2. 我们用PA8,也就是定时器PA8(定时器一的通道1)产生PWM,最好PWM通过低通滤波转换成为一个直流信号。
PA8经过滤波器连到PDC ,我们把PDC和ADC连到一起。
这样可以通过DAC输出信号,然后通过ADC进行测量。
三。 代码讲解
1. timer.c
//TIM1 CH1 PWM输出设置
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM1_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //使能定时器1TIMx外设
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA外设时钟使能
//设置该引脚为复用输出功能,输出TIM1 CH1的PWM脉冲波形
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //TIM1_CH1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //设置PA8为复用功能输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIO
TIM_TimeBaseStructure.TIM_Period = arr; //设置自动重装载周期值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置预分频值 不分频
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //CH1 PWM2模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //OC1 低电平有效
TIM_OC1Init(TIM1, &TIM_OCInitStructure); //根据指定的参数初始化外设TIMx,初始化输出比较通道1
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); //CH1 预装载使能
TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的预装载寄存器
TIM_CtrlPWMOutputs(TIM1,ENABLE); //MOE 主输出使能,高级定时器必须开启这个
TIM_Cmd(TIM1, ENABLE); //使能TIMx
}
2,main.c
//设置输出电压
//vol:0~330,代表0~3.3V
void PWM_DAC_Set(u16 vol)
{
float temp=vol;
temp/=100;
temp=temp*256/3.3;
TIM_SetCompare1(TIM1,temp);
}
int main(void)
{
u16 adcx;
float temp;
u8 t=0;
u16 pwmval=0;
u8 key;
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
KEY_Init(); //KEY初始化
LED_Init(); //LED端口初始化
usmart_dev.init(72); //初始化USMART
LCD_Init(); //LCD初始化
Adc_Init(); //ADC初始化
TIM1_PWM_Init(255,0); //TIM1 PWM初始化, Fpwm=72M/256=281.25Khz.ARR= 255,所以分辨率是8位
TIM_SetCompare1(TIM1,100);//初始值为0,设置比较器CCR的值,达到设置PWM占空比的目的
POINT_COLOR=RED;//设置字体为红色
LCD_ShowString(60,50,200,16,16,"WarShip STM32");
LCD_ShowString(60,70,200,16,16,"PWM DAC TEST");
LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(60,110,200,16,16,"2015/1/15");
LCD_ShowString(60,130,200,16,16,"WK_UP:+ KEY1:-");
//显示提示信息
POINT_COLOR=BLUE;//设置字体为蓝色
LCD_ShowString(60,150,200,16,16,"PWM VAL:");
LCD_ShowString(60,170,200,16,16,"DAC VOL:0.000V");
LCD_ShowString(60,190,200,16,16,"ADC VOL:0.000V");
TIM_SetCompare1(TIM1,pwmval);//初始值
while(1)
{
t++;
key=KEY_Scan(0);
if(key==WKUP_PRES)
{
if(pwmval<250)pwmval+=10;
TIM_SetCompare1(TIM1,pwmval); //输出
}else if(key==KEY1_PRES)
{
if(pwmval>10)pwmval-=10;
else pwmval=0;
TIM_SetCompare1(TIM1,pwmval); //输出
}
if(t==10||key==KEY1_PRES||key==WKUP_PRES) //WKUP/KEY1按下了,或者定时时间到了
{
adcx=TIM_GetCapture1(TIM1); //读回设置的CCR的值
LCD_ShowxNum(124,150,adcx,4,16,0); //显示DAC寄存器值
temp=(float)adcx*(3.3/256); //得到DAC电压值
adcx=temp;
LCD_ShowxNum(124,170,temp,1,16,0); //显示电压值整数部分
temp-=adcx;
temp*=1000;
LCD_ShowxNum(140,170,temp,3,16,0x80); //显示电压值的小数部分
adcx=Get_Adc_Average(ADC_Channel_1,20); //得到ADC转换值,用ADC去测量通道1的值
temp=(float)adcx*(3.3/4096); //得到ADC电压值
adcx=temp;
LCD_ShowxNum(124,190,temp,1,16,0); //显示电压值整数部分
temp-=adcx;
temp*=1000;
LCD_ShowxNum(140,190,temp,3,16,0x80); //显示电压值的小数部分
t=0;
LED0=!LED0;
}
delay_ms(10);
}
}
这里V2版和V3版有所不同,V2版用的是定时器4的通道1输出PWM,所以程序不同。
上一篇:54。I2C通信实验
下一篇:52. STM32的DAC实验
推荐阅读最新更新时间:2024-03-16 16:16
设计资源 培训 开发板 精华推荐
- 非常见问题解答第223期:如何在没有软启动方程的情况下测量和确定软启动时序?
- 兆易创新GD25/55全系列车规级SPI NOR Flash荣获ISO 26262 ASIL D功能安全认证证书
- 新型IsoVu™ 隔离电流探头:为电流测量带来全新维度
- 英飞凌推出简化电机控制开发的ModusToolbox™电机套件
- 意法半导体IO-Link执行器电路板为工业监控和设备厂商带来一站式参考设计
- Melexis采用无磁芯技术缩小电流感测装置尺寸
- 千丘智能侍淳博:用数字疗法,点亮“孤独症”儿童的光
- 数药智能冯尚:ADHD数字疗法正为儿童“多动症”提供更有效便捷服务
- Vicor高性能电源模块助力低空航空电子设备和 EVTOL的发展
- 创实技术electronica 2024首秀:加速国内分销商海外拓展之路