一.舵机介绍
1.舵机简介
舵机(英文叫Servo):它由直流电机、减速齿轮组、传感器和控制电路组成的一套自动控制系统。最早的舵机是船舶上的一种大甲板机械。舵机的大小由外舾装按照船级社的规范决定,选型时主要考虑扭矩大小,通过发送信号,指定输出轴旋转角度。随着科技的发展与开发的需要(航模,微型机器人等),小型的舵机逐渐在市场上出现。舵机一般而言都有最大旋转角度(比如180度。)与普通直流电机的区别主要在,直流电机是一圈圈转动的,舵机只能在一定角度内转动,不能一圈圈转(数字舵机可以在舵机模式和电机模式中切换,没有这个问题)。普通直流电机无法反馈转动的角度信息,而舵机可以。用途也不同,普通直流电机一般是整圈转动做动力用,舵机是控制某物体转动一定角度用(比如机器人的关节)。
图1 舵机种类
图2 舵机内部结构
2.舵机控制原理
舵机的伺服系统由可变宽度的脉冲来进行控制,也就是我们常说的PWM信号,控制线是用来传送脉冲的。脉冲的参数有最小值,最大值,和频率。一般而言,舵机的基准信号都是周期为20ms,宽度为1.5ms。这个基准信号定义的位置为中间位置。舵机有最大转动角度,中间位置的定义就是从这个位置到最大角度与最小角度的量完全一样。最重要的一点是,不同舵机的最大转动角度可能不相同,但是其中间位置的脉冲宽度是一定的,那就是1.5ms。如下图:
图3 PWM
图4 PWM与舵机转角示意图
角度是由来自控制线的持续的脉冲所产生。这种控制方法叫做脉冲调制。脉冲的长短决定舵机转动多大角度。例如:1.5毫秒脉冲会到转动到中间位置(对于180°舵机来说,就是90°位置)。当控制系统发出指令,让舵机移动到某一位置,并让他保持这个角度,这时外力的影响不会让他角度产生变化,但是这个是由上限的,上限就是他的最大扭力。除非控制系统不停的发出脉冲稳定舵机的角度,舵机的角度不会一直不变。
当舵机接收到一个小于1.5ms的脉冲,输出轴会以中间位置为标准,逆时针旋转一定角度。接收到的脉冲大于1.5ms情况相反。不同品牌,甚至同一品牌的不同舵机,都会有不同的最大值和最小值。一般而言,最小脉冲为1ms,最大脉冲为2ms。如下:
图5 PWM控制原理
二.PWM介绍
脉冲宽度调制(即PWM)是一种模拟控制方脉冲宽度调制是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中脉冲宽度调制是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。从广义上,PWM跟I2C,SPI,MIPI等接口一样,都属于一种通讯协议。
随着电子技术的发展,出现了多种PWM技术,其中包括:相电压控制PWM、脉宽PWM法、随机PWM、SPWM法、线电压控制PWM等,而在镍氢电池智能充电器中采用的脉宽PWM法,它是把每一脉冲宽度均相等的脉冲列作为PWM波形,通过改变脉冲列的周期可以调频,改变脉冲的宽度或占空比可以调压,采用适当控制方法即可使电压与频率协调变化。可以通过调整PWM的周期、PWM的占空比而达到控制充电电流的目的。
对舵机的控制,我们只需要最简单的脉宽PWM法即可,即通过改变输入舵机的PWM信号的高电平的脉宽实现对舵机角度的控制。
三.linux舵机驱动(伪)
根据舵机的控制原理,我们只要让DragonBoard 410c发出可调制脉宽的PWM信号就行。然后博主翻了翻410c的手册,发现410c没有带PWM硬件模块。。。。。。好吧,作为一名创客,咱只能逢山开路------用GPIO口模拟PWM呗,于是有了下面这个简单的测试驱动。
stree.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DRIVER_NAME "stree"
#define COMPAtiBLE "thundersoft,stree"
#define PERIOD 20000 //PWM频率固定为50HZ(20毫秒一个周期,20000us)
//TBS2701舵机
#ifdef POWER_SWITCH
#define POWER_GPIO
#endif
struct tbs2701_data{
int pwm_enable; //舵机使能标志位
// int poll_time;
int pwm_gpio; //舵机角度控制pin
#ifdef POWER_SWITCH
int power_gpio;//供电控制
#endif
int period;//周期
int high_us;//高电平脉宽,决定舵机转角,限制在0.5ms到2.5ms之间
int angle;//舵机转角
struct mutex data_lock;
wait_queue_head_t data_queue;
//struct work_struct pwm_work;
struct work_struct pwm_work_enable;
bool turn_flag;
//atomic_t counter;/* 一共经历了多少秒?(定义为原子量)*/
struct timer_list s_timer; /*设备要使用的定时器*/
int value_last;
};
struct tbs2701_data* tbs2701;
static void pwm_work_func(struct work_struct* work){
//static void second_timer_handle(unsigned long arg){
//struct timespec now,last;
//struct tbs2701_data* data = container_of(timer,struct tbs2701_data,s_timer);//这种获取方式是否合理
int high_us,low_us;
int period;
period = tbs2701->period;
if(!period||!tbs2701->pwm_enable)
{
printk("we can't get period or pwm_enable is not set");
return;
}
/*high_us = data->high_us;
if(high_us<=500)//限制在0.5ms到2.5ms之间
{
high_us=500;
}
else if(high_us>=2500)
{
high_us=2499;
}
if(!period||!high_us)
{
printk("we can't period or high_ms");
return;
}
low_us = period-high_us;
printk("### high_us = %d, low_us = %d n ", high_us, low_us);*/
printk("timer is enabling~~~!");
//mutex_lock(&tbs2701->data_lock);
if(tbs2701->pwm_enable)
{
high_us = tbs2701->high_us;
low_us = period-high_us;
if(tbs2701->turn_flag==true)
{
//tbs2701->turn_flag=false;
gpio_direction_output(tbs2701->pwm_gpio,1);
//usleep(high_us); //使用sleep的CPU占用率低于1%,但是实测波形极其不稳定,无法满足使用
udelay(high_us); //理论上出来的波形更精准,但是CPU占用率达到25%,太耗资源
//usleep_range(high_us,high_us);
// mod_timer(&tbs2701->s_timer,jiffies + 3*HZ);//重新设置定时周期,1000*HZ=1ms
}
else if(tbs2701->turn_flag==false)
{
tbs2701->turn_flag=true;
gpio_direction_output(tbs2701->pwm_gpio,0);
// usleep(low_us);
udelay(low_us);
//usleep_range(low_us,low_us);
// mod_timer(&tbs2701->s_timer,jiffies + 6*HZ);//重新设置定时周期
}
}
//mutex_unlock(&tbs2701->data_lock);
}
static int parse_dt(struct platform_device* pdev,struct tbs2701_data* data){
//int rc;
struct device_node* node = pdev->dev.of_node;
/*rc = of_property_read_u32(node,"thunder,poll_time",&data->poll_time);
if(rc){
pr_warning("%s you should point time",__FUNCTION__);
data->poll_time = 20;
}*/
data->pwm_gpio = of_get_named_gpio(node,"thundersoft,gpio_pwm",0);
if(data->pwm_gpio<0)
{
pr_err("%s,pwm gpio error gpion",__FUNCTION__);
return -EINVAL;
}
data->period=PERIOD;//周期暂定为20ms
#ifdef POWER_SWITCH
data->power_gpio = of_get_named_gpio(node,"thundersoft,gpio_irq",0);
if(data->power_gpio<0){
pr_err("%s,powr gpio error gpion",__FUNCTION__);
return -EINVAL;
}
#endif
return 0;
}
static ssize_t tbs2701_show_enable(struct device *dev,
struct device_attribute *attr, char *buf){
struct tbs2701_data* data = dev_get_drvdata(dev);
ssize_t lenth = sprintf(buf,"%d",data->pwm_enable);
return lenth;
}
static ssize_t tbs2701_store_enable(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size){
struct tbs2701_data* data = dev_get_drvdata(dev);
int enable = simple_strtoul(buf,NULL,10);
if(enable==data->pwm_enable)
{
printk("do not need to change status");
return size;
}
mutex_lock(&data->data_lock);
if(enable){
//schedule_work(&data->pwm_work);
data->pwm_enable = 1;
add_timer(&data->s_timer);
}
else{
//cancel_work_sync(&data->pwm_work);
data->pwm_enable = 0;
del_timer(&data->s_timer);//关闭PWM时删除定时器
}
mutex_unlock(&data->data_lock);
return size;
}
static ssize_t tbs2701_show_value(struct device *dev,
struct device_attribute* attr,char* buf){
struct tbs2701_data* data = dev_get_drvdata(dev);
ssize_t lenth;
// wait_event_interruptible(data->data_queue,data->data_ready);
// data->data_ready = false;
mutex_lock(&data->data_lock);
// lenth = sprintf(buf,"%d",data->angle);//这里转化为角度再输出
lenth = sprintf(buf,"%d",data->high_us);
printk("%dn",data->high_us);
mutex_unlock(&data->data_lock);
return lenth;
}
static ssize_t tbs2701_store_value(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size){
struct tbs2701_data* data = dev_get_drvdata(dev);
int value = 0;
value = simple_strtoul(buf,NULL,10);
if(value<=500)//限制在0.5ms到2.5ms之间
{
value=501;
}
else if(value>=2500)
{
value=2499;
}
if(data->value_last==value)
{
return size; //与上一次转角相同,无需改变脉宽
}
mutex_lock(&data->data_lock);
if(data->pwm_enable){
// cancel_work_sync(&data->pwm_work);
data->high_us=value;
//mod_timer(&data->s_timer,jiffies + low_us*1000*HZ);
//add_timer(&data->s_timer);
schedule_work(&data->pwm_work);
}
else{
//del_timer(&data->s_timer);//关闭PWM时删除定时器
cancel_work_sync(&data->pwm_work);
}
data->value_last=value;//记录最后改变的值
mutex_unlock(&data->data_lock);
return size;
}
static DEVICE_ATTR(enable,0644,tbs2701_show_enable,tbs2701_store_enable);
static DEVICE_ATTR(value,0644,tbs2701_show_value,tbs2701_store_value);
static int tbs2701_probe(struct platform_device *pdev){
//struct tbs2701_data* data;
int result;
tbs2701 = kmalloc(sizeof(struct tbs2701_data),GFP_KERNEL);
if(!tbs2701){
pr_err("%s kmalloc errorn",__FUNCTION__);
return -ENOMEM;
}
dev_set_drvdata(&pdev->dev,tbs2701);
result = parse_dt(pdev,tbs2701);
if(result<0){
pr_err("%s error when parse dtn",__FUNCTION__);
result = -EINVAL;
goto err_parse_dt;
}
if(gpio_is_valid(tbs2701->pwm_gpio)){
result = gpio_request(tbs2701->pwm_gpio,"pwm_gpio");
if(result<0){
pr_err("Unable to request pwm gpion");
goto err_parse_dt;
}
//gpio_direction_output(data->pwm_gpio,1);
}
#ifdef POWER_SWITCH
if(gpio_is_valid(tbs2701->power_gpio)){
result = gpio_request(tbs2701->power_gpio,"power_gpio");
if(result<0){
pr_err("uanble to request power_gpion");
goto err_parse_dt;
}
//gpio_direction_output(data->power_gpio,1);
}
#endif
INIT_WORK(&data->pwm_work,pwm_work_func);
/*初始化定时器*/
//init_timer(&tbs2701->s_timer);
//tbs2701->s_timer.function = &second_timer_handle;
//tbs2701->s_timer.expires = jiffies + HZ;
mutex_init(&tbs2701->data_lock);
//tbs2701->turn_flag=true;//初始化high
//init_waitqueue_head(&data->data_queue);
result=sysfs_create_file(&pdev->dev.kobj,&dev_attr_enable.attr);
result=sysfs_create_file(&pdev->dev.kobj,&dev_attr_value.attr);
printk("tbs2701_probe sucessn");
return 0;
err_parse_dt:
kfree(tbs2701);
printk("tbs2701_probe failedn");
return result;
}
static int tbs2701_remove(struct platform_device *pdev){
kfree(tbs2701);
return 0;
}
static struct of_device_id tbs2701_match_table[] = {
{ .compatible = COMPATIBLE,},
{ },
};
static struct platform_driver tbs2701_driver = {
.probe = tbs2701_probe,
.remove = tbs2701_remove,
.driver = {
.owner = THIS_MODULE,
.name = DRIVER_NAME,
.of_match_table = tbs2701_match_table,
},
};
module_platform_driver(tbs2701_driver);
MODULE_LICENSE("GPL v2");
这是这个驱动测试出来的PWM波形,波形效果还不错:
图6 实测PWM波形
但是这个驱动有也有很大缺点,具体我们在下一章节再细讲。
|