pca9685的舵机控制
Verilog实现IIC主机对从机的写操作(zybo z7板运行代码)
输入信号:
word[15:0]——输入理想的舵机角度
write[2:0]——输入指定转动的舵机序号
start——传输信号开始
initialize——重置所有舵机角度
输出信号:
SDA——IIC数据线
SCL——IIC时钟线
IIC_SEND模块的主要功能为将接受到的并行8位数据信号,寄存器地址信号转为串行方式输出,同时在一次传输结束之后发送O_done_flag信号,告知控制模块一次发送完成。
Control_setPWM模块的主要功能为接受前一转换器模块发送来的控制信号,进而将16位数据信号转换为高8位和低8位分别发送,同时接受相应的write信号并将其转化为实际的从机的寄存器地址。因为每次改变舵机角度时需要发送4次IIC信号给pca9685驱动板,所以当Control_setPWM收到start信号时,进入一个4状态的有限状态机,在4状态循环内进入下一状态的标志为IIC_SEND完成一次发送的O_done_flag信号,同时在4状态完成时发送done信号代表一次控制命令执行完成。
在Verilog实现IIC主机对从机的写操作(zybo z7板运行代码)
这篇博文中已经上传了verilog实现IIC信号传输的代码,所以这里只需要Control_setPWM模块的代码和顶层模块代码就可以实现了
//实现setPWM功能
module Control_setPWM(
input O_done_flag,//检测一次IIC传输是否完成
input [2:0]write,//输入地址写入信号(0--3号舵机)
input [15:0]word,//输入16进制下降沿的时间
input start,//开始信号,改变数据后是否开始传输
input rst,
input clk,
input initialize,//初始化信号
output reg [7:0]word_add,//输出寄存器地址
output reg [7:0]data,//输出数据
output reg en,//输出使能信号
output reg done//一次命令传输完成
// output reg [4:0]state//检验输出
);
//variation declaration
reg en_reg,en_next;
reg [4:0]state_reg,state_next;//有限状态机,一共5个状态,1个空闲位,一次发送4个数据
reg [7:0]data_reg,data_next;
reg [7:0]word_add_reg,word_add_next;
reg [15:0]word_reg;//存储输入的下降沿时间
reg done_reg,done_next;
reg [16:0]i_reg,i_next;//循环计数值
parameter ON_L=8'H06;//16进制
parameter ON_H=8'H07;
parameter OFF_L=8'H08;
parameter OFF_H=8'H09;
parameter PCA9685_MODE1=8'H00;//寄存器1地址
parameter PCA9685_PRESCALE=8'HFE;//周期寄存器地址
always@(posedge clk,posedge rst)
if(rst) begin//没有输出数据
state_reg<=0;
data_reg<=0;
word_add_reg<=0;
en_reg<=0;
done_reg<=0;
i_reg=17'b11000011010100000;
// i_reg<=5;
end
else begin
state_reg<=state_next;
data_reg<=data_next;
word_add_reg<=word_add_next;
en_reg<=en_next;
done_reg<=done_next;
i_reg<=i_next;
end
always@*
begin
state_next<=state_reg;
data_next<=data_reg;
word_add_next<=word_add_reg;
en_next<=en_reg;
done_next<=done_reg;
case(state_reg)
0://idle状态,复位所有寄存器,并让使能信号拉低
if(initialize)//当有初始化信号时,优先进入初始化
begin
state_next<=26;//进入初始化状态
// data_next<=8'b00110001;
data_next<=8'b00000000;//1st发送全0信号
word_add_next<=PCA9685_MODE1;//装载MODE1
en_next<=1;
done_next<=0;
end
else//再考虑传输信号
if(start)//传输信号开始
begin
state_next<=1;
word_add_next<=ON_L+4*write;
data_next<=8'H00;//起始信号一直为0
en_next<=1;//使能
done_next<=0;
end
else begin
state_next<=0;//停留在idle状态
word_add_next<=0;
data_next<=0;
en_next<=0;//使能信号为低电平
done_next<=0;//返回为低电平
end
1://ON_L状态
if(O_done_flag)//接受传输是否完成信号,完成进入下一个状态
begin
state_next<=2;
word_add_next<=ON_H+4*write;
data_next<=0;
en_next<=1;
done_next<=0;
end
else begin
state_next<=1;//保持
end
2://ON_H状态
if(O_done_flag)
begin
state_next<=3;//发送完毕后进入下一个状态
word_add_next<=OFF_L+4*write;
data_next<=word[7:0];//先输入前7位
en_next<=1;
done_next<=0;
end
else begin
state_next<=2;
end
3://OFF_L状态
if(O_done_flag)
begin
state_next<=4;//发送完毕后进入下一个状态
word_add_next<=OFF_H+4*write;//写入下降位高位地址
data_next<=word[15:8];//先输入高8位
en_next<=1;//使能传输
done_next<=0;//传输完成
end
else begin
state_next<=3;
end
4://OFF_H状态
if(O_done_flag)begin
state_next<=0;//停留在idle状态
word_add_next<=0;
data_next<=0;
en_next<=0;//使能信号为低电平
done_next<=1;//结束信号才表示传输完成
end
else begin
state_next<=4;
end
5://初始化第一个状态,进入睡眠,默认使用内部时钟
if(O_done_flag)
begin
data_next<=8'b01111111;//127
word_add_next<=PCA9685_PRESCALE;
en_next<=1;
state_next<=6;
done_next<=0;
end
else
state_next<=5;
6://设置内部周期寄存器
if(O_done_flag)
begin
data_next<=8'b00000000;//退出睡眠
word_add_next<=PCA9685_MODE1;
en_next<=1;
state_next<=7;
done_next<=0;
i_next<=17'b11000011010100000;
// i_next<=5;//提前给计数位赋值
end
else
state_next<=6;
7://延迟状态
if(O_done_flag)
begin
state_next<=8;//进入延迟阶段
done_next<=0;
word_add_next<=0;
data_next<=0;
en_next<=0;//不传输信号
i_next<=i_reg-1;//计数位自减
end
else
state_next<=7;
8://延迟1ms
if(i_reg==0)
begin
state_next<=9;
data_next<=8'b10100001;//发送重置信号
word_add_next<=PCA9685_MODE1;
en_next<=1;
done_next<=0;
end
else
begin
state_next<=8;//保留在延迟阶段
done_next<=0;
word_add_next<=0;
data_next<=0;
en_next<=0;//不传输信号
i_next<=i_reg-1;//计数位自减
end
9://复位信号
if(O_done_flag)//回归0状态
begin
state_next<=10;
data_next<=8'b00000000;
en_next<=1;
done_next<=1;//复位完成,进入重载信号,标志位放1
word_add_next<=ON_L;//写入0号舵机寄存器低位
end
else
state_next<=9;
10://0号舵机
if(O_done_flag)
begin
state_next<=11;
data_next<=0;
en_next<=1;
done_next<=0;
word_add_next<=ON_H;//写入0号舵机寄存器高位
end
else
state_next<=10;//保留在10
11:
if(O_done_flag) begin
state_next<=12;
data_next<=8'b00110011;
en_next<=1;
done_next<=0;
word_add_next<=OFF_L;//写入0号舵机寄存器低位
end
else
state_next<=11;
12:
if(O_done_flag) begin
state_next<=13;
data_next<=8'b00000001;
en_next<=1;
done_next<=0;
word_add_next<=OFF_H;//写入寄存器高位
end
else
state_next<=12;
13:
if(O_done_flag) begin
state_next<=14;
data_next<=8'b00000000;
en_next<=1;
done_next<=0;
word_add_next<=ON_L+4;//写入1号舵机寄存器低位
end
else
state_next<=13;
14:
if(O_done_flag) begin
state_next<=15;
data_next<=8'b00000000;
en_next<=1;
done_next<=0;
word_add_next<=ON_H+4;//写入1号舵机寄存器高位
end
else
state_next<=14;
15:
if(O_done_flag) begin
state_next<=16;
data_next<=8'b00110011;
en_next<=1;
done_next<=0;
word_add_next<=OFF_L+4;//写入1号舵机寄存器低位
end
else
state_next<=15;
16:
if(O_done_flag) begin
state_next<=17;
data_next<=8'b00000001;
en_next<=1;
done_next<=0;
word_add_next<=OFF_H+4;//写入1号舵机寄存器低位
end
else
state_next<=16;
17:
if(O_done_flag) begin
state_next<=18;
data_next<=8'b00000000;
en_next<=1;
done_next<=0;
word_add_next<=ON_L+8;//写入1号舵机寄存器低位
end
else
state_next<=17;
18:
if(O_done_flag) begin
state_next<=19;
data_next<=8'b00000000;
en_next<=1;
done_next<=0;
word_add_next<=ON_H+8;//写入1号舵机寄存器高位
end
else
state_next<=18;
19:
if(O_done_flag) begin
state_next<=20;
data_next<=8'b00110011;
en_next<=1;
done_next<=0;
word_add_next<=OFF_L+8;//写入1号舵机寄存器低位
end
else
state_next<=19;
20:
if(O_done_flag) begin
state_next<=21;
data_next<=8'b00000001;
en_next<=1;
done_next<=0;
word_add_next<=OFF_H+8;
end
else
state_next<=20;
21:
if(O_done_flag) begin
state_next<=22;
data_next<=8'b00000000;
en_next<=1;
done_next<=0;
word_add_next<=ON_L+12;
end
else
state_next<=21;
22:
if(O_done_flag) begin
state_next<=23;
data_next<=8'b00000000;
en_next<=1;
done_next<=0;
word_add_next<=ON_H+12;
end
else
state_next<=22;
23:
if(O_done_flag) begin
state_next<=24;
data_next<=8'b00110011;
en_next<=1;
done_next<=0;
word_add_next<=OFF_L+12;
end
else
state_next<=23;
24:
if(O_done_flag) begin
state_next<=25;
data_next<=8'b00000001;
en_next<=1;
done_next<=0;//写完OFF高位代表4个舵机全部初始化完成,返回done信号
word_add_next<=OFF_H+12;
end
else
state_next<=24;
25:
if(O_done_flag) begin
state_next<=0;//返回idle状态
data_next<=8'b00000000;//数据位置零
en_next<=0;//发送位置0
done_next<=1;//完成标志位置0
word_add_next<=0;//寄存器都置零
end
else
state_next<=25;
26:
if(O_done_flag) begin
state_next<=5;//进入睡眠状态
data_next<=8'b00010000;//数据位置零,1的位置出错!!!!
en_next<=1;//发送位置1
done_next<=0;//完成标志位置0
word_add_next<=PCA9685_MODE1;//寄存器都置零
end
else
state_next<=26;
default:
begin
state_next<=0;//默认进入空闲位
word_add_next<=0;
en_next<=0;
data_next<=0;
done_next<=0;
end
endcase
end
//output logic
always@(posedge clk) begin
data<=data_reg;
word_add<=word_add_reg;
en<=en_reg;
done<=done_reg;
// state<=state_reg;
end
endmodule//实现setPWM功能
module IIC_RECEIVE_COMMAND(
input clk,
input rst,
input start,
input initialize,
input [2:0]write,
input [15:0]word,
output O_scl,
output IO_sda,
output done
// output [4:0]state
);
wire [7:0]word_add;//地址线
wire [7:0]data;//数据线
wire O_done_flag;//完成标志位
wire en;//使能线
// reg [2:0]write=3'b001;//2号舵机
// reg [15:0]word=16'H00ef;//角度为60度,占空比6%
Control_setPWM c1(
.O_done_flag(O_done_flag),
.write(write),
.word(word),
.rst(rst),
.clk(clk),
.start(start),
.initialize(initialize),
.done(done),
.en(en),//使能信号
.data(data),
.word_add(word_add)
// .state(state)
);
IIC_SEND t1(
.I_clk(clk),
.I_rst_n(rst),
.I_iic_send_en(en),
.I_word_addr(word_add),
.I_write_data(data),
.O_done_flag(O_done_flag),
.O_scl(O_scl),
.IO_sda(IO_sda)
);
endmodule