Tcar:智能车之基于mg995型号舵机的驱动控制模块

2023-12-27 00:38

本文主要是介绍Tcar:智能车之基于mg995型号舵机的驱动控制模块,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

3、舵机模块 mg995
直流电机 + 控制电路
3.1 驱动程序
  mg995有三条线:
  电源线
  GND线
  控制线

  要想控制舵机,就是操作 控制线,只需要控制这一根。
  控制线连接到CPU特定的管脚上去
  
  ' 编程控制舵机,就变成了编程控制对应的CPU管脚'
  
控制原理:
  1.控制线接收周期性的方波脉冲信号
  2.一个周期20ms
  3.每个周期中高电平维持的时间(占空比)不同,
  4.舵机转动到的角度不同

驱动程序架构:
1) delay方式延时会造成系统的' 卡顿'
2) 可以考虑定时器的方式
 struct timer_list
  .expires = jiffies + HZ //延时1s
  当时s5pv210中HZ=100
tick = 1/100 = 10ms
想延时19/1 ms
  18/2 ms 
  S5P6818中HZ=1000
tick = 1/1000 = 1ms
延时19.5/0.5ms
18.5/1.5ms
  不管HZ=100或者1000都' 满足不了对舵机的控制精度要求'
  
3) 可以考虑内核中的 hrtimer, 属于一个 高精度的定时器,以 ns为单位
   hrtimer是2.6及以后的内核中出现的,其实现使用的核心算法是基于红黑树
   timer_list使用的时间轮算法
   struct hrtimer {
 ktime_t _expires;//超时时间
 //定时时间到对应的处理函数
 enum hrtimer_restart (*function)(struct hrtimer *);
...  
};

使用hrtimer步骤:
1. 定义hrtimer结构体
struct hrtimer timer;
2. 初始化定时器
void hrtimer_init(struct hrtimer *timer, clockid_t clock_id, enum  hrtimer_mode mode);
3. 设置定时器处理函数
enum hrtimer_restart (*function)(struct hrtimer *); 
4. 启动定时器
hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum  hrtimer_mode mode);
5. 推后定时器的到期时间
u64 hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t   interval);
6. 取消定时器
int hrtimer_cancel(struct hrtimer *timer);
 
// duoji.zip/mg995.c,舵机驱动程序

3.2 应用程序
   gui client:
4个按钮
点击某个按钮时个服务器发送不同的命令

   开发板上的server中:
接收命令,采用类似于duoji.zip/mg995_test.c的
方式来控制舵机

小车的行进控制如何实现的?         

舵机如何实现?

 1) 通过控制线控制 GPIOD6
 2) 接收周期为20ms ,占空比不同的方波信号
占空比不同,输出转轴转到的角度不同          
 3) 字符设备框架
 4) 主要算法
输出高电平
延时
输出低电平
延时


delay函数不合适 消耗CPU资源
timer_list 也不合适 延时控制精度不够
hr_timer


5) 封装界面  

// tcar_src.tar.gz

