嵌入式开发练习(十五)PWM、DAC的使用

嵌入式学习 专栏收录该内容
18 篇文章 2 订阅

如有错误,欢迎指正,谢谢!

一、PWM

1、PWM简介

PWM是 Pulse Width Modulation 的缩写,中文意思就是脉冲宽度调制,简称脉宽调制。它是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,其控制简单、灵活和动态响应好等优点而成 为电力电子技术最广泛应用的控制方式,其应用领域包括测量,通信, 功率控制与变换,电动机控制、伺服控制、调光、开关电源,甚至某些 音频放大器,因此学习PWM具有十分重要的现实意义。其实我们也可以这样理解,PWM是一种对模拟信号电平进行数字编码 的方法。通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信号的电平进行编码。PWM信号仍然是数字的,因为在给定的 任何时刻,满幅值的直流供电要么完全有(ON),要么完全无(OFF)。电压或电流源是以一种通(ON)或断(OFF)的重复脉冲序列被加到模拟负载上去 的。通的时候即是直流供电被加到负载上的时候,断的时候即是供电被 断开的时候。只要带宽足够,任何模拟值都可以使用 PWM 进行编码。

2、stm32的配置

  1. 定时器复用功能引脚初始化
static void ADVANCE_TIM_GPIO_Config(void) 
{
  GPIO_InitTypeDef GPIO_InitStructure;

  // Êä³ö±È½ÏͨµÀ GPIO ³õʼ»¯
	RCC_APB2PeriphClockCmd(ADVANCE_TIM_CH1_GPIO_CLK, ENABLE);
  GPIO_InitStructure.GPIO_Pin =  ADVANCE_TIM_CH1_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(ADVANCE_TIM_CH1_PORT, &GPIO_InitStructure);

  // Êä³ö±È½ÏͨµÀ»¥²¹Í¨µÀ GPIO ³õʼ»¯
	RCC_APB2PeriphClockCmd(ADVANCE_TIM_CH1N_GPIO_CLK, ENABLE);
  GPIO_InitStructure.GPIO_Pin =  ADVANCE_TIM_CH1N_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(ADVANCE_TIM_CH1N_PORT, &GPIO_InitStructure);

  // Êä³ö±È½ÏͨµÀɲ³µÍ¨µÀ GPIO ³õʼ»¯
	RCC_APB2PeriphClockCmd(ADVANCE_TIM_BKIN_GPIO_CLK, ENABLE);
  GPIO_InitStructure.GPIO_Pin =  ADVANCE_TIM_BKIN_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(ADVANCE_TIM_BKIN_PORT, &GPIO_InitStructure);
	// BKINÒý½ÅĬÈÏÏÈÊä³öµÍµçƽ
	GPIO_ResetBits(ADVANCE_TIM_BKIN_PORT,ADVANCE_TIM_BKIN_PIN);	
}
  1. 定时器模式配置
static void ADVANCE_TIM_Mode_Config(void)
{
  // ¿ªÆô¶¨Ê±Æ÷ʱÖÓ,¼´ÄÚ²¿Ê±ÖÓCK_INT=72M
	ADVANCE_TIM_APBxClock_FUN(ADVANCE_TIM_CLK,ENABLE);

/*--------------------ʱ»ù½á¹¹Ìå³õʼ»¯-------------------------*/
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	// ×Ô¶¯ÖØ×°ÔؼĴæÆ÷µÄÖµ£¬ÀÛ¼ÆTIM_Period+1¸öƵÂʺó²úÉúÒ»¸ö¸üлòÕßÖжÏ
	TIM_TimeBaseStructure.TIM_Period=ADVANCE_TIM_PERIOD;	
	// Çý¶¯CNT¼ÆÊýÆ÷µÄʱÖÓ = Fck_int/(psc+1)
	TIM_TimeBaseStructure.TIM_Prescaler= ADVANCE_TIM_PSC;	
	// ʱÖÓ·ÖƵÒò×Ó £¬ÅäÖÃËÀÇøʱ¼äʱÐèÒªÓõ½
	TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;		
	// ¼ÆÊýÆ÷¼ÆÊýģʽ£¬ÉèÖÃΪÏòÉϼÆÊý
	TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;		
	// Öظ´¼ÆÊýÆ÷µÄÖµ£¬Ã»Óõ½²»ÓùÜ
	TIM_TimeBaseStructure.TIM_RepetitionCounter=0;	
	// ³õʼ»¯¶¨Ê±Æ÷
	TIM_TimeBaseInit(ADVANCE_TIM, &TIM_TimeBaseStructure);

	/*--------------------Êä³ö±È½Ï½á¹¹Ìå³õʼ»¯-------------------*/		
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	// ÅäÖÃΪPWMģʽ1
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	// Êä³öʹÄÜ
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	// »¥²¹Êä³öʹÄÜ
	TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; 
	// ÉèÖÃÕ¼¿Õ±È´óС
	TIM_OCInitStructure.TIM_Pulse = ADVANCE_TIM_PULSE;
	// Êä³öͨµÀµçƽ¼«ÐÔÅäÖÃ
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	// »¥²¹Êä³öͨµÀµçƽ¼«ÐÔÅäÖÃ
	TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
	// Êä³öͨµÀ¿ÕÏеçƽ¼«ÐÔÅäÖÃ
	TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
	// »¥²¹Êä³öͨµÀ¿ÕÏеçƽ¼«ÐÔÅäÖÃ
	TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
	TIM_OC1Init(ADVANCE_TIM, &TIM_OCInitStructure);
	TIM_OC1PreloadConfig(ADVANCE_TIM, TIM_OCPreload_Enable);

	/*-------------------ɲ³µºÍËÀÇø½á¹¹Ìå³õʼ»¯-------------------*/
	// ÓйØɲ³µºÍËÀÇø½á¹¹ÌåµÄ³ÉÔ±¾ßÌå¿É²Î¿¼BDTR¼Ä´æÆ÷µÄÃèÊö
	TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
  TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
  TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
  TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;
	// Êä³ö±È½ÏÐźÅËÀÇøʱ¼äÅäÖ㬾ßÌåÈçºÎ¼ÆËã¿É²Î¿¼ BDTR:UTG[7:0]µÄÃèÊö
	// ÕâÀïÅäÖõÄËÀÇøʱ¼äΪ152ns
  TIM_BDTRInitStructure.TIM_DeadTime = 11;
  TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable;
	// µ±BKINÒý½Å¼ì²âµ½¸ßµçƽµÄʱºò£¬Êä³ö±È½ÏÐźű»½ûÖ¹£¬¾ÍºÃÏñÊÇɲ³µÒ»Ñù
  TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;
  TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
  TIM_BDTRConfig(ADVANCE_TIM, &TIM_BDTRInitStructure);
	
	// ʹÄܼÆÊýÆ÷
	TIM_Cmd(ADVANCE_TIM, ENABLE);	
	// Ö÷Êä³öʹÄÜ£¬µ±Ê¹ÓõÄÊÇͨÓö¨Ê±Æ÷ʱ£¬Õâ¾ä²»ÐèÒª
	TIM_CtrlPWMOutputs(ADVANCE_TIM, ENABLE);
}

