为有别于其他讲解PWM模块的文章,本文不打算拿出大篇幅对PWM模块各个寄存器进行详细讲解,因为那些东西网上、书店到处都是。如果英语好的话,可以参考datasheet,762页的PDF文档,可惜了我那可怜的英文水平……
本文将结合着实际智能小车的控制来写点东西,多写点控制策略上的东西。
如在寄存器方面有何疑问,都可以留言,我都尽力解答。
首先我电磁车上关于PWM的代码:
复制代码
//PWM.C
void Steering_PWM_Init(void)
{
PWMCTL_CON67 = 1; //PWM通道67级联,B或SB作为时钟源
PWME_PWME7 = 0;
PWMPRCLK_PCKB = 0x3; //预分频 B 8分频 10MHz
PWMSCLB = 0x01; //SB_CLK = B_CLK / (2*1) == 5MHz
PWMPOL_PPOL7 = 1; //起始高电平
PWMCLK_PCLK7 = 1; //PWM模块时钟选择SB
PWMCNT6 = 0x00; //PWMCNTx:通道计数寄存器
PWMCNT7 = 0x00;
PWMPER67 = 50000; // 周期==(1/5M)*(50000)=10ms;
PWMCAE_CAE7 = 0; //左对齐
}
void Motor_PWM_Init(void)
{
PWMCTL_CON01 = 1; //PWM通道01,45级联,A或SA作为时钟源
PWMCTL_CON45 = 1;
PWMPRCLK_PCKA = 0x2; //预分频 A 4分频 20MHz
PWMSCLA = 0x01; //SB_CLK = B_CLK / (2*1) == 10MHz
PWMPOL_PPOL1 = 1;
PWMPOL_PPOL5 = 1;
PWMCLK_PCLK1 = 1;
PWMCLK_PCLK5 = 1;
PWMCNT0 = 0x00;
PWMCNT1 = 0x00;
PWMCNT4 = 0x00;
PWMCNT5 = 0x00;
PWMPER01 = 10000; // 1ms ;1KHz
PWMPER45 = 10000; // 1ms :1KHz
PWMCAE_CAE1 = 0;
PWMCAE_CAE5 = 0;
}
//控制舵机
void Set_PWM67(uint16 duty, uint16 period)
{
if(duty <= period)
{
PWMPER67 = period;
PWMDTY67 = duty;
}
}
//控制电机
void Set_PWM01(uint16 duty, uint16 period)
{
if(duty <= period)
{
PWMPER01 = period;
PWMDTY01 = duty;
}
}
void Set_PWM45(uint16 duty, uint16 period)
{
if(duty <= period)
{
PWMPER45 = period;
PWMDTY45 = duty;
}
}
//PWM.h
/******************************************************************************
* PWM模块
*******************************************************************************/
void Steering_PWM_Init(void);
void Motor_PWM_Init(void);
void Set_PWM01(uint16 duty, uint16 period);
void Set_PWM67(uint16 duty, uint16 period);
void Set_PWM45(uint16 duty, uint16 period);
#define Start_PWM01() {PWME_PWME1 = 1;}
#define Stop_PWM01() {PWME_PWME1 = 0;}
#define Start_PWM67() {PWME_PWME7 = 1;}
#define Stop_PWM67() {PWME_PWME7 = 0;}
#define Start_PWM45() {PWME_PWME5 = 1;}
#define Stop_PWM45() {PWME_PWME5 = 0;}
复制代码
首先,上述代码默认PWM通道是关闭的,所以在使用时需要先用定义的宏去打开PWM模块。
舵机部分:
PWM控制的无非就是舵机和电机。咱们先谈谈舵机,先使用宏Start_PWM67();打开67通道级联的PWM模块,然后就可以对舵机的转角进行实际操作了。
转角的控制一般有查表的开环控制和PD的闭环控制,查表就是先根据位置量列出一个数组分别对应着各位置量时的转角参数。
如:
左右极限值和中心值为:2120/5380/3750
复制代码
const uint16 steer_arr[253] = {
2120, 2132, 2145, 2158, 2171, 2184, 2197, 2210,
2223, 2236, 2249, 2262, 2275, 2288, 2301, 2314,
2326, 2339, 2352, 2365, 2378, 2391, 2404, 2417,
2430, 2443, 2456, 2469, 2482, 2495, 2508, 2521,
2533, 2546, 2559, 2572, 2585, 2598, 2611, 2624,
2637, 2650, 2663, 2676, 2689, 2702, 2715, 2728,
2740, 2753, 2766, 2779, 2792, 2805, 2818, 2831,
2844, 2857, 2870, 2883, 2896, 2909, 2922, 2935,
2947, 2960, 2973, 2986, 2999, 3012, 3025, 3038,
3051, 3064, 3077, 3090, 3103, 3116, 3129, 3141,
3154, 3167, 3180, 3193, 3206, 3219, 3232, 3245,
3258, 3271, 3284, 3297, 3310, 3323, 3336, 3348,
3361, 3374, 3387, 3400, 3413, 3426, 3439, 3452,
3465, 3478, 3491, 3504, 3517, 3530, 3543, 3555,
3568, 3581, 3594, 3607, 3620, 3633, 3646, 3659,
3672, 3685, 3698, 3711, 3724, 3737, 3750, 3762,
3775, 3788, 3801, 3814, 3827, 3840, 3853, 3866,
3879, 3892, 3905, 3918, 3931, 3944, 3956, 3969,
3982, 3995, 4008, 4021, 4034, 4047, 4060, 4073,
4086, 4099, 4112, 4125, 4138, 4151, 4163, 4176,
4189, 4202, 4215, 4228, 4241, 4254, 4267, 4280,
4293, 4306, 4319, 4332, 4345, 4358, 4370, 4383,
4396, 4409, 4422, 4435, 4448, 4461, 4474, 4487,
4500, 4513, 4526, 4539, 4552, 4565, 4577, 4590,
4603, 4616, 4629, 4642, 4655, 4668, 4681, 4694,
4707, 4720, 4733, 4746, 4759, 4771, 4784, 4797,
4810, 4823, 4836, 4849, 4862, 4875, 4888, 4901,
4914, 4927, 4940, 4953, 4966, 4978, 4991, 5004,
5017, 5030, 5043, 5056, 5069, 5082, 5095, 5108,
5121, 5134, 5147, 5160, 5173, 5185, 5198, 5211,
5224, 5237, 5250, 5263, 5276, 5289, 5302, 5315,
5328, 5341, 5354, 5367, 5380
};
复制代码
这是我电磁车的,一开始我也是用的开环查表的方式,可能有人要说了,怎么这么多,这要手工排不排到眼花啊?其实这是因为传感器是模拟量,一般没这么多啊,好吧,如果各位和我一样懒,其实咱们可以另外写一个程序帮我们在建表。
如:
复制代码
#include <cstdlib>
#include <iostream>
#define min 2120
#define max 5380
#define count 253
using namespace std;
int main(int argc, char *argv[])
{
int i = 0;
cout << "const uint16 steering_arr[count] = {";
for(i = 0;i <= count - 1;i++)
{
if(i) cout << ", ";
if(!(i%8)) cout << endl << "/t";
cout << min + (max - min) * i / (count - 1);
}
cout<<",/n";
cout << endl << "};" << endl;
return 0;
}
复制代码
这下简单了吧!呵呵,谁叫咱会偷懒呢,还有一点要注意的是因为这里位置量比较多,所以整个列表都是均匀排布的,当位置量较少时,最好是中间密,两边疏,这样有利于智能车的直道的稳定性。
或是如下PD控制:
复制代码
#define KP_steering_ 8
#define KD_steering_ 3
#define KP_steering 16
#define KD_steering 6
uint16 temp;
sint16 error;
pre_pos_ = pos_;
error = pos_ - pre_pos_;
if(pos_ <= 30 && pos_ >= -30)
{
temp = Steer_Angle_Center + KP_steering_ * ~pos_ - KD_steering_ * error;
if(temp < 2120)
{
Set_PWM67(2120, 50000);
test_steering = 2120;
}
else if(temp > 5380)
{
Set_PWM67(5300, 50000);
test_steering = 5380;
}
else
{
Set_PWM67(temp, 50000);
test_steering = temp;
}
}
else if(pos_ > 30 || pos_ < -30)
{
temp = Steer_Angle_Center + KP_steering * ~pos_ - KD_steering * error;
if(temp < 2120)
{
Set_PWM67(2120, 50000);
test_steering = 2120;
}
else if(temp > 5380)
{
Set_PWM67(5380, 50000);
test_steering = 5380;
}
else
{
Set_PWM67(temp, 50000);
test_steering = temp;
}
}
else
{
_asm(nop);
}
复制代码
电机部分:
谈到电机,必然先谈电机驱动,前几届的队伍多半是用33886作为智能车的电机驱动芯片,但我个人认为33886驱动能力有限,且发热量过半,还因为飞思卡尔比赛的原因使该芯片一篇难求,水涨船高,蛮贵的,其实不如使用4个功率MOS管搭建H桥电路来用于电机驱动,具体做法以及如何搭线(如何搭线关系到是否可以反转制动)不打算在这里详说,有时间专门写篇文章讨论这个。
言归正传,还是谈电机控制,电机控制无非是一开始就给个速度让它跑,
如:
//假设电机驱动,一路用PWM,另一路用IO口置低电平
Start_PWM01();
Set_PWM01(5000,10000);
这种开环控制因为电池电压以及实际路况等原因,必然不是最好的。
PID控制的引入可以很好的解决这个问题,我采用的是PID与BANG-BANG控制相结合的方法,由PID是控制精度,BANG-BANG是控制力度,达到了比较好的效果。
这里简单讲讲BANG-BANG控制,对调速范围宽、静态误差小和动态响应快的随动系统来说,单闭环控制是不能满足要求的,所以随动系统采用电流环、速度环和位置环来完成控制。在随动系统控制中,PID控制具有结构简单且在对象模型不确知的情况下也可达到有效控制的特点,但对模型参数变化及干扰的适应能力较差。BANG-BANG控制在系统偏差大,可加大系统的控制力度,提高系统的快速性,因此,BANG-BANG控制是随动系统中不可缺少的控制方式。说明白点就是即时间最优控制的各个分量u(t)都是时间t的分段常值函数,并在开关时间上由一个恒值到另一个恒值的跳变。而映射到智能车上所谓一个恒值到另一个恒值就是速度的最小值到最大值。
具体依旧打算令开篇详讲。
一周热门 更多>