这次边做边写,基本上把做proj的整个步骤记录下来,包括中间的思考过程,也挺有意思的
小车主控板思路
主控板需要完成的功能:
1.接受传输过来的数据
2.根据数据控制电机PWM
3.根据数据控制舵机PWM控制电机的PWM实现:
1.一个函数,两个参数:move(dir,speed)
2.dir控制前后,speed控制pwm.
3.如果方向改变需要延迟一定时间控制舵机的pwm实现:
1.一个函数,两个参数:turn(dir,value)
2.dir控制左右,value是设定值控制pwm
3.value需要配合PID算法写好
两个电机控制需要两个tim的pwm,需要四个管脚输出控制正反转.
PID算法实现的时候,舵机有反馈线反馈现在所在的角度.反馈用ADC采样读取
PS: 未测试NRF24L01通信模块前自己留一个串口,使用蓝牙调PID
程序步骤
- 开启时钟(GPIO,TIM, ADC,USART,AFIO)
- 写move函数
- 写PID算法
- 写turn函数
- 配置蓝牙串口
- 调试PID
开启时钟
ABP1和APB2对应的外设见下表
使用TIM生成PWM波
这里使用TIM生成的PWM是为了让PWM更加稳定可靠
原理解释
通用定时器可以利用GPIO引脚进行脉冲输出,在配置为比较输出、PWM输出功能时,捕获/比较寄存器TIMx_CCR被用作比较功能,下面把它简称为比较寄存器。这里直接举例说明定时器的PWM输出工作过程:若配置脉冲计数器TIMx_CNT为向上计数,而重载寄存器TIMx_ARR被配置为N,即TIMx_CNT的当前计数值数值X在TIMxCLK时钟源的驱动下不断累加,当TIMx_CNT的数值X大于N时,会重置TIMx_CNT数值为0重新计数。
而在TIMxCNT计数的同时,TIMxCNT的计数值X会与比较寄存器TIMx_CCR预先存储了的数值A进行比较,当脉冲计数器TIMx_CNT的数值X小于比较寄存器TIMx_CCR的值A时,输出高电平(或低电平),相反地,当脉冲计数器的数值X大于或等于比较寄存器的值A时,输出低电平(或高电平)。
如此循环,得到的输出脉冲周期就为重载寄存器TIMx_ARR存储的数值(N+1)乘以触发脉冲的时钟周期,其脉冲宽度则为比较寄存器TIMx_CCR的值A乘以触发脉冲的时钟周期,即输出PWM的占空比为 A/(N+1) 。
TIM3复用引脚
如下图
感觉没有引脚冲突的情况下,可以默认没有重映像,TIM3_CH1~4的管脚对应的就是PA6、PA7、PB0、PB1.输出模式也好确定,查手册可以看到:输出采用推挽复用输出
Mode值放这方便查看:
void tim3()
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //初始化计数配置
TIM_OCInitTypeDef TIM_OCInitStructure;//初始化TIM3的外设
TIM_TimeBaseStructure. TIM_Period =999; //计数上限为1000
TIM_TimeBaseStructure.TIM_Prescaler =71; //计时器预分频为72/(71+1)=1MHZ
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //不分
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //TIM脉冲宽度调制模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 10;//设置了待装入捕获比较寄存器的脉冲值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //TIM输出比较极性高
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);//使能TIM3在CCR1上的预装载寄存器
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 10;
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);//使能TIM3在CCR2上的预装载寄存器
TIM_Cmd(TIM3,ENABLE);
}
注意TIM_OCPolarity的选择和OCMode的PWM两种模式的区别:
- OCPolarity为高则高电平有效
- 若TIM_OCInitTypeDef.TIM_OCMode = TIM_OCMode_PWM1时:
当计时器值小于比较器设定值时则TIMX输出脚此时输出有效
当计时器值大于或等于比较器设定值时则TIMX输出脚此时输出无效 - 若TIM_OCInitTypeDef.TIM_OCMode = TIM_OCMode_PWM2时:
当计时器值小于比较器设定值时则TIMX输出脚此时输出无效
当计时器值大于或等于比较器设定值时则TIMX输出脚此时输出有效
修改比较寄存器值的函数TIM_SetComparex(TIM_TypeDef* TIMx, u16 Comparex)
把TIMx的比较寄存器x的值设成Comparex
u16 TIMCompare1 = 0x7FFF;
TIM_SetCompare1(TIM3, TIMCompare1);
试用驱动后的第一次修订
因为采用的大电机的mos驱动很窒息,有一个刹车功能,所以是采用的双路PWM驱动正反转。因此修改之前的TIM生成的PWM配置,采用多个定时器输出多路PWM波.
新的PWM安排如下:
- TIM2的CH3和TIM3的CH1分别对应PA2和PA6给大电机的两路PWM.
- TIM4的CH1对应着PB6给舵机的PWM.
新的引脚安排:
- MOS驱动使能采用PB9.
大坑出现
尝试着正转和反转,但是遇到了大坑.想通过关闭TIM来关闭PWM输出,结果是关闭了TIM之后PWM输出端口就自动高电平无法拉低!.
原因:复用推挽输出下IO口无法控制高低电平,因此拉低只能使用配置占空比的方法.
所以之前配多路TIM是没太大意义的。。关闭PWM只要把占空比调成零就好了!坑啊!