1、实验目的
使用逻辑派Z1开发板(基于紫光同创 compa 系列 PGC4KD-6ILPG144 芯片)编写SG90 180度舵机驱动代码,最终使用开发板按键控制舵机左右旋转和归位。
2、实验原理
2.1、SG90 180度舵机介绍
SG90舵机是一种位置(角度)伺服的驱动器,适用于那些需要角度不断变化并可以保持的控制系统。在机器人机电控制系统中,舵机控制效果是性能的重要影响因素。舵机可以在微机电系统和航模中作为基本的输出执行机构,因为其简单的控制和输出机制使得其在嵌入式系统中应用十分广泛。
本实验使用的舵机模块如下:
2.2、SG90舵机工作原理介绍
SG90舵机是一种小型舵机,具有三条信号线:GND(接地)、VCC(电源正极、5V)和SIG(控制信号)。它通过接收SIG信号线传入的脉宽调制信号(PWM),解析目标角度信息,并利用内部信号调制芯片将信号转化为直流偏置电压。舵机内部的电位器与输出轴机械连接,实时反馈当前角度并产生对应电压。信号调制芯片通过比较直流偏置电压和电位器反馈电压计算电压差,并将其传递给电机驱动芯片控制电机的转动方向。当电机转动时,电位器同步旋转,逐步减小电压差,直到达到目标角度时停止电机。通过减速齿轮组,电机的高速低扭矩输出被转化为低速高扭矩的精确控制,从而实现舵机的稳定动作和自校正功能。这里只需要大概了解其工作原理即可,我们主要重点关注的是SG90舵机的控制原理。
2.3、SG90舵机控制原理
我们在上文中提到,SG90舵机通过脉宽调制信号(PWM)控制。其需要一个20ms的时基脉冲,高电平持续时间在0.5ms~2.5ms。对于180度舵机而言。不同一个PWM信号的不同高电平持续时间与舵机旋转角度的对应关系如下:
我们使用开发板输出高电平持续时间在0.5ms~2.5ms之间,周期为20ms的PWM信号就能够控制舵机任意旋转0~180度。
3、代码设计
模块端口如下表:
舵机驱动代码如下:
`timescale 1ns / 1ps
module steering_engine(
input clk ,
input rst_n ,
input [ 2: 0] key_in ,
output reg pwm
);
//参数
parameter TIME_20MS = 100_0000;
parameter TIME_3S = 1000;
parameter pwm_0d5ms = 2_5000;
parameter pwm_1ms = 5_0000;
parameter pwm_1d5ms = 7_5000;
parameter pwm_2ms = 10_0000;
parameter pwm_2d5ms = 12_5000;
//内部信号
reg [ 16: 0] pwm_level ;
reg [ 19: 0] cnt_20ms ;
reg [ 19: 0] cnt_3s ;//这里是三秒钟转180度
//20ms计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_20ms <= 0;
else if(cnt_20ms== TIME_20MS - 1)
cnt_20ms <= 0;
else
cnt_20ms <= cnt_20ms + 1;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_3s <= 0;
else if(cnt_3s== TIME_3S - 1)
cnt_3s <= 0;
else
cnt_3s <= cnt_3s + 1;
end
//按键情况列出
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)
pwm_level <= pwm_0d5ms;//默认归位
else if((key_in[0] == 0)&&(cnt_3s== TIME_3S - 1)&&(pwm_level<pwm_2d5ms))
pwm_level <= pwm_level + 1'b1;
else if((key_in[1] == 0)&&(cnt_3s== TIME_3S - 1)&&(pwm_level>pwm_0d5ms))
pwm_level <= pwm_level - 1'b1;
else if(key_in[2] == 0)
pwm_level <= pwm_0d5ms;//归位
else
pwm_level <= pwm_level;
end
//输出pwm
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)
pwm <= 0;
else if(cnt_20ms < pwm_level)
pwm <= 1;
else
pwm <= 0;
end
endmodule
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
模块的核心是如何产生受按键控制的可以调整的pwm信号,我们使用一个20ms计数器cnt_20ms 用来产生20ms的时基脉冲。使用一个 pwm_level信号决定输出的pwm信号高电平持续时间,只有当cnt_20ms的值小于pwm_level,输出的pwm信号才为高电平,当cnt_20ms的值大于pwm_level,输出的pwm信号为低电平。所以我们只需要通过按键逻辑更改 pwm_level的值,即可控制输出pwm信号的高电平持续时间,pwm_level默认为pwm_0d5ms,也就是默认舵机旋转0度。
当按键key0被按下后,每当cnt_3s计数到1000,并且pwm_level的值小于pwm_2d5ms时,pwm_level才会加一,这是为了使得舵机转动得不至于过快和防止高电平持续时间大于2.5ms不满足SG90舵机的控制要求。
当按键key1被按下后,每当cnt_3s计数到1000,并且pwm_level的值大于于pwm_0d5ms时,pwm_level才会减一,这是为了使得舵机转动得不至于过快和防止高电平持续时间小于0.5ms不满足SG90舵机的控制要求。
当按键key2被按下后,pwm_level的值被重新被设置为pwm_0d5ms,使得舵机旋转到0度位置,舵机归位。
4、引脚绑定
define_attribute {p:key_in[2]} {PAP_IO_DIRECTION} {INPUT}
define_attribute {p:key_in[2]} {PAP_IO_LOC} {17}
define_attribute {p:key_in[2]} {PAP_IO_VCCIO} {1.2}
define_attribute {p:key_in[2]} {PAP_IO_STANDARD} {LVCMOS12}
define_attribute {p:key_in[2]} {PAP_IO_PULLUP} {TRUE}
define_attribute {p:key_in[1]} {PAP_IO_DIRECTION} {INPUT}
define_attribute {p:key_in[1]} {PAP_IO_LOC} {19}
define_attribute {p:key_in[1]} {PAP_IO_VCCIO} {1.2}
define_attribute {p:key_in[1]} {PAP_IO_STANDARD} {LVCMOS12}
define_attribute {p:key_in[1]} {PAP_IO_PULLUP} {TRUE}
define_attribute {p:key_in[0]} {PAP_IO_DIRECTION} {INPUT}
define_attribute {p:key_in[0]} {PAP_IO_LOC} {20}
define_attribute {p:key_in[0]} {PAP_IO_VCCIO} {1.2}
define_attribute {p:key_in[0]} {PAP_IO_STANDARD} {LVCMOS12}
define_attribute {p:key_in[0]} {PAP_IO_PULLUP} {TRUE}
define_attribute {p:pwm} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:pwm} {PAP_IO_LOC} {92}
define_attribute {p:pwm} {PAP_IO_VCCIO} {1.2}
define_attribute {p:pwm} {PAP_IO_STANDARD} {LVCMOS12}
define_attribute {p:pwm} {PAP_IO_DRIVE} {2}
define_attribute {p:pwm} {PAP_IO_NONE} {TRUE}
define_attribute {p:pwm} {PAP_IO_SLEW} {SLOW}
define_attribute {p:clk} {PAP_IO_DIRECTION} {INPUT}
define_attribute {p:clk} {PAP_IO_LOC} {5}
define_attribute {p:clk} {PAP_IO_VCCIO} {1.2}
define_attribute {p:clk} {PAP_IO_STANDARD} {LVCMOS12}
define_attribute {p:clk} {PAP_IO_PULLUP} {TRUE}
define_attribute {p:rst_n} {PAP_IO_DIRECTION} {INPUT}
define_attribute {p:rst_n} {PAP_IO_LOC} {15}
define_attribute {p:rst_n} {PAP_IO_VCCIO} {1.2}
define_attribute {p:rst_n} {PAP_IO_STANDARD} {LVCMOS12}
define_attribute {p:rst_n} {PAP_IO_PULLUP} {TRUE}
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
5、代码仿真
我们对舵机驱动模块进行仿真
仿真代码如下:
`timescale 1ns / 1ps
module tb();
reg clk ;
reg rst_n ;
reg [ 2: 0] key_in ;
wire pwm ;
defparam u_steering_engine.TIME_20MS = 400,
u_steering_engine.TIME_3S = 30 ,
u_steering_engine.pwm_0d5ms = 2_5 ,
u_steering_engine.pwm_1ms = 5_0 ,
u_steering_engine.pwm_1d5ms = 7_5 ,
u_steering_engine.pwm_2ms = 10_0 ,
u_steering_engine.pwm_2d5ms = 12_5 ;
parameter CLK_FREQ = 50;//Mhz
initial
begin
#2
rst_n = 0 ;
clk = 0 ;
#10
rst_n = 1 ;
end
always # ( 1000/CLK_FREQ/2 ) clk = ~clk ;
GTP_GRS GRS_INST(
.GRS_N (1'b1 )
);
steering_engine u_steering_engine(
.clk (clk ),
.rst_n (rst_n ),
.key_in (3'b110 ),
.pwm (pwm )
);
endmodule
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
- 舵机驱动模块输入输出逻辑较为简单,我们只需要模拟参数时钟与复位即可,为了便于观察,我们使用defparam语句对舵机驱动模块内的参数进行重定义,我们这里是模拟按下按键k0使得舵机向右旋转,期望输出的pwm信号应该是占空比逐渐增大的。Modelsim与PDS进行联合仿真的操作在第二部分开发环境搭建章节已有介绍,我们这里启动Modelsim进行仿真,查看pwm信号是否如预期般占空比逐渐增大。
如上图所示,当按下按键k0之后(key_in位宽为3,按键按下后为低电平,未按下为高电平,按下k0后变为3’b110,也就是十进制的6),可以发现输出的pwm信号高电平部分在逐渐增加,由于我们在tb代码中更改了参数,所以这里的pwm信号周期、高电平持续时间并不满足舵机控制的要求,只是为了直观的看出占空比的变换,对pwm信号进行了调整。在实际舵机控制过程中,为了舵机运行得更加平稳,不出现抽搐等现象,pwm波形的变换过程会更加平缓。
同理如果按下k1,舵机将会向左旋转,对应的pwm占空比应该会逐渐减小,按下k2,舵机将会归位,回到0度位置,读者可以在tb文件中对输入按键信号进行修改,观察pwm信号的变换情况。
6、实验现象
当按下开发板按键k0时,舵机向右旋转,当按下k1时,舵机向左旋转,当按下k2时舵机归位。