/* mg995.c 驱动程序代码演示 */
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/miscdevice.h>
#include <mach/gpio.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>#define DEVICE_NAME "mg995"#define KP_ROW3   S5PV210_GPH3(3)
#define KP_COL3   S5PV210_GPH2(3)#define TCAR_MG9951_PIN  (KP_ROW3)
#define TCAR_MG9952_PIN  (KP_COL3)typedef struct _mg995_gpios {/* TCAR PIN output port */unsigned int gpio_no;const char  *name;unsigned int cfg_val;unsigned int pin_val;
} mg995_gpios_t;mg995_gpios_t gpio_pins[] = {{.gpio_no    = TCAR_MG9951_PIN,.name       = "GPH3",.cfg_val    = S3C_GPIO_SFN(1),.pin_val    = 1,},{.gpio_no    = TCAR_MG9952_PIN,.name       = "GPH2",.cfg_val    = S3C_GPIO_SFN(1),.pin_val    = 1,}
};static void mg995_gpio_init(void)
{int i;int pins_num;int ret;printk(KERN_INFO " %s\n", __FUNCTION__);pins_num = ARRAY_SIZE(gpio_pins);for (i = 0; i < pins_num; i++) {if (gpio_is_valid(gpio_pins[i].gpio_no)) {ret = gpio_request(gpio_pins[i].gpio_no, gpio_pins[i].name);if (ret) {printk(KERN_ERR "failed to get GPIO for mg995\n");return;}s3c_gpio_cfgpin(gpio_pins[i].gpio_no, gpio_pins[i].cfg_val);gpio_direction_output(gpio_pins[i].gpio_no, gpio_pins[i].pin_val);// gpio_free(gpio_pins[i].gpio_no);}}printk(KERN_INFO " %s end\n", __FUNCTION__);return;
}#define PWM_W  (20*1000*1000)
#define SERVO_NUM	2struct servo_data {int counter;int status;ktime_t nanodelay_l;ktime_t nanodelay_h;int pin;struct hrtimer hr_timer;
};static struct servo_data servo_data[SERVO_NUM];enum hrtimer_restart servo_hrtimer_callback( struct hrtimer *timer ){struct servo_data *data=container_of(timer,struct servo_data,hr_timer);int idx=data->pin;if(data->status==1){printk("counter = %d,status = %d,ns = %ld ns\n",data->counter, data->status,ktime_to_ns(data->nanodelay_h));gpio_direction_output(gpio_pins[idx].gpio_no, 1);hrtimer_forward(timer, hrtimer_get_expires(timer), data->nanodelay_h);data->status=0;}else{printk("counter = %d, status = %d,ns = %ld ns\n",data->counter, data->status,ktime_to_ns(data->nanodelay_l));gpio_direction_output(gpio_pins[idx].gpio_no, 0);hrtimer_forward(timer, hrtimer_get_expires(timer), data->nanodelay_l);data->status=1;data->counter--;}if(data->counter==0){return HRTIMER_NORESTART;}else{return HRTIMER_RESTART;}
}

//delay_h: us
static void mg995_cmd(int idx, int delay_h){int nanodelay_h;int nanodelay_l;ktime_t ktime;gpio_direction_output(gpio_pins[idx].gpio_no, 0);if (delay_h < PWM_W) {nanodelay_l = PWM_W - delay_h*1000;nanodelay_h=delay_h*1000;       servo_data[idx].nanodelay_l=ktime_set(0,nanodelay_l);servo_data[idx].nanodelay_h=ktime_set(0,nanodelay_h);servo_data[idx].pin=idx;servo_data[idx].counter=10;servo_data[idx].status=1;servo_data[idx].hr_timer.function = &servo_hrtimer_callback;ktime=ktime_set(0,0);hrtimer_start( &(servo_data[idx].hr_timer), ktime, HRTIMER_MODE_REL );}return;
}static int mg995_ioctl(struct inode *inode, struct file *file, unsigned int cmd_idx, unsigned long val)
{if (cmd_idx < 2 && cmd_idx >= 0) {mg995_cmd(cmd_idx, val);}return 0;
}static int mg995_open(struct inode *inode, struct file *file)
{return 0;
}
static int mg995_close(struct inode *inode, struct file *file)
{return 0;
}static struct file_operations mg995_fops=
{.owner=THIS_MODULE,.ioctl=mg995_ioctl,.open = mg995_open,.release 	=mg995_close,
};static struct miscdevice mg995_miscdev = {.minor		= MISC_DYNAMIC_MINOR,.name		= DEVICE_NAME,.fops		= &mg995_fops,
};static int mg995_init(void)
{int ret;int i;mg995_gpio_init();ret = misc_register(&mg995_miscdev);if(ret < 0) {printk(DEVICE_NAME "can't register major number\n");return ret;}for(i=0;i<SERVO_NUM;i++){hrtimer_init( &(servo_data[i].hr_timer), CLOCK_MONOTONIC, HRTIMER_MODE_REL );}return 0;
}static void mg995_exit(void)
{int pins_num;int i;misc_deregister(&mg995_miscdev);pins_num = ARRAY_SIZE(gpio_pins);for (i = 0; i < pins_num; i++) {if (gpio_is_valid(gpio_pins[i].gpio_no)) {gpio_free(gpio_pins[i].gpio_no);}}for(i=0;i<SERVO_NUM;i++){hrtimer_cancel( &(servo_data[i].hr_timer) );}
} module_init(mg995_init);
module_exit(mg995_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("tarena");
MODULE_DESCRIPTION("MG995 Driver");

/* 测试代码 test.c */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>int main(void)
{int fd;int val = 0;int idx = 0;fd = open("/dev/mg995", O_RDWR);if (fd < 0) {printf("open /dev/mg995 failed.\n");return -1;}while (1) {printf("\nplease input idx (0 ~ 1): ");scanf("%d", &idx);printf("\nplease input high levle value (0 ~ 2500 us) :");scanf("%d", &val);if(ioctl(fd,idx, val) == -1) {printf("set pwm failed.\n");return -1;}}close(fd);return 0;
}

这篇关于Tcar:智能车之基于mg995型号舵机的驱动控制模块的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/541341

相关文章

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

智能交通(二)——Spinger特刊推荐

特刊征稿 01  期刊名称: Autonomous Intelligent Systems  特刊名称: Understanding the Policy Shift  with the Digital Twins in Smart  Transportation and Mobility 截止时间: 开放提交:2024年1月20日 提交截止日

基于 YOLOv5 的积水检测系统:打造高效智能的智慧城市应用

在城市发展中,积水问题日益严重,特别是在大雨过后,积水往往会影响交通甚至威胁人们的安全。通过现代计算机视觉技术,我们能够智能化地检测和识别积水区域,减少潜在危险。本文将介绍如何使用 YOLOv5 和 PyQt5 搭建一个积水检测系统,结合深度学习和直观的图形界面,为用户提供高效的解决方案。 源码地址: PyQt5+YoloV5 实现积水检测系统 预览: 项目背景

【C++学习笔记 20】C++中的智能指针

智能指针的功能 在上一篇笔记提到了在栈和堆上创建变量的区别,使用new关键字创建变量时,需要搭配delete关键字销毁变量。而智能指针的作用就是调用new分配内存时,不必自己去调用delete,甚至不用调用new。 智能指针实际上就是对原始指针的包装。 unique_ptr 最简单的智能指针,是一种作用域指针,意思是当指针超出该作用域时,会自动调用delete。它名为unique的原因是这个

Jenkins构建Maven聚合工程,指定构建子模块

一、设置单独编译构建子模块 配置: 1、Root POM指向父pom.xml 2、Goals and options指定构建模块的参数: mvn -pl project1/project1-son -am clean package 单独构建project1-son项目以及它所依赖的其它项目。 说明: mvn clean package -pl 父级模块名/子模块名 -am参数