目录
一、前言
实现精确控制是许多物联网和自动化项目的核心需求,掌握PWM(脉冲宽度调制)技术尤为重要,在许多精细化控制的场景中,往往都是通过PWM实现的。
那么本文选择了一个示例,想要通过PWM驱动SG90舵机,SG90舵机广泛应用于机器人、无人机、云台及自动化设备等领域,其控制依赖于精确的PWM信号。
二、设备选型
本文选用的开发板芯片为ST17H66,本文中驱动的舵机为SG90(产生驱动PWM信号即可)180°,那么其驱动信号为50HZ的PWM信号,高电平占比分别为2.5%、7.5%、12.5%时,分别可以驱动舵机旋转-90°、0°、90°。
三、功能实现
3.1 PWM分析
50HZ的驱动信号,说明其周期为20ms,我们以让其保持0°来说(本文中的角度并非实际角度,只代表与其他角度的相对角度,用来表明舵机的三个位置),那么我们需要产生1.5ms的高电平和18.5ms的低电平,然后持续该波形。
3.2 查阅技术手册
为了探究ST17H66的PWM的实现方法,需要查阅技术手册,我们打开认识SDK文件。
开发手册:【免费】伦茨ST17H66开发资源包_st17h66资源-CSDN文库
具体我记得这个文件应该是唯一一个提到PWM功能实现的函数。
3.3 实现代码
3.3.1 摘抄例程
首先,摘抄出文档中给出的代码。
hal_pwrmgr_register(MOD_USR8, NULL, NULL);// 开机初始化注册睡眠功耗控制变
量,睡眠会关闭pwm模块
hal_pwrmgr_lock(MOD_USR8);// 开启pwm之前需要设备不能进入睡眠,否则pwm失效
hal_pwm_init(PWM_CH0, PWM_CLK_DIV_4, PWM_CNT_UP, PWM_POLARITY_FALLING);
hal_pwm_open_channel(PWM_CH0, USR_IO_BUZZER);
hal_pwm_set_count_val(PWM_CH0, (((16000000/(1<<PWM_CLK_DIV_4))/(freq))*(duty)/100),\((16000000/(1<<PWM_CLK_DIV_4))/(freq)));
hal_pwm_start();
3.3.2 分析参数
看到程序之后是不一头雾水?下面我们一起来分析程序。
hal_pwm_init(PWM_CH0, PWM_CLK_DIV_4, PWM_CNT_UP, PWM_POLARITY_FALLING);
首先看hal_pwm_init函数,第一个参数很明显是PWM通道,此处我们可以不改变,使用CH0即可。
剩下的三个参数分别为分频系数、计数模式、极性模式。其中分频系数和计数模式想必大家都比较了解,第三个极性我们选择PWM_POLARITY_FALLING,这将使PWM波的第二个阶段是低电平。
我们分频系数取决于我们系统的是时钟频率,在main.c中,本工程的时钟频率为16MHZ,因此我选择了16作为分频系数,这样可以得到1MHZ的频率,即每个时钟周期为1us。这样也方便产生20ms的周期波形。
计数模式选择上升计数。
hal_pwm_open_channel(PWM_CH0, USR_IO_BUZZER);
然后我们来看hal_pwm_open_channel函数,两个参数,根据函数名可以推断这是打开通道的函数,那么第一个参数就是通道,我们写前面初始化的通道,第二个参数,例程中没有提到,但是经过我的试验,这是IO口,所以写IO口即可,这里我选择P34。
hal_pwm_set_count_val(PWM_CH0, (((16000000/(1<<PWM_CLK_DIV_4))/(freq))*(duty)/100),\((16000000/(1<<PWM_CLK_DIV_4))/(freq)));
最后再来看hal_pwm_set_count_val函数,看到这么多的运算是不是感觉很难,其实不然,我们不要看他的参数写的多多,我们去找到这个函数的定义。
/**************************************************************************************
@fn hal_pwm_set_count_val
@brief This function process for change pwm count value
input parameters
@param PWMN_e pwmN : pwm channel
uint16_t cmpVal : the compare value of PWM channel
uint16_t cntTopVal : the counter top value of PWM channel
output parameters
@param None.
@return None.
**************************************************************************************/
void hal_pwm_set_count_val(PWMN_e pwmN, uint16_t cmpVal, uint16_t cntTopVal)
{
if(cmpVal > cntTopVal)
return;
PWM_NO_LOAD_CH(pwmN);
PWM_SET_CMP_VAL(pwmN, cmpVal);
PWM_SET_TOP_VAL(pwmN, cntTopVal);
PWM_LOAD_CH(pwmN);
}
一切豁然开朗,不过就是我们常说的比较值和重装载值而已。之前我们通过分频,已经知道其产生的1us的周期,那么我们20ms一个周期就可以写重装载值为20*1000us。比较值1.5ms我们可以写1.5*1000us。
3.3.3 可用代码
我们将代码按需修改后如下:
hal_pwrmgr_register(MOD_USR8, NULL, NULL);
hal_pwrmgr_lock(MOD_USR8);// 开启pwm之前需要设备不能进入睡眠,否则pwm失效
hal_pwm_init(PWM_CH0, PWM_CLK_DIV_16, PWM_CNT_UP, PWM_POLARITY_FALLING);//16分频 上升模式 低电平
hal_pwm_open_channel(PWM_CH0, P34);
hal_pwm_set_count_val(PWM_CH0,1500,20000);//1us--20ms
hal_pwm_start();
我们将其代码,放到我们的任务初始化函数中,关闭睡眠模式,工程引入pwm.c文件,使用时导入pwm.h文件。
四、实现效果
烧写程序后,本文代码经我使用示波器查看后,波形正确。(忘记拍照了哈哈)