历史上的今天
今天是:2024年10月17日(星期四)
2018年10月17日 | 53.PWM DAC实验
2018-10-17 来源:eefocus
一。 PWM DAC的原理

N= ARR-1
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实验
史海拾趣
|
如果大家都同意网络摄影机是未来的趋势,你可以注意到有许多的厂商已经进入并且持续投入这个充满吸引力的市场。有些是从类比摄影机转换过来的,寻找新的改革方式来保留市场占有率。有些则是这个新技术的新手。这表示会有越来越多的选择伴随着让人困 ...… 查看全部问答> |
|
大家好,我的笔记本昨天关机之后再重新启动就黑屏了,出现bos的命令,不知道这个是怎么回事,最近机子温度挺高的,不过要是不玩游戏就正常温度,昨天下载了一个驱动人生安装上了,然后下载了一个360安装上了,之后重启就不行了,安全模式都进不去了 ...… 查看全部问答> |
|
CPLD1270开发板 一、开发板简介: 本开发板主芯片采用Altera公司的MAXII系列芯片EPM1270T144C。Altera公司推出的MAX? II器件系列,是迄今成本最低的CPLD。MAX II器件采用了全新的CPLD体系结构,在所有CPLD系列中单位I/O成本最低, ...… 查看全部问答> |
|
最近刚接触USB驱动,对USB驱动编写,是不是一定要按照枚举的过程,可不可能有省略的? 看了一些资料,都是一些理论的,可否帮忙提供关于USB枚举的简单代码。不要介绍标准设备请求的那种~~ 谢谢各位了~~~… 查看全部问答> |
|
用platform builder定制OS后, 可以下载到自己的PDA上吗 还是PDA的OS已经烧死不能再改了, 那一般所说的win ce OS 开发是怎么加入到已有的OS中的呢, 比方说, 能添加到我现有的PDA OS(windows mobile 5.0)中吗?… 查看全部问答> |




