#申请原创# @21小跑堂
3.按键+定时器+PWM测试
3.1按键轮询方式
开发板上有一个可编程按键,接在了PA07上,低电平触发,外接了上拉电阻
按键轮询是间隔很短时间不断查询GPIO状态,从而得知是否有按键动作,按键在按下或释放的过程中,可能会伴随抖动,在抖动过程中,会产生多次高低电平,导致被识别为多次按键操作,因此为了避免误判需要进行去抖处理,软件上可以用延时来去抖
用轮询的方式写个按键程序,用按键控制LED的亮灭,代码实现
#include "gpio.h"
void led_init(void)
{
stc_gpio_cfg_t stcGpioCfg;
Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);
stcGpioCfg.enDir = GpioDirOut;
stcGpioCfg.enPu = GpioPuDisable;
stcGpioCfg.enPd = GpioPdEnable;
Gpio_ClrIO(STK_LED_PORT, STK_LED_PIN);
Gpio_Init(STK_LED_PORT, STK_LED_PIN, &stcGpioCfg);
}
void key_init(void)
{
stc_gpio_cfg_t stcGpioCfg;
Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);
stcGpioCfg.enDir = GpioDirIn;
stcGpioCfg.enPu = GpioPuDisable;
stcGpioCfg.enPd = GpioPdDisable;
Gpio_Init(STK_USER_PORT, STK_USER_PIN, &stcGpioCfg);
}
int32_t main(void)
{
led_init();
key_init();
while(1)
{
if(Gpio_GetInputIO(STK_USER_PORT, STK_USER_PIN) == FALSE)
{
delay1ms(10);
while(Gpio_GetInputIO(STK_USER_PORT, STK_USER_PIN) == FALSE);
Gpio_WriteOutputIO(STK_LED_PORT, STK_LED_PIN,!Gpio_ReadOutputIO(STK_LED_PORT, STK_LED_PIN));
}
}
}
编译烧录查看效果
3.2按键中断方式
与上面的轮询方法不同,这种方式并不用一直查询GPIO的状态,按键状态改变时会产生中断,程序在检测到中断后再判断按键状态
查看interrupts_hc32l19x.c这个文件,这是官方提供的一个统一中断入口文件
可以看到在GPIOA的中断函数里调用了PortA_IRQHandler这个方法,这个方法面用了__WEAK声明
按照interrupts_hc32l19x.h的注释说明,可以在需要的地方重新定义这个方法
使用这种方式要把这里填上
代码示例
#include "gpio.h"
void led_init(void)
{
stc_gpio_cfg_t stcGpioCfg;
Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);
stcGpioCfg.enDir = GpioDirOut;
stcGpioCfg.enPu = GpioPuDisable;
stcGpioCfg.enPd = GpioPdEnable;
Gpio_ClrIO(STK_LED_PORT, STK_LED_PIN);
Gpio_Init(STK_LED_PORT, STK_LED_PIN, &stcGpioCfg);
}
void key_init(void)
{
stc_gpio_cfg_t stcGpioCfg;
Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);
stcGpioCfg.enDir = GpioDirIn;
stcGpioCfg.enDrv = GpioDrvL;
stcGpioCfg.enPu = GpioPuDisable;
stcGpioCfg.enPd = GpioPdDisable;
Gpio_Init(STK_USER_PORT, STK_USER_PIN, &stcGpioCfg);
Gpio_EnableIrq(STK_USER_PORT, STK_USER_PIN, GpioIrqFalling);
EnableNvic(PORTA_IRQn, IrqLevel3, TRUE);
}
void PortA_IRQHandler(void)
{
if(TRUE == Gpio_GetIrqStatus(STK_USER_PORT, STK_USER_PIN))
{
if(Gpio_GetInputIO(STK_USER_PORT, STK_USER_PIN) == FALSE)
{
Gpio_WriteOutputIO(STK_LED_PORT, STK_LED_PIN,!Gpio_ReadOutputIO(STK_LED_PORT, STK_LED_PIN));
}
Gpio_ClearIrq(STK_USER_PORT, STK_USER_PIN);
}
}
int32_t main(void)
{
led_init();
key_init();
while(1)
{
;
}
}
运行效果
3.3定时器测试
HC32L196有4个通用定时器、2个低功耗定时器和3个高级定时器,还有一个PCA(可编程计数器阵列)也可作为定时器使用
选用Timer0做一个每1ms触发一次的定时器,用它来控制LED闪烁,按键调节闪烁速度
首先设置一下系统时钟,不做任何更改时用的是内部时4M时钟
板子上有个32M的外部晶振,接下来使用它作为时钟源
void xth_init()
{
//当使用的时钟源HCLK大于24M:设置FLASH 读等待周期为1 cycle
Flash_WaitCycle(FlashWaitCycle1);
Sysctrl_SetXTHFreq(SysctrlXthFreq24_32MHz);
Sysctrl_XTHDriverCfg(SysctrlXtalDriver3);
Sysctrl_SetXTHStableTime(SysctrlXthStableCycle16384);
Sysctrl_ClkSourceEnable(SysctrlClkXTH, TRUE);
delay1ms(10);
Sysctrl_SysClkSwitch(SysctrlClkXTH);
}
设置timer0参数,使用16位重载计数器模式,计数器会从设定的值开始计数到0xFFFF后产生中断
void timer0_init()
{
stc_bt_mode0_cfg_t stcBtBaseCfg;
DDL_ZERO_STRUCT(stcBtBaseCfg);
Sysctrl_SetPeripheralGate(SysctrlPeripheralBaseTim, TRUE); //Base Timer外设时钟使能
stcBtBaseCfg.enWorkMode = BtWorkMode0; //定时器模式
stcBtBaseCfg.enCT = BtTimer; //定时器功能,计数时钟为内部PCLK
stcBtBaseCfg.enPRS = BtPCLKDiv32; //PCLK/32 32M/32=1M
stcBtBaseCfg.enCntMode = Bt16bitArrMode; //自动重载16位计数器/定时器
stcBtBaseCfg.bEnTog = FALSE;
stcBtBaseCfg.bEnGate = FALSE;
Bt_Mode0_Init(TIM0, &stcBtBaseCfg); //TIM0 的模式0功能初始化
Bt_M0_ARRSet(TIM0, 0x10000-1000); //设置重载值
Bt_M0_Cnt16Set(TIM0, 0x10000-1000); //设置计数初值
Bt_ClearIntFlag(TIM0,BtUevIrq); //清中断标志
Bt_Mode0_EnableIrq(TIM0); //使能TIM0中断
EnableNvic(TIM0_IRQn, IrqLevel3, TRUE); //TIM0中断使能
Bt_M0_Run(TIM0); //TIM0 运行。
}
中断处理
uint16_t timecount = 0;
uint16_t timecount_max = 500;
void Tim0_IRQHandler(void)
{
//Timer0 模式0 溢出中断
if(TRUE == Bt_GetIntFlag(TIM0, BtUevIrq))
{
if(timecount < timecount_max)
timecount += 1;
else
{
timecount = 0;
Gpio_WriteOutputIO(STK_LED_PORT, STK_LED_PIN,!Gpio_ReadOutputIO(STK_LED_PORT, STK_LED_PIN));
}
Bt_ClearIntFlag(TIM0,BtUevIrq); //中断标志清零
}
}
void PortA_IRQHandler(void)
{
if(TRUE == Gpio_GetIrqStatus(STK_USER_PORT, STK_USER_PIN))
{
if(Gpio_GetInputIO(STK_USER_PORT, STK_USER_PIN) == FALSE)
{
if(timecount_max == 500)
timecount_max = 200;
else if(timecount_max == 200)
timecount_max = 1000;
else
timecount_max = 500;
}
Gpio_ClearIrq(STK_USER_PORT, STK_USER_PIN);
}
}
main函数
int32_t main(void)
{
xth_init();
//时钟分频设置
Sysctrl_SetHCLKDiv(SysctrlHclkDiv1);
Sysctrl_SetPCLKDiv(SysctrlPclkDiv1);
led_init();
key_init();
timer0_init();
while(1)
{
;
}
}
运行效果
3.4PWM输出测试
4个通用定时器3个高级定时器和PCA都有输出PWM的能力,然而开发板上LED所接的PD14这个IO并不能输出PWM
因此外接2个LED来测试PWM,选用PA00和PA01作为Timer1的两个PWM通道输出
初始化为PWM输出
void pwm_led_init()
{
stc_gpio_cfg_t stcLEDPort;
DDL_ZERO_STRUCT(stcLEDPort);
Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);
stcLEDPort.enDir = GpioDirOut;
Gpio_Init(GpioPortA, GpioPin0, &stcLEDPort);
Gpio_SetAfMode(GpioPortA,GpioPin0,GpioAf5); //PA00设置为TIM1_CHA
Gpio_Init(GpioPortA, GpioPin1, &stcLEDPort);
Gpio_SetAfMode(GpioPortA,GpioPin1,GpioAf5); //PA01设置为TIM1_CHB
}
接下来看看手册上对于定时器PWM模式的描述
这里选用PWM2上计数,锯齿波单点比较,时钟32分频计数周期1000
void pwm_timer1_init()
{
stc_bt_mode23_cfg_t stcBtBaseCfg;
stc_bt_m23_compare_cfg_t stcBtPortCmpCfg;
DDL_ZERO_STRUCT(stcBtBaseCfg);
DDL_ZERO_STRUCT(stcBtPortCmpCfg);
Sysctrl_SetPeripheralGate(SysctrlPeripheralBaseTim, TRUE);
stcBtBaseCfg.enWorkMode = BtWorkMode2; //锯齿波模式
stcBtBaseCfg.enCT = BtTimer; //定时器功能,计数时钟为内部PCLK
stcBtBaseCfg.enPRS = BtPCLKDiv32; //PCLK
stcBtBaseCfg.enCntDir = BtCntUp; //向上计数,在三角波模式时只读
stcBtBaseCfg.enPWMTypeSel = BtIndependentPWM; //独立输出PWM
stcBtBaseCfg.enPWM2sSel = BtSinglePointCmp; //单点比较功能
stcBtBaseCfg.bOneShot = FALSE; //循环计数
stcBtBaseCfg.bURSSel = FALSE; //上下溢更新
Bt_Mode23_Init(TIM1, &stcBtBaseCfg); //TIM0 的模式23功能初始化
Bt_M23_ARRSet(TIM1, 1000-1, TRUE); //设置重载值,并使能缓存
Bt_M23_CCR_Set(TIM1, BtCCR0A, 0); //设置比较值A
Bt_M23_CCR_Set(TIM1, BtCCR0B, 0); //设置比较值B
stcBtPortCmpCfg.enCH0ACmpCtrl = BtPWMMode2; //OCREFA输出控制OCMA:PWM模式2
stcBtPortCmpCfg.enCH0APolarity = BtPortPositive; //正常输出
stcBtPortCmpCfg.bCh0ACmpBufEn = TRUE; //A通道缓存控制
stcBtPortCmpCfg.enCh0ACmpIntSel = BtCmpIntNone; //A通道比较控制:无
stcBtPortCmpCfg.enCH0BCmpCtrl = BtPWMMode2; //OCREFB输出控制OCMB:PWM模式2
stcBtPortCmpCfg.enCH0BPolarity = BtPortPositive; //正常输出
stcBtPortCmpCfg.bCH0BCmpBufEn = TRUE; //B通道缓存控制使能
stcBtPortCmpCfg.enCH0BCmpIntSel = BtCmpIntNone; //B通道比较控制:无
Bt_M23_PortOutput_Cfg(TIM1, &stcBtPortCmpCfg); //比较输出端口配置
//事件更新周期设置,0表示锯齿波每个周期更新一次,每+1代表延迟1个周期
Bt_M23_SetValidPeriod(TIM1,0); //间隔周期设置
Bt_M23_Cnt16Set(TIM1, 0); //设置计数初值
Bt_M23_EnPWM_Output(TIM1, TRUE, FALSE); //TIM0 端口输出使能
Bt_M23_Run(TIM1);
}
两个LED交替呼吸
int32_t main(void)
{
uint16_t pwm = 0;
xth_init();
//时钟分频设置
Sysctrl_SetHCLKDiv(SysctrlHclkDiv1);
Sysctrl_SetPCLKDiv(SysctrlPclkDiv1);
led_init();
key_init();
timer0_init();
pwm_led_init();
pwm_timer1_init();
while(1)
{
if(pwm<2000)
pwm += 1;
else
pwm = 0;
if(pwm < 1000)
{
Bt_M23_CCR_Set(TIM1, BtCCR0A, pwm);
Bt_M23_CCR_Set(TIM1, BtCCR0B, 1000-pwm);
}
else
{
Bt_M23_CCR_Set(TIM1, BtCCR0A, 2000 - pwm);
Bt_M23_CCR_Set(TIM1, BtCCR0B, pwm - 1000);
}
delay1ms(2);
}
}
运行效果
|