03、SG90 舵机驱动实验
一、原理
SG90舵机是一种常见的旋转电机模块。它能转动的角度主要有三种类型:0到90度的小范围转动,0到180度的半圆转动,以及可以完整旋转360度的全周模式。这种电机常用于需要精准控制转动的场景,例如:
- 智能垃圾桶的自动开盖
- 让智能小车实现灵活转向
- 摄像头支架的多角度调整
- 机械臂的关节运动控制
它的设计简单实用,特别适合需要精确控制角度的自动化项目。
SG90舵机通常通过PWM信号控制角度。它的工作原理很简单:
- 信号周期:每20毫秒(0.02秒)发送一次控制信号。
- 高电平时间:在这20毫秒里,给舵机发送高电平的时长决定了转动角度。
- 占空比公式:占空比 = 高电平时间 ÷ 20毫秒
比如:
- 当高电平持续1毫秒时,舵机转到0度;
- 高电平1.5毫秒时转到中间位置90度;
- 2毫秒时则转到180度。
简单来说,就是通过调整每次信号中高电平的持续时间(1-2毫秒之间),就能精准控制舵机转动的角度。
G90舵机角度控制说明(信号周期固定为20ms):
脉冲宽度对应角度关系:
- 0.5ms → 0°
- 1.0ms → 45°
- 1.5ms → 90°
- 2.0ms → 135°
- 2.5ms → 180°
::: (通过调整高电平信号的持续时间,可在20ms周期内控制舵机转向不同角度) :::
二、硬件连接
SG90舵机的三根线功能如下:
- 棕线:连接电源地(GND);
- 红线:连接电源(需4.8V到7.2V的电压);
- 黄线:接收PWM控制信号(用于调整舵机角度)。
以下是更直白的改写版本:
三、实验代码
3.1 检查GPIO占用
目的:确保你要用的GPIO引脚没有被其他功能占用
操作:
- 查看芯片手册,确认目标GPIO的复用功能是否已关闭
- 检查设备树配置,确保该GPIO未被设置为其他设备的信号引脚
3.2 配置内核驱动
步骤1:修改 Makefile
在 kernel/drivers/pwm/
文件夹的 Makefile
文件中,添加一行:
C
obj-$(CONFIG_PWM_SG90) += pwm-SG90.o
1
(这行代码的作用是告诉内核需要编译这个驱动模块)
步骤2:添加配置选项
在同一个文件夹的 Kconfig
文件中,添加:
C
config PWM_SG90
tristate "SG90舵机驱动"
help
启用对SG90伺服电机PWM驱动的支持
1
2
3
4
2
3
4
步骤3:启用驱动支持
在内核配置文件 configs/lubancat2_defconfig
中,添加:
C
CONFIG_PWM_SG90=y
1
:::
总结操作流程
检查并确认GPIO可用性
在驱动目录修改两个配置文件(Makefile 和 Kconfig)
在设备配置文件中启用该驱动选项 :::
这样修改后,内核编译时就会包含对SG90伺服电机的PWM控制支持。
3.3、修改设备树
C
/ {
......
hc_sg90 {
compatible = "hc-sg90";
pwms = <&pwm12 0 20000000 0>;
status = "okay";
};
};
&pwm12 {
pinctrl-names = "active";
pinctrl-0 = <&pwm12m1_pins>;
status = "okay";
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
3.4、驱动
C
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/timekeeping.h>
#include <linux/wait.h>
#include <linux/irqflags.h>
#include <linux/pwm.h>
static int major;
static struct class *class;
static struct pwm_device *pwm_test;
static int sg90_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
printk("sg90 match success \n");
if (node){
/* 从子节点中获取PWM设备 ___ */
pwm_test = devm_of_pwm_get(&pdev->dev, node, NULL);
if (IS_ERR(pwm_test)){
printk(KERN_ERR" pwm_test,get pwm error!!\n");
return -1;
}
}
else{
printk(KERN_ERR" pwm_test of_get_next_child error!!\n");
return -1;
}
pwm_config(pwm_test, 1500000, 20000000); /* 配置PWM:1.5ms,90度,周期:20000000ns = 20ms */
pwm_set_polarity(pwm_test, PWM_POLARITY_NORMAL); /* 设置输出极性:占空比为高电平 */
pwm_enable(pwm_test); /* 使能PWM输出 */
return 0;
}
static int sg90_remove(struct platform_device *dev)
{
pwm_config(pwm_test, 500000, 20000000); /* 配置PWM:0.5ms,0度 */
pwm_free(pwm_test);
return 0;
}
static const struct of_device_id sg90_of_match[] = {
{ .compatible = "hc-sg90" },
{ }
};
static struct platform_driver sg90_platform_driver = {
.driver = {
.name = "my_sg90",
.of_match_table = sg90_of_match,
},
.probe = sg90_probe,
.remove = sg90_remove,
};
static int sg90_open (struct inode *node, struct file *filp)
{
return 0;
}
//0.5ms————0度;
//1.0ms————45度;
//1.5ms————90度;
//2.0ms————135度;
//2.5ms————180度;
//data[0] --- > 500000
static ssize_t sg90_write (struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
int res;
unsigned char data[1];
if(size != 1)
return 1;
res = copy_from_user(data, buf, size);
/* 配置PWM:旋转任意角度(单位1度) */
pwm_config(pwm_test, data[0], 20000000);
return 1;
}
static int sg90_release (struct inode *node, struct file *filp)
{
return 0;
}
static struct file_operations sg90_ops = {
.owner = THIS_MODULE,
.open = sg90_open,
.write = sg90_write,
.release = sg90_release,
};
static int sg90_init(void)
{
major = register_chrdev(0 , "sg90", &sg90_ops);
class = class_create(THIS_MODULE, "sg90_class");
device_create(class, NULL, MKDEV(major, 0), NULL, "sg90");
platform_driver_register(&sg90_platform_driver);
return 0;
}
static void sg90_exit(void)
{
platform_driver_unregister(&sg90_platform_driver);
device_destroy(class, MKDEV(major, 0));
class_destroy(class);
unregister_chrdev(major, "sg90");
}
module_init(sg90_init);
module_exit(sg90_exit);
MODULE_LICENSE("GPL");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
=