UCM108E二次开发-外部中断&&定时器&&PWM使用
这节内容主要是介绍UCM108E的片内外设使用情况,同样也是用官方提供的二次开发demo来做进一步的开发。
GPIO外部中断
UC8188的GPIO使用起来比较简单,官方也提供了比较全面的函数库。GPIO控制器可以驱动或者获取信号/数据。通过相应的寄存器,可以定义I/O的功能、方向、状态以及中断。
GPIO的主要特点
- 受控I/O个数微29
- I/O输入/输出可配
- 输入/输出状态:上拉、浮空
- 所有I/O均可作为中断源,触发方式可配:低电平、高电平、上升沿、下降沿
- I/O功能多重复用
同时也需要指出,GPIO的功能并没有类似于STM32类型的MCU多,比如IO都不支持下拉功能,所以在进行硬件设计的时候,需要详细参考GPIO特性,在硬件设计上面不足或者规避这些不足。
编写示例函数
操作GPIO为外部中断触发模式,主要是由以下几个步骤:
- 将IO设置为输入模式-->设置中断触发类型-->使能中断-->使能全局中断
static void trigger_gpio_init(void)
{
gpio_set_pin_direction(UC_GPIO, TRIGGER_GPS_PPS_GPIO, GPIO_DIR_IN);
gpio_set_irq_type(UC_GPIO, TRIGGER_GPS_PPS_GPIO, GPIO_IT_RISE_EDGE);
gpio_set_irq_en(UC_GPIO, TRIGGER_GPS_PPS_GPIO, 1);
gpio_int_enable();
printf("trigger gpio pin init done.\r\n");
}
- 编写中断服务函数-->获取中断状态-->清除中断标记位
void gpio_handler(void)
{
uint32_t pin, status;
status = gpio_get_irq_status(UC_GPIO);
gpio_int_clear_pending();
for (pin = GPIO_PIN_0; pin <= GPIO_PIN_29; pin++) {
if (status & (1 << pin)) {
printf("GPIO %d INTERRPUT\r\n", pin);
}
}
}
注意:
- 中断标记位一定要在中断服务函数里面清除,不然下次无法继续触发中断
- 中断服务函数的入口是
gpio_handler
,在UC8188的单独示例中,中断服务函数是ISR_GPIO
- 在本示例工程的
libraries/HAL_Drivers/drv_gpio.c
中也有gpio_handler
需要将此先屏蔽,然后自己实现这个中断服务函数
烧录运行
将上述代码编译完成后,烧录到开发板运行,然后手动触发GPIO25,可以看到中断打印
Time: 2022/6/18 19:53:38.00
POS: ST 10, tLon 1.899968, tLat 0.596802, fAlt: 38.554977
Time: 2022/6/18 19:53:39.00
POS: ST 10, tLon 1.899968, tLat 0.596802, fAlt: 38.554977
Time: 2022/6/18 19:53:40.00
POS: ST 10, tLon 1.899968, tLat 0.596802, fAlt: 38.554977
Time: 2022/6/18 19:53:41.00
POS: ST 10, tLon 1.899968, tLat 0.596802, fAlt: 38.554977
GPIO 25 INTERRPUT
Time: 2022/6/18 19:53:42.00
POS: ST 10, tLon 1.899968, tLat 0.596802, fAlt: 38.554977
GPIO 25 INTERRPUT
Time: 2022/6/18 19:53:43.00
POS: ST 10, tLon 1.899968, tLat 0.596802, fAlt: 38.554977
GPIO 25 INTERRPUT
Time: 2022/6/18 19:53:44.00
POS: ST 10, tLon 1.899968, tLat 0.596802, fAlt: 38.554977
GPIO 25 INTERRPUT
定时器
UC8188有两个独立的定时器,设计的也比较简单。
主要特点
- 32位加计数器
- 3位可编程(可以实时修改)预分频器,定时器时钟=系统时钟/(预分频系数+1)
- 两种中断或事件类型可选择,即计数器溢出和比较结果相等触发
编写示例函数
我主要是用定时器来做一个精确计时,所以只用了定时中断功能。在确定定时时间之前,需要确定系统主频,分频系数等关键指标。通过研读代码我们可知在启动函数那里,配置了系统主频
addi x11, x0, 70
jal uart_set_cfg
- 配置分频系数和计数值-->工作在计数模式-->使能计数溢出中断-->使能总中断
cfg.pre = 7;
cfg.cnt = 0xffffffffU - ((SYSTEM_CLK>>8)/(cfg.pre+1))*1;
cfg.cmp = 0;
timer_init(UC_TIMER1, &cfg);
timer_enable(UC_TIMER1);
timer_int_enable(UC_TIMER1, TIMER_IT_OVF);
int_enable();
- 编写中断服务函数-->清除中断-->重新装入计数值-->做自己的业务
static int cnt = 0;
timer_int_clear_pending(UC_TIMER1, TIMER_IT_OVF);
timer_set_count(UC_TIMER1, 0xffffffffU - (SYSTEM_CLK>>11));
cnt++;
if(cnt%0xff == 0){
printf("timer1 overflow interrupt!!!\r\n");
}
烧录运行
编译上述代码,在串口工具中应该会每隔1s打印一下
timer1 overflow interrupt!!!
timer1 overflow interrupt!!!
timer1 overflow interrupt!!!
timer1 overflow interrupt!!!
timer1 overflow interrupt!!!
timer1 overflow interrupt!!!
timer1 overflow interrupt!!!
timer1 overflow interrupt!!!
timer1 overflow interrupt!!!
timer1 overflow interrupt!!!
timer1 overflow interrupt!!!
timer1 overflow interrupt!!!
timer1 overflow interrupt!!!
PWM
PWM外设相对来说也是比较简单。
特点
编写示例函数
我的需求是,可以调节一个灯的亮度,并且可以周期的控制灯的闪烁逻辑。
- 确定PWM外接的GPIO管脚,使能对应的PWM--> 设置周期-->设置占空比
pwm_enable(UC_PWM3);
pwm_set_period(UC_PWM3, 1000);
pwm_set_duty(UC_PWM3, 300);
- 周期闪烁
while(1)
{
printf("cnt : %d\r\n", cnt++);
pwm_set_duty(UC_PWM3, 30);
delay_ms(500);
pwm_set_duty(UC_PWM3, 0);
delay_ms(500);
}
注意:这里我尝试使用pwm_enable
和pwm_disable
来控制灯的亮灭,但是发现pwm_disable
后GPIO默认是高电平,不符合我的设计需求,故用占空比为0表示灯灭,由于PWM外设过于简单,在做硬件设计的时候需要注意这里。
烧录运行
将上述代码烧录到开发板,可以看到1s一次打印cnt值,同时LED灯也在闪烁,改变duty可以改变led亮度
pwm test
cnt : 0
cnt : 1
cnt : 2
cnt : 3
cnt : 4
cnt : 5
总结
UC8188的外设使用起来很简单,也很实用。但是在做相关硬件设计时,需要注意。