STM32的PWN和DAC的实践认知

STM32的PWM和DAC的实践认知



任务摘要

阅读学习野火开发板资料(零死角玩转stm32-中级篇、零死角玩转 STM32F103—指南者)和网上资源,熟悉 脉冲宽度调制(PWM)和数模/模数转换原理。完成以下实验:

  1. 用STM32F103输出一路PWM波形,建议采用定时器方法。用示波器观察输出波形。
  2. 用STM32F103的DAC功能完成以下波形输出,用示波器观察波形,并用蜂鸣器或手机耳机收听输出声音效果、感受歌曲的音质差异。
    1)输出一个周期2khz的正弦波(循环)。此波形驱动作用至蜂鸣器或喇叭,会呈现一个“滴…”的单音;
    2)将一段数字音频歌曲数据转换为模拟音频波形输出(循环)。

一、PWM简介

PWM是Pulse Width Modulation的缩写,中文意思就是脉冲宽度调 制,简称脉宽调制。它是利用微处理器的数字输出来对模拟电路进行控 制的一种非常有效的技术,其控制简单、灵活和动态响应好等优点而成 为电力电子技术最广泛应用的控制方式,其应用领域包括测量,通信, 功率控制与变换,电动机控制、伺服控制、调光、开关电源,甚至某些 音频放大器,因此学习PWM具有十分重要的现实意义。
在这里插入图片描述
STM32F1 PWM介绍
 STM32F1除了基本定时器TIM6和TIM7,其他定时器都可以产生PWM输出 。其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出 。而通用定时器也能同时产生多达 4路的 PWM 输出,这些在定时器中断 章节中已经介绍过。 PWM的输出其实就是对外输出脉宽可调(即占空比调节)的方波信号 ,信号频率是由自动重装寄存器 ARR 的值决定,占空比由比较寄存器 CCR 的值决定。
 在这里插入图片描述
PWM输出比较模式总共有8种,具体由寄存器 CCMRx 的位 OCxM[2:0] 配置。我们这里只讲解最常用的两种PWM输出模式:PWM1和PWM2,
PWM1和PWM2这两种模式用法差不多,区别之处就是输出电平的极性不 同。
在这里插入图片描述
PWM边沿对齐模式
当 TIMx_CR1 寄存器中的 DIR 位为低时执行递增计数,计数器CNT从 0 计数到自动重载值(TIMx_ARR 寄存器的内容),然后重新从 0 开始 计数并生成计数器上溢事件。 以 PWM 模式 1 为例。只要TIMx_CNT < TIMx_CCRx, PWM 参考信号 OCxREF 便为有效的高电平,否则为无效的低电平。如果 TIMx_CCRx 中 的比较值小于自动重载值(TIMx_ARR 中),则 OCxREF 保持为“ 1”。 如果比较值为 0, 则 OCxREF 保持为“ 0”。
在这里插入图片描述
PWM中心对齐模式
在中心对齐模式下,计数器 CNT 是工作做递增/递减模式下。开始的 时候, 计数器CNT 从 0 开始计数到自动重载值减 1(ARR-1),生成计数 器上溢事件;然后从自动重载值开始向下计数到 1 并生成计数器下溢事 件。之后从 0 开始重新计数。
在这里插入图片描述
PWM输出配置步骤
(1)使能定时器及端口时钟,并设置引脚复用器映射 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
在这里插入图片描述

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

GPIO_PinRemapConfig(GPIO_FullRemap_TIM3,ENABLE); 可选的参数在 stm32f10x_gpio.h 都已经列出来非常详细

GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出

在这里插入图片描述
(2)初始化定时器参数,包含自动重装值,分频系数,计数方式等

void TIM_TimeBaseInit(TIM_TypeDef*TIMx,TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);

在这里插入图片描述

二、STM32F103输出PWM波形图

1、文档查找

在野火官方资料库中,找到“TIM—通用定时器-4路PWM输出”文件夹,
在这里插入图片描述
在这里插入图片描述
打开keil project
在这里插入图片描述
对打开的工程进行编译,成功通过且无报错
在这里插入图片描述

2、代码程序

main.c函数

// TIM¡ªÍ¨Óö¨Ê±Æ÷-4·PWMÊä³öÓ¦ÓÃ
#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_GeneralTim.h"  

/**
  * @brief  Ö÷º¯Êý
  * @param  ÎÞ  
  * @retval ÎÞ
  */
int main(void)
{
    
    
	/* led ¶Ë¿ÚÅäÖà */ 
	LED_GPIO_Config();
	
	/* ¶¨Ê±Æ÷³õʼ»¯ */
	GENERAL_TIM_Init();
	
  while(1)
  {
    
          
  }
}

主要代码

void TIM3_Int_Init(u16 arr,u16 psc)
{
    
    
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能

	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	 计数到5000为500ms
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  10Khz的计数频率  
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
 
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断

	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

	TIM_Cmd(TIM3, ENABLE);  //使能TIMx外设
							 
}
//定时器3中断服务程序
void TIM3_IRQHandler(void)   //TIM3中断
{
    
    
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 
		{
    
    
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx的中断待处理位:TIM 中断源 
		LED1=!LED1;
		}
}
//TIM3 PWM部分初始化 
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM3_PWM_Init(u16 arr,u16 psc)
{
    
      
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);	//使能定时器3时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟
	
	GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射  TIM3_CH2->PB5    
 
   //设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形	GPIOB.5
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
 
   //初始化TIM3
	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
	
	//初始化TIM3 Channel2 PWM模式	 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
	TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC2

	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR2上的预装载寄存器
 
	TIM_Cmd(TIM3, ENABLE);  //使能TIM3
	

}

