之前分享过GPT定时器的基础使用,把GPT作为基础的定时器来使用的。
在RA系列板子上,GPT更重要的作用之一,是进行PWM输出,不然对不起他的全名:General PWM Timer。
要使用PWM输出功能,借助FSP工具,可以很方便的进行配置,然后在代码中进行对应的调用处理。
正好,我手头有一个MX1508的直流电机控制模块,以及一个带风扇的小直流电机,刚好可以用上。
MX1508一般是这样的:
可以同时控制两路直流电机,例如在小车上使用。
通常按照如下方式使用:
不过,我手头的这个,只能控制一个电机:
通过MX1508控制电机,需要两个信号接口,一个是PWM控制转速,一个是DIR控制方向。
通过查看RA4M2的硬件手册和原理图,最终进行如下的设计:
对应的手册信息如下:
做好了设计规划,就先用FSP进行设置。
首先,P405默认设置为普通IO控制LED3,如果要使用PWM进行控制,则需要取消原有的设置,也就是禁用P405默认的配置:
然后,配置GPT1:
当选择GPIOCA and GPIOCB的时候,会自动设置GPIOC1A、GPIOC1B为P405、P406。如果用GPIOCA or GPIOCB,则只能二选一启用一个。
其次,进行gpt1 stack的设置:
先添加一个gpt:
在设置对应的配置,按照如下步骤设置或者检查对应的值是否正确:
各个部分的说明如下:
做好了设置,生成代码,就可以在进行实际的代码编写了。
其他部分的代码FSP都生成好了,我们只需要编写关键调用即可:
void hal_entry(void)
{
/* TODO: add your own code here */
int32_t pwm_counter = 0; // PWM次数计数
uint32_t key_counter = 0; // 按键次数计数
bool status = false; // 按键状态
bool direction = true; // 运转方向
bsp_io_level_t Pin_P005; // 定义按键1
fsp_err_t err = FSP_SUCCESS;// 定义调用返回结果
err = R_IOPORT_Open(&g_ioport_ctrl, &g_bsp_pin_cfg); // 初始化引脚
assert(FSP_SUCCESS == err); // 判断是否初始化成功
err = R_GPT_Open(&g_timer_gpt1_ctrl, &g_timer_gpt1_cfg); // 初始化GPT1
assert(FSP_SUCCESS == err); // 判断是否初始化成功
(void) R_GPT_Start(&g_timer_gpt1_ctrl); // 启用GPT1
err = R_GPT_PeriodSet(&g_timer_gpt1_ctrl, 10000); // 设置运行频率为10KHz
assert(FSP_SUCCESS == err);
while (1)
{
R_IOPORT_PinRead(&g_ioport_ctrl, BSP_IO_PORT_00_PIN_05, &Pin_P005); // 读取按键1的结果
if (Pin_P005 == BSP_IO_LEVEL_LOW && !status)
{ // 判断按键有没有按下
status = true; // 设置按下状态
key_counter++; // 按键次数+1
}
else if (Pin_P005 == BSP_IO_LEVEL_HIGH && status)
{ // 判断按键有没有松开
status = false; // 设置松开状态
}
if(key_counter%2==0) {
// 偶数次按下,则保持运转状态
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_15, BSP_IO_LEVEL_HIGH); // 点亮LED
// 设置占空比
err = R_GPT_DutyCycleSet(&g_timer_gpt1_ctrl, pwm_counter, GPT_IO_PIN_GTIOCA_AND_GTIOCB);
assert(FSP_SUCCESS == err);
// 延时
R_BSP_SoftwareDelay (100, BSP_DELAY_UNITS_MILLISECONDS);
} else {
// 奇数次按下,则正常运转
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_15, BSP_IO_LEVEL_LOW); // 关闭LED
// 根据运转方向,设置是加速还是减速,实际运转,将类似呼吸灯,逐渐加速直到最大值,再逐渐减速直到最小值,如此反复
if(direction) {
pwm_counter += 100;
} else {
pwm_counter -= 100;
}
if (pwm_counter > 10000) {
// 超过最大值,则保持最大值,但方向转向
pwm_counter = 10000;
direction = !direction;
// 设置占空比
err = R_GPT_DutyCycleSet(&g_timer_gpt1_ctrl, 0, GPT_IO_PIN_GTIOCA_AND_GTIOCB);
assert(FSP_SUCCESS == err);
}
else if (pwm_counter < 0) {
// 达到最小值,则保持最小值,但方向转向
pwm_counter = 0;
direction = !direction;
// 设置占空比
err = R_GPT_DutyCycleSet(&g_timer_gpt1_ctrl, 0, GPT_IO_PIN_GTIOCA_AND_GTIOCB);
assert(FSP_SUCCESS == err);
}
else {
// 设置占空比
err = R_GPT_DutyCycleSet(&g_timer_gpt1_ctrl, pwm_counter, GPT_IO_PIN_GTIOCA_AND_GTIOCB);
assert(FSP_SUCCESS == err);
}
// 设置运转方向及指示灯
if(direction) {
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_04, BSP_IO_LEVEL_HIGH);
} else {
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_04, BSP_IO_LEVEL_LOW);
}
// 延时
R_BSP_SoftwareDelay (100, BSP_DELAY_UNITS_MILLISECONDS);
}
}
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}
上述代码的逻辑非常清晰,注解也很详细,就不一一说明了,几个重点说一下:
在编译烧录上诉代码之前,把MX1508和电机节后,然后接到开发板上。
通过开发板的手册,可以得知,开发板上有两个PMOD接口,P406在PMOD1上,可以用该PMOD接口,直接驱动DC电机,非常方便:
具体的接线如下:
为了方便检查,点击电机按照预期的方向运转,我在点击前面挂了一张纸巾,这样就可以根据纸巾飘动方向,来判断运转是否正常了。
实际运转的效果如下:
具体控制效果,可以查看文章后面的视频。
通过视频可以看到,使用按键1,可以很方便的进行速度的保持,不管是在高速状态,还是在低速状态。
而在非保持状态下,电机按照预期的,先加速,再转向减速,纸巾的飘动方向和幅度也随之变化。
另外,当占空比过低的时候,电机会不启动。
更多回帖