前言
TLSR825X系列的IO配置与常用MCU相差无几,应用到低功耗设计的话差异比较大,825X系列的停止模式下功耗相对较高(suspend模式),要做到较低功耗需要类似于ST单片机进入待机Standby模式(deepsleep模式),仅通过wakeup引脚或时钟来唤醒,唤醒后系统重新通过启动文件来跳转到main入口
本章讲下GPIO的常用配置与IO中断、IO唤醒功能,如有异议,欢迎留言指正
功能简介
- 8258系列共有5组36个GPIO,
PA0-PA7、PB0-PB7、PC0-PC7、PD0-PD7、PE0-PE3
- PA0~PD7可以用作通用IO口,其中上下拉寄存器为模拟寄存器控制,该寄存器可以在deepsleep模式中保持
- PA7默认用作SWS功能(调试下载),必须内部上拉
- PE0~PE3被用于内部flash的SPI读写通信引脚,用户不允许使用
- PA5~PA6可复用DM和DP用作USB功能
不同的型号封装和IO管脚不同,以实际型号为准
具体引脚复用表可查看数据手册第7章7.1GPIO章节
寄存器配置
寄存器
- GPIO寄存器偏移地址为0x580~0x59c,对应PA0~PE3中的输入、输出、上下拉、强弱输出、中断等配置
#define reg_gpio_pa_in REG_ADDR8(0x580)
#define reg_gpio_pa_ie REG_ADDR8(0x581)
#define reg_gpio_pa_oen REG_ADDR8(0x582)
#define reg_gpio_pa_out REG_ADDR8(0x583)
#define reg_gpio_pa_pol REG_ADDR8(0x584)
#define reg_gpio_pa_ds REG_ADDR8(0x585)
#define reg_gpio_pa_gpio REG_ADDR8(0x586)
#define reg_gpio_pa_irq_en REG_ADDR8(0x587)
- - - - - - - - -
常用接口
- 功能配置:主要是配置成普通IO还是复用高级功能(uart、spi、iic等)
void gpio_set_func(GPIO_PinTypeDef pin, GPIO_FuncTypeDef func);
- 输入使能
void gpio_set_input_en(GPIO_PinTypeDef pin, unsigned int value);
- 读取管脚电平
unsigned int gpio_read(GPIO_PinTypeDef pin);
- 输出使能
int gpio_is_output_en(GPIO_PinTypeDef pin);
- 设置管脚输出电平
void gpio_write(GPIO_PinTypeDef pin, unsigned int value)
- 设置上下拉
void gpio_setup_up_down_resistor(GPIO_PinTypeDef gpio, GPIO_PullTypeDef up_down)
输出实例
gpio_set_func(GPIO_PA4, AS_GPIO); // 数字gpio
gpio_set_output_en(GPIO_PA4, 1);//使能输出
gpio_set_input_en(GPIO_PA4, 0); //禁用输入
gpio_write(GPIO_PA4, 0); //输出低
gpio_write(GPIO_PA4, 1); //输出高
输入实例
gpio_set_func(GPIO_PA4, AS_GPIO); // 数字gpio
gpio_set_output_en(GPIO_PA4, 0);//禁用输出
gpio_set_input_en(GPIO_PA4, 1); //使能输入
gpio_setup_up_down_resistor(GPIO_PA4, PM_PIN_PULLUP_10K); //上拉10K
if(gpio_read(GPIO_PA4)){ //读取电平 H
;
}else{//L
;
}
IO中断
中断响应会映射到统一入口,通过中断标志来判断是哪个中断源;
IO中断源获取到后,如果单组中断源存在多个IO使能的中断,可以进一步判断IO电平来决定是哪个IO触发了中断
- IO中断最大支持三组,分别对应
IRQ_GPIO
、IRQ_GPIO_RISC0
、IRQ_GPIO_RISC1
,实际需要根据应用来合理分配,建议一组设置一个IO中断(相互独立),如果同组使能多个GPIO需要在入口中断中进行判断
代码实例
- 参考
app_gpio_irq.c
的例程代码,分别配置了三组独立的GPIO中断
#define GPIO_TEST_PIN1 GPIO_PD0
#define GPIO_TEST_PIN2 GPIO_PD1
#define GPIO_TEST_PIN3 GPIO_PD2
void app_gpio_irq_test_init(void)
{
//IRQ_GPIO 下降沿触发
/***step1. 配置为输入*/
gpio_set_func(GPIO_TEST_PIN1, AS_GPIO); //enable GPIO func
gpio_set_input_en(GPIO_TEST_PIN1, 1); //enable input
gpio_set_output_en(GPIO_TEST_PIN1, 0); //disable output
/***step2. 上拉 下降沿中断 ***/
gpio_setup_up_down_resistor(GPIO_TEST_PIN1, PM_PIN_PULLUP_10K); //上拉10K open pull up resistor
gpio_set_interrupt_pol(GPIO_TEST_PIN1, pol_falling); //下降沿 falling edge
/***step3. 设置IRQ中断 ***/
reg_irq_src = FLD_IRQ_GPIO_EN; //清中断标志
reg_irq_mask |= FLD_IRQ_GPIO_EN;//使能irq中断
gpio_en_interrupt(GPIO_TEST_PIN1, 1);
//IRQ_GPIO_RISC0 下降沿触发
/***step1. 配置为输入********/
gpio_set_func(GPIO_TEST_PIN2, AS_GPIO); //enable GPIO func
gpio_set_input_en(GPIO_TEST_PIN2, 1); //enable input
gpio_set_output_en(GPIO_TEST_PIN2, 0); //disable output
/***step2. 上拉 下降沿中断 ***/
gpio_setup_up_down_resistor(GPIO_TEST_PIN2, PM_PIN_PULLUP_10K); //上拉10K open pull up resistor
gpio_set_interrupt_pol(GPIO_TEST_PIN2, pol_falling); //下降沿 falling edge
/***step3. 设置IRQ中断 ***/
reg_irq_src = FLD_IRQ_GPIO_RISC0_EN; //清中断标志
reg_irq_mask |= FLD_IRQ_GPIO_RISC0_EN;//使能irq_risc0中断
gpio_en_interrupt_risc0(GPIO_TEST_PIN2, 1);
//IRQ_GPIO_RISC1 上升沿触发
/***step1. 配置为输入********/
gpio_set_func(GPIO_TEST_PIN3, AS_GPIO); //enable GPIO func
gpio_set_input_en(GPIO_TEST_PIN3, 1); //enable input
gpio_set_output_en(GPIO_TEST_PIN3, 0); //disable output
/***step2. set the polarity and open pullup ***/
gpio_setup_up_down_resistor(GPIO_TEST_PIN3, PM_PIN_PULLDOWN_100K); //下拉100K open pull down resistor
gpio_set_interrupt_pol(GPIO_TEST_PIN3, pol_rising); //上升沿 rising edge
//***step3. 设置IRQ中断 ***/
reg_irq_src = FLD_IRQ_GPIO_RISC1_EN; //清中断标志
reg_irq_mask |= FLD_IRQ_GPIO_RISC1_EN;//使能irq_risc1中断
gpio_en_interrupt_risc1(GPIO_TEST_PIN3, 1);
irq_enable(); //开启总中断
}
- 函数irq_handler为中断入口,通过判断中断标志来进行相对应处理
//所有中断入口
_attribute_ram_code_ void irq_handler(void)
{
irq_blt_sdk_handler (); //ble中断处理
/************ test1 irq ***************/
if(reg_irq_src & FLD_IRQ_GPIO_EN)
{
reg_irq_src = FLD_IRQ_GPIO_EN; // clear irq_gpio irq flag
printf("test1 irq !\n");
}
/************* test2 irq risc0 *************/
if(reg_irq_src & FLD_IRQ_GPIO_RISC0_EN)
{
reg_irq_src = FLD_IRQ_GPIO_RISC0_EN; // clear irq_gpio irq flag
printf("test2 irq risc0 !\n");
}
/************* test3 irq risc1 *************/
if(reg_irq_src & FLD_IRQ_GPIO_RISC1_EN)
{
reg_irq_src = FLD_IRQ_GPIO_RISC1_EN; // clear irq_gpio irq flag
printf("test3 irq risc1!\n");
}
}
IO唤醒
系统休眠状态下是无法直接通过IO中断唤醒的,唤醒机制仅支持Timer与PAD;休眠前可通过配置使能PAD的唤醒电平来触发
代码实例
参考test_low_power.c
文件代码,实际开发可同时使能多个IO进行指定的电平唤醒;
DEEPSLEEP模式
- 调用cpu_sleep_wakeup进入深度休眠后,管脚PB6产生低电平脉冲系统会立即重启
gpio_setup_up_down_resistor(GPIO_PB6, PM_PIN_PULLUP_10K); //上拉10K
cpu_set_gpio_wakeup(GPIO_PB6, Level_Low,1); //低电平唤醒
while(1)
{
cpu_sleep_wakeup(DEEPSLEEP_MODE, PM_WAKEUP_PAD, 0); //进入deepsleep,开启PAD唤醒
}
SUSPEND模式
- 调用cpu_sleep_wakeup进入停止模式后,管脚PB6产生低电平脉,系统唤醒并执行打印
gpio_setup_up_down_resistor(GPIO_PB6, PM_PIN_PULLUP_10K); //上拉10K
cpu_set_gpio_wakeup(GPIO_PB6, Level_Low,1); //低电平唤醒
while(1)
{
cpu_sleep_wakeup(SUSPEND_MODE, PM_WAKEUP_PAD, 0); //进入suspend,开启PAD唤醒
printf("suspend mode wakeup\n");
break;
}
IO唤醒常见问题
- 休眠前如果唤醒管脚电平处于有效状态,调用
cpu_sleep_wakeup
进入DEEPSLEEP_MODE会立即重启; 而配置进入SUSPEND_MODE或DEEPSLEEP_RETENTION_MODE并立即返回错误结果状态 - deepsleep深度休眠模式下为了保持IO的输出能力,需要通过上下拉来实现,但是上拉1M的电平会比VCC(3.3V)略低,建议配置为10K
- 上拉10K的控制中不要使用PC0~PC7,在deepsleep retention wakeup时会有短时间的抖动,产生毛刺