3、环境配置

相关配置
魔术棒–Debug–配置如下(选中Use Simulator
在这里插入图片描述按照图示进行操作
在这里插入图片描述
在这里插入图片描述

4、结果展示

效果显示
在这里插入图片描述
示波器显示:
在这里插入图片描述

三、DAC简介

1、认识DAC

DAC(中文:数字模拟转换器)是一种将数字信号转换为模拟信号(以电流、电压或电荷的形式)的设备。电脑对声音这种信号不能直接处理,先把它转化成电脑能识别的数字信号,就要用到声卡中的DAC,它把声音信号转换成数字信号,要分两步进行,即采样和转换。(更多认识可参考资料什么是DAC_adc是什么意思

adc: Analog-to-Digital Converter的缩写,意思是模/数转换器。实现把模拟信号转变为数字量的设备称为模数(A/D)转换器,简称ADC 实现把把数字量转变为模拟量的设备称为数模(D/A)转换器,

在这里插入图片描述

2、2khz的正弦波输出

计算公式
在这里插入图片描述
先对正弦波进行采样,取出一定的点放到数组中,再去执行输出代码。这里采用Matlab进行采样点的获取。
在这里插入图片描述

文档有两个脚本
在这里插入图片描述

用MATLAB打开如下(信号系统实验时已经安装MATLAB,这里不说安装步骤,大家可以搜索相关教程
在这里插入图片描述
运行如图
在这里插入图片描述
对首行份数进行修改如下
在这里插入图片描述
保存后再次运行如图
在这里插入图片描述
我们可以对比采样份数不同,正弦图像变化。
修改后代码

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

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

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,'.')                     %把这些点画出来 

查看样点
在这里插入图片描述
自动保存到以下c文件
在这里插入图片描述
打开此文件,然后复制
在这里插入图片描述
打开 DAC-输出正弦波 的工程keil文件
在这里插入图片描述
将复制的全部粘贴到 uint16_t Sine12bit数组中
在这里插入图片描述
编译main.c函数生成hex文件
在这里插入图片描述

3、输出波形

示波器波形
在这里插入图片描述

4、蜂鸣器

将板子上的PA4或PA5接上蜂鸣器,并接地,便可让蜂鸣器发出声音
在这里插入图片描述

四、数字音频数据转换为模拟音频波形输出

方法介绍

首先用音频制作工具如audition制作一段数字化的2khz正弦波wav文件,制作时须指定采样频率、量化位数和通道数,以及时间长度。MCU资源有限,建议采样8khz,量化16bit,单通道,时长仅仅2秒左右。音频wav数据可以用类似汉字字模的保存方式,直接copy到Keil代码中数组中,不必使用SD卡上的wav文件(野火开发板是读取SD卡上的wav文件)。

1、软件下载

此次试验需要下载三个软件进行,一是 Adobe Audition CS6,二是notepad++,三是Uedit32

为大家附上下载链接;
Adobe Audition CS6 含汉化补丁+破解补丁.
网站内也附有详细的安装教程,供大家参考
notepad++.
uedit32中文破解版 v25.0.0.53绿色版.
可参考网页进行相关操作。
在这里插入图片描述
将文件中所有的压缩包解压到当前文件目录下,然后按照网址所提示的安装教程进行安装。

2.软件使用

1.Adobe Audition CS6

打开Adobe Audition CS6,找到下载歌曲路径,点击打开
在这里插入图片描述
音频显示
在这里插入图片描述
鼠标拖动截取片段,点击存储
在这里插入图片描述
下拉设置格式,其采样类型格式设置都会改变,点击确定
在这里插入图片描述

2.Uedit32

将保存的文件用Uedit32打开
在这里插入图片描述
在这里插入图片描述
打开后界面显示如下
在这里插入图片描述
选择复制(16进制复制,快捷键Ctrl+A
在这里插入图片描述
新建文件,然后粘贴复制内容到新文件
在这里插入图片描述
CTRL+A 鼠标右键 选择范围 输入起始的行号和列号,选中内容
在这里插入图片描述
在这里插入图片描述
复制选中的内容
在这里插入图片描述

3.notepad++

notepad++ 软件,将以上复制的内容粘贴进去
在这里插入图片描述
删除不需要的尾行,将该文本复制下来。
打开 38-DAC—输出正弦波 工程文件
在这里插入图片描述
将文本复制进uint16_t Sine12bit[]数组中去,并删掉中括号里面的数据
在这里插入图片描述
编译main函数
在这里插入图片描述

3.结果显示

程序烧录,结果显示
在这里插入图片描述


五、总结

对STM32的PWM和DAC的输出波形有了一定的了解,也明白了如何使用单片机进行波形输出。内容写得较为详细,希望对你有所帮助。

六、参考资料

stm32f103学习总结)—stm32 PMW输出实验.
STM32的PWM和DAC的练习.
使用STM32控制无源蜂鸣器发声播放音乐(STM32_07).
STM32的PWM和DAC练习.
什么是DAC_adc是什么意思.

猜你喜欢

转载自blog.csdn.net/QWERTYzxw/article/details/112194949