之前分享过GPT定时器的基础使用,把GPT作为基础的定时器来使用的。
在RA系列板子上,GPT更重要的作用之一,是进行PWM输出,不然对不起他的全名:General PWM Timer。
要使用PWM输出功能,借助FSP工具,可以很方便的进行配置,然后在代码中进行对应的调用处理。
正好,我手头有一个MX1508的直流电机控制模块,以及一个带风扇的小直流电机,刚好可以用上。
MX1508一般是这样的:
可以同时控制两路直流电机,例如在小车上使用。
通常按照如下方式使用:
不过,我手头的这个,只能控制一个电机:
通过MX1508控制电机,需要两个信号接口,一个是PWM控制转速,一个是DIR控制方向。
通过查看RA4M2的硬件手册和原理图,最终进行如下的设计:
- PWM控制使用GPT1,因为其两个PWM输出IO口,分别为P405、P406,而P405刚好接到了板子上的LED3,可以用其亮度,表示当前PWM的占空比情况,而P406则输出控制电机
- 使用LED2来表示电机运转的方向,其对应的控制引脚为P404,同时用于控制电机运转的方向
- 使用板载SW1按键,其对应的因较为P005,用来控制是否保持电机当前的转速;同时,使用LED1的亮灭表示是否保持。
对应的手册信息如下:
做好了设计规划,就先用FSP进行设置。
首先,P405默认设置为普通IO控制LED3,如果要使用PWM进行控制,则需要取消原有的设置,也就是禁用P405默认的配置:
然后,配置GPT1:
当选择GPIOCA and GPIOCB的时候,会自动设置GPIOC1A、GPIOC1B为P405、P406。如果用GPIOCA or GPIOCB,则只能二选一启用一个。
其次,进行gpt1 stack的设置:
先添加一个gpt:
在设置对应的配置,按照如下步骤设置或者检查对应的值是否正确:
各个部分的说明如下:
- Common:主要是启用输出功能
- General:主要是设置变量名、通道、模式、周期、周期单位,这些会生成到代码文件中,作为配置调用
- Output:设置启用两个输出IO端口
- Pins:检查是否为对应的引脚设置即可
做好了设置,生成代码,就可以在进行实际的代码编写了。
其他部分的代码FSP都生成好了,我们只需要编写关键调用即可:
void hal_entry(void)
{
int32_t pwm_counter = 0;
uint32_t key_counter = 0;
bool status = false;
bool direction = true;
bsp_io_level_t Pin_P005;
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);
assert(FSP_SUCCESS == err);
(void) R_GPT_Start(&g_timer_gpt1_ctrl);
err = R_GPT_PeriodSet(&g_timer_gpt1_ctrl, 10000);
assert(FSP_SUCCESS == err);
while (1)
{
R_IOPORT_PinRead(&g_ioport_ctrl, BSP_IO_PORT_00_PIN_05, &Pin_P005);
if (Pin_P005 == BSP_IO_LEVEL_LOW && !status)
{
status = true;
key_counter++;
}
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);
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);
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
R_BSP_NonSecureEnter();
#endif
}
上述代码的逻辑非常清晰,注解也很详细,就不一一说明了,几个重点说一下:
- 按键采用了根据按下计数次数,来进行是否保持的设定;如果按下偶数次,则保持;如果按下奇数次,则不保持。这样做的目的,是为了防止按下的时候,如果一直检测到按下,则会不断地触发。也可以用按下来进行保持,松开则不包吃,但是这样并不方便。
- 占空比根据direction,来自动进行增加或者减小,在0~10000之间往复
- 实际运转的时候,如果处于非保持状态,则自动加速到最大速度,然后反向运转,自动减速到最小速度,如此往复;在次之间任意时刻按下按键,则会保持当前的转速运转;再按一次,则恢复往复运转状态。
在编译烧录上诉代码之前,把MX1508和电机节后,然后接到开发板上。
通过开发板的手册,可以得知,开发板上有两个PMOD接口,P406在PMOD1上,可以用该PMOD接口,直接驱动DC电机,非常方便:
具体的接线如下:
为了方便检查,点击电机按照预期的方向运转,我在点击前面挂了一张纸巾,这样就可以根据纸巾飘动方向,来判断运转是否正常了。
实际运转的效果如下:
具体控制效果,可以查看文章后面的视频。
通过视频可以看到,使用按键1,可以很方便的进行速度的保持,不管是在高速状态,还是在低速状态。
而在非保持状态下,电机按照预期的,先加速,再转向减速,纸巾的飘动方向和幅度也随之变化。
另外,当占空比过低的时候,电机会不启动。