3、演示效果

在这里插入图片描述

二、DAC

1、DAC简介

DAC 为数字/模拟转换模块,故名思议,它的作用就是把输入的数字编码,转换成对
应的模拟电压输出,它的功能与 ADC 相反。在常见的数字信号系统中,大部分传感器信号被化成电压信号,而 ADC 把电压模拟信号转换成易于计算机存储、处理的数字编码,由计算机处理完成后,再由 DAC 输出电压模拟信号,该电压模拟信号常常用来驱动某些执行器件,使人类易于感知。如音频信号的采集及还原就是这样一个过程。

STM32 具有片上 DAC 外设,它的分辨率可配置为 8 位或 12 位的数字输入信号,具有两个 DAC 输出通道,这两个通道互不影响,每个通道都可以使用 DMA 功能,都具有出错
检测能力,可外部触发。

2、任务1—正弦波输出

野火采用的是通过matlab来进行取样,再存放在数组中完成的。并规定了输出的电压大小,最高为3.3V。

其中的定时器设置、通道设置,此处不做介绍,请参考野火相关资料

matlab采样代码

%用于产生正弦数据表,输出到文件dac_sinWave.c 文件中,复制到c语言数组即可

n = 2*pi/32 : 2*pi/32 : 2*pi      %分成32等份

a = sin(n)+1;                     %求取sin函数值并向上平移一个单位,消除负数值
a = a * 3.3/2;                    %调整幅值,使范围限制为0~3.3   
r = a* (2.^12) /3.3               %求取dac数值,12位dac LSB = 3.3/2.^12 
r = uint16(r);                     %double型数据转化成16位整型数据 

for i = 1:32                        
if r(i) > 4095                      %限制数据最大不超过4095
    r(i) = 4095
end
end 

dlmwrite('dac_sinWave.c',r);      %把数据写入到文件,方便添加到stm32工程中
plot(n,r,'.')                     %把这些点画出来 

将生成的数据进行导出,存储到数组中
在这里插入图片描述就可以有相应的波形了

波形如下

在这里插入图片描述

3、任务2—音频输出

同理,我们先随意选择一段音频,通过如下软件来进行设置
在这里插入图片描述

设置为单通道、8000的采样率,格式为wav
在这里插入图片描述将文件导入到matlab中
在这里插入图片描述
我们就能看到他的数据量,采样率
在这里插入图片描述我们看到波形与先前的波形是一样的,说明导入到matlab中是没有问题的

将程序进行修改

%用于产生正弦数据表,输出到文件dac_sinWave.c 文件中,复制到c语言数组即可


a = data+1;                     %求取sin函数值并向上平移一个单位,消除负数值
a = a * 3.3/2;                    %调整幅值,使范围限制为0~3.3   
r = a* (2.^12) /3.3               %求取dac数值,12位dac LSB = 3.3/2.^12 
r = uint16(r);                     %double型数据转化成16位整型数据 

for i = 1:48000                       
if r(i) > 4095                      %限制数据最大不超过4095
    r(i) = 4095
end
end 

dlmwrite('dac_sinWave.c',r);      %把数据写入到文件,方便添加到stm32工程中
plot(48000,r,'.')                     %把这些点画出来 

可以看出取样后的信号与先前的信号比较相似(并进行了放大)
在这里插入图片描述但很可以我没能输出正确的音频,只有感觉是噪声的东西

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值