STM32控制PCA9685产生16路PWM波控制SG90舵机
如果你能点开这篇文章,说明你已经知道PCA9685是多么强大,NXP公司原本做这片芯片是为了提供给LED使用,在其官方文档里也能看到所有PWM输出都是写着LED,但是PWM波形不仅仅能够控制一个简单的LED,PWM应用很广泛的一个方面就是电机调速,以及部分舵机控制角度就是通过调节PWM波的占空比来实现,因此本篇博客就来介绍一下这块如何使用这块芯片。
博主采用的是下面这款模块,淘宝上很容易买到。
这款芯片通过IIC总线来控制,如果你想很快上手,可以参考博主的另一篇文章通用软件模拟IIC,只需要改改你要用的引脚就可以了。
闲话少说,上代码
芯片使用可以参考这篇文章,我这里只讲怎么用
#ifndef _PCA9685_H
#define _PCA9685_H
#include "sys.h"
#include "delay.h"
#include "iic.h"
#define pca_adrr 0x80
#define pca_mode1 0x00
#define pca_pre 0xFE
#define LED0_ON_L 0x06
#define LED0_ON_H 0x07
#define LED0_OFF_L 0x08
#define LED0_OFF_H 0x09
void pca_write(u8 adrr,u8 data);
u8 pca_read(u8 adrr);
void PCA9685_Init(float hz,u8 angle);
void pca_setfreq(float freq);
void pca_setpwm(u8 num, u32 on, u32 off);
void PCA_Set(u8 num,u8 start_angle,u8 end_angle,u8 mode,u8 speed);
#endif
下面是具体实现的代码
#include "pca9685.h"
#include "iic.h"
#include "delay.h"
#include "math.h"
void pca_write(u8 adrr,u8 data)
{
IIC_Start();
IIC_Write_One_Byte(pca_adrr);
IIC_Wait_Ack();
IIC_Write_One_Byte(adrr);
IIC_Wait_Ack();
IIC_Write_One_Byte(data);
IIC_Wait_Ack();
IIC_Stop();
}
u8 pca_read(u8 adrr)
{
u8 data;
IIC_Start();
IIC_Write_One_Byte(pca_adrr);
IIC_Wait_Ack();
IIC_Write_One_Byte(adrr);
IIC_Wait_Ack();
IIC_Start();
IIC_Write_One_Byte(pca_adrr|0x01);
IIC_Wait_Ack();
data=IIC_Read_One_Byte(0);
IIC_Stop();
return data;
}
void pca_setfreq(float freq)
{
u8 prescale,oldmode,newmode;
double prescaleval;
freq *= 0.915;
prescaleval = 25000000;
prescaleval /= 4096;
prescaleval /= freq;
prescaleval -= 1;
prescale =floor(prescaleval + 0.5f);
oldmode = pca_read(pca_mode1);
newmode = (oldmode&0x7F) | 0x10;
pca_write(pca_mode1, newmode);
pca_write(pca_pre, prescale);
pca_write(pca_mode1, oldmode);
delay_ms(2);
pca_write(pca_mode1, oldmode | 0xa1);
}
void pca_setpwm(u8 num, u32 on, u32 off)
{
pca_write(LED0_ON_L+4*num,on);
pca_write(LED0_ON_H+4*num,on>>8);
pca_write(LED0_OFF_L+4*num,off);
pca_write(LED0_OFF_H+4*num,off>>8);
}
void PCA9685_Init(float hz,u8 angle)
{
u32 off=0;
u8 i=0;
IIC_Init();
pca_write(pca_mode1,0x0);
pca_setfreq(hz);
off=(u32)(102+angle*2.2);
for(i=0;i<=15;i++)
{
pca_setpwm(i,0,off);
}
delay_ms(500);
}
void PCA_Set(u8 num,u8 start_angle,u8 end_angle,u8 mode,u8 speed)
{
u8 i;
u32 off=0;
switch(mode)
{
case 0:
{
off=(u32)(102+end_angle*2.2);
pca_setpwm(num,0,off);
}break;
case 1:
{
off=(u32)(158+end_angle*2.2);
pca_setpwm(num,0,off);
if(end_angle>start_angle)
{
delay_ms((u16)((end_angle-start_angle)*2.7));
}
else
{
delay_ms((u16)((start_angle-end_angle)*2.7));
}
}break;
case 2:
{
if(end_angle>start_angle)
{
for(i=start_angle;i<=end_angle;i++)
{
off=(u32)(158+i*2.2);
pca_setpwm(num,0,off);
delay_ms(2);
delay_us(speed*250);
}
}
else if(start_angle>end_angle)
{
for(i=start_angle;i>=end_angle;i--)
{
off=(u32)(158+i*2.2);
pca_setpwm(num,0,off);
delay_ms(2);
delay_us(speed*250);
}
}
}break;
}
}
代码中使用到的delay和sys,以及iic可以通过另外一篇文章页尾下载,这里就不放链接了。
经测试可以看到舵机的输出角度比较稳定。
这里还是简单说一下舵机SG90吧,这是一款90g舵机,通过周期为50Hz的PWM波控制,也就是20ms,其中0.5ms高电平时舵机输出0度,1ms输出45度,1.5ms输出90度,2ms输出135度,2.5ms输出180度,在代码中通过off=(u32)(158+i*2.2);
来将其转变成具体要写到芯片通道寄存器的值,你可以通过调整这个计算式来调整精度,不过经博主个人测试,输出精度已经足够,能够满足一般使用了。
觉得有用不妨点个赞吧,欢迎点击这里访问博主个人网站,一起交流学习
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)