fr8008gp低功耗还是非常难调的,自己搞了半个月才搞定,原厂和代理也是拖拖拉拉,烦不胜烦!
主要在于PMU的配置,这里先弄一个非常简单的工程,只有屏幕一个外设,代码启动后先显示一个界面,5S后直接睡眠:
..................................................
ota_gatt_add_service();
//touchpad_init_pin(true);
gc9c01_init();
//user_time_init();
system_sleep_enable();
.................................................
其中gc9c01_init函数有如下语句:
void gc9c01_init(void)
{
gc9c01_init_io();
printf("gc9c01_init_io finish\r\n");
gc9c01_init_spi();
gc9c01_init_dma();
LCD_RELEASE_RESET();
co_delay_100us(1200);
LCD_SET_RESET();
co_delay_100us(3500);
LCD_RELEASE_RESET();
co_delay_100us(1200);
.............................................
SPI_CMD(0x29);
co_delay_100us (1200);
printf("gc9c01 lcd init ok\r\n");
//gc9c01_oled_clear(0, 0, GC9C01_LCD_Width, GC9C01_LCD_High,COLOR_RED);//XÖá±ØÐëÊÇ12µÄ±¶Êý
gc9c01_oled_show_rwb(0, 0, GC9C01_LCD_Width, GC9C01_LCD_High,(uint8_t *)image_rbw);
printf("oled_clear end\r\n");
// while(1);
co_delay_100us (5*10000);//²âÊÔ,˯ÃßÇ°ÏÈ¿´Ò»ÏÂÆÁÄ»
LCD_DISABLE_BACKLIGHT();
GC9C01_EnterSleep();
}
对于低功耗user_entry_before_sleep_imp和user_entry_after_sleep_imp这两个函数非常关键,前者为进入睡眠的函数,后者为推出睡眠的函数,fr8008gp要求把需要控制的管脚在进入睡眠钱配置为PMU模式(否则进入睡眠电压不可控),在退出睡眠的时候要重新初始化各个需要的模块:
__attribute__((section("ram_code"))) void user_entry_before_sleep_imp(void)
{
//pmu_calibration_stop();
uart_putc_noint_no_wait(UART0, 's');
co_delay_100us(1);
pmu_set_pin_to_PMU(GPIO_PORT_A, GPIO_BIT_1);
gc9c01_uninit_io();
}
__attribute__((section("ram_code"))) void user_entry_after_sleep_imp(void)
{
system_set_port_mux(GPIO_PORT_A, GPIO_BIT_1, PORTA1_FUNC_UART0_TXD);
pmu_set_pin_to_CPU(GPIO_PORT_A, GPIO_PIN_1);
uart_init(UART0, LOG_UART_BAUDRATE_CFG);
fr_uart_enableIrq(UART0, Uart_irq_erbfi);
uart_putc_noint_no_wait(UART0, 'w');
NVIC_EnableIRQ(PMU_IRQn);
pmu_calibration_start(PMU_CALI_SEL_RCLFOSC, LP_RC_CALIB_CNT);
}
既然和IO口有关系这里就涉及到屏幕的管脚的配置,屏幕管脚初始化和睡眠前的去初始化如下:
#define LCD_CS_PORT GPIO_D
#define LCD_CS_PIN GPIO_PIN_1
#define LCD_DC_PORT GPIO_E
#define LCD_DC_PIN GPIO_PIN_0
#define LCD_REST_PORT GPIO_E
#define LCD_REST_PIN GPIO_PIN_1
#define LCD_BACKLIGHT_PORT GPIO_B
#define LCD_BACKLIGHT_PIN GPIO_PIN_2
#define LCD_SPI_PORT GPIO_D
#define LCD_SPI_PIN (GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_0)
#define LCD_RELEASE_CS() gpio_write_pin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_SET)
#define LCD_SET_CS() gpio_write_pin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_CLEAR)
#define LCD_CLEAR_DC() gpio_write_pin(LCD_DC_PORT, LCD_DC_PIN, GPIO_PIN_CLEAR)
#define LCD_SET_DC() gpio_write_pin(LCD_DC_PORT, LCD_DC_PIN, GPIO_PIN_SET)
#define LCD_RELEASE_RESET() pmu_set_gpio_value(LCD_REST_PORT,LCD_REST_PIN,GPIO_PIN_SET)
#define LCD_SET_RESET() pmu_set_gpio_value(LCD_REST_PORT,LCD_REST_PIN,GPIO_PIN_CLEAR)
#define LCD_ENABLE_BACKLIGHT() pmu_set_gpio_value((enum system_port_t)LCD_BACKLIGHT_PORT,LCD_BACKLIGHT_PIN,GPIO_PIN_SET)
#define LCD_DISABLE_BACKLIGHT() pmu_set_gpio_value((enum system_port_t)LCD_BACKLIGHT_PORT,LCD_BACKLIGHT_PIN,GPIO_PIN_CLEAR)
static DMA_LLI_InitTypeDef Gc9c01_Link_Channel[20];
static DMA_HandleTypeDef gc9c01_DMA_Channel;
static dma_LinkParameter_t Gc9c01_LinkParameter;
static volatile bool dma_transfer_done = true;
SPI_HandleTypeDef GC9C01_SPI_Handle;
static void (*dma_trans_done_callback)(void) = NULL;
static void gc9c01_init_io(void)
{
GPIO_InitTypeDef GPIO_Handle;
// backlight
pmu_set_pin_to_PMU((enum system_port_t)LCD_BACKLIGHT_PORT, LCD_BACKLIGHT_PIN);
pmu_set_pin_dir((enum system_port_t)LCD_BACKLIGHT_PORT, LCD_BACKLIGHT_PIN, GPIO_DIR_OUT);
// reset
pmu_set_pin_to_PMU((enum system_port_t)LCD_REST_PORT, LCD_REST_PIN);
pmu_set_pin_dir((enum system_port_t)LCD_REST_PORT, LCD_REST_PIN, GPIO_DIR_OUT);
// DC
GPIO_Handle.Pin = LCD_DC_PIN;
GPIO_Handle.Mode = GPIO_MODE_OUTPUT_PP;
gpio_init(LCD_DC_PORT, &GPIO_Handle);
/* CS */
GPIO_Handle.Pin = LCD_CS_PIN;
GPIO_Handle.Mode = GPIO_MODE_OUTPUT_PP;
gpio_init(LCD_CS_PORT, &GPIO_Handle);
GPIO_Handle.Pin = LCD_SPI_PIN;
GPIO_Handle.Mode = GPIO_MODE_AF_PP;
GPIO_Handle.Pull = GPIO_PULLDOWN;
GPIO_Handle.Alternate = GPIO_FUNCTION_2;
gpio_init(LCD_SPI_PORT, &GPIO_Handle);
LCD_RELEASE_CS();
LCD_SET_DC();
LCD_ENABLE_BACKLIGHT();
#ifdef DEVICE_LCD_PWM
init_lcd_pwm();
#endif
}
void gc9c01_uninit_io(void)
{
pmu_set_pin_to_PMU((enum system_port_t)LCD_DC_PORT, LCD_DC_PIN);
pmu_set_pin_dir((enum system_port_t)LCD_DC_PORT, LCD_DC_PIN, GPIO_DIR_IN);
pmu_set_pin_to_PMU((enum system_port_t)LCD_CS_PORT, LCD_CS_PIN);
pmu_set_pin_dir((enum system_port_t)LCD_CS_PORT, LCD_CS_PIN, GPIO_DIR_IN);
pmu_set_pin_to_PMU((enum system_port_t)LCD_SPI_PORT, LCD_SPI_PIN);
pmu_set_pin_dir((enum system_port_t)LCD_SPI_PORT, LCD_SPI_PIN, GPIO_DIR_IN);
}
经过这样的配置在睡眠的时候低电流为31UA,平均电流65UA:
其实之前我能够达到10UA的,但是现在不知道为啥总是不行了:
电流波形数据和代码固件请从如下地址下载:https://download.csdn.net/download/cheng___yi/87455507
重新开启SPI
上面是睡眠后再也不开启SPI进行图片刷图的情况,如果唤醒后要刷图还要重新把SPI等管脚从PMU模式切换成SPI等模式:
void gc9c01_reinit_io(void)
{
pmu_set_pin_to_CPU ((enum system_port_t)LCD_DC_PORT, LCD_DC_PIN);
pmu_set_pin_to_CPU ((enum system_port_t)LCD_CS_PORT, LCD_CS_PIN);
pmu_set_pin_to_CPU ((enum system_port_t)LCD_SPI_PORT, LCD_SPI_PIN);
gc9c01_init_io();
printf("gc9c01_init_io finish\r\n");
gc9c01_init_spi();
gc9c01_init_dma();
lcd_gpio_state=false;
}
//开机初始化
void gc9c01_init(void)
{
gc9c01_reinit_io();
........................................
}
这里用RTC的闹钟功能实现一个5S定时器,然后设置标志位等待主循环切图:
void proj_init(void)
{
......................................
gc9c01_init();
rtc_init();
rtc_CountUpdate(0);
rtc_AlarmConfig(AlARM_A,0,0,5);
NVIC_EnableIRQ(PMU_IRQn);
system_sleep_enable();
}
void rtc_alarma_handle(void)
{
system_sleep_disable();
if(time0_5s_timeout==false)
{
time0_5s_timeout=true;
time0_5s_cnt++;
}
}
主循环中先配置管脚在切图:
__attribute__((section("ram_code"))) void main_loop(void)
{
while(1)
{
.................
//ÂÖ²¥Í¼Æ¬
if((time0_5s_timeout==true) && (first_loop==true))
{
printf("time0_5s_timeout %d\r\n",time0_5s_cnt);
gc9c01_reinit_io();
switch (time0_5s_cnt)
{
............................
经过这样的配置基本可以实现唤醒后切图的效果!
图片5S切换效果
PD5的bug
昨晚调功耗到1点半,莫名其妙搞了好久才知道是因为PD5的问题,我PMU设置了PD5就是不能够正常亮屏,最终放弃了,今天找到原厂技术支持,原厂这样说:
初始化pd5的时候加个pmu_set_pin_mux_cfg_sel(GPIO_PORT_D, PMU_GPIO_MUX_CFG_PK);//设置pmu口一组io配置为gpio功能
这个问题主要是Pmu操作下,pd5没有单独的使能输出功能,和pd4是共用的使能输出。
看来还是得多沟通呀,经过上面的设置后LVGL工程的电流情况如下:
电流还是没有达到预期,这个还要继续调试!
设置管脚为PMU模式
fr8008gp的GPIO在睡眠后就被设置成关闭状态(浮空输入),但是对于我们屏幕的RST和背光管脚要在睡眠的时候依旧维持状态不变,这个就要把管脚设置为PMU模式:
#define LCD_REST_PORT GPIO_PORT_E
#define LCD_REST_PIN GPIO_BIT_1
#define LCD_BACKLIGHT_PORT GPIO_PORT_B
#define LCD_BACKLIGHT_PIN GPIO_BIT_2
#define LCD_RELEASE_RESET() pmu_set_gpio_value(LCD_REST_PORT,(1 << LCD_REST_PIN),GPIO_PIN_SET)
#define LCD_SET_RESET() pmu_set_gpio_value(LCD_REST_PORT,(1 << LCD_REST_PIN),GPIO_PIN_CLEAR)
#define LCD_ENABLE_BACKLIGHT() pmu_set_gpio_value(LCD_BACKLIGHT_PORT,(1 << LCD_BACKLIGHT_PIN),GPIO_PIN_SET)
#define LCD_DISABLE_BACKLIGHT() pmu_set_gpio_value(LCD_BACKLIGHT_PORT,(1 << LCD_BACKLIGHT_PIN),GPIO_PIN_CLEAR)
static void gc9c01_init_io(void)
{
------------------------------
// backlight
pmu_set_pin_to_PMU(LCD_BACKLIGHT_PORT, (1 << LCD_BACKLIGHT_PIN));
pmu_set_pin_mux(LCD_BACKLIGHT_PORT, LCD_BACKLIGHT_PIN, PMU_PIN_FUNC_GPIO);
pmu_set_pin_dir(LCD_BACKLIGHT_PORT, (1 << LCD_BACKLIGHT_PIN), GPIO_DIR_OUT);
pmu_set_pin_pull(LCD_BACKLIGHT_PORT,(1 << LCD_BACKLIGHT_PIN),GPIO_PULL_NONE);
// reset
pmu_set_pin_to_PMU(LCD_REST_PORT, (1 << LCD_REST_PIN));
pmu_set_pin_mux(LCD_REST_PORT, LCD_REST_PIN, PMU_PIN_FUNC_GPIO);
pmu_set_pin_dir(LCD_REST_PORT, (1 << LCD_REST_PIN), GPIO_DIR_OUT);
pmu_set_pin_pull(LCD_REST_PORT,(1 << LCD_REST_PIN),GPIO_PULL_NONE);
LCD_DISABLE_BACKLIGHT();
----------------------------------
}
注意PMU只能够用于控制变化比较慢的管脚.对于速度比较快的管脚不合适!这里经过设置不同的芯片电流不一样,有18UA的和37UA的具体原因不明!
VBAT电压低于3.3V需要打开LDO的bypass模式
如上面的"设置管脚为PMU模式"章节的说明,我们功耗有18UA和37UA两种低电流,我这里使用3.0供电,还遇到一个非常奇葩的bug,相同的代码相同的硬件设计不同的板子低电流都不一样,有的板子电流227UA,无语了.
这个情况是因为没有屏蔽内部的LDO造成的,非常无语,按照原厂的说法电压再3.6V就没有问题!这里尝试关闭LDO模式
void pmu_sub_init(void)
{
-------------------------------------------
ool_write(PMU_REG_IOLDO_CFG_1, ool_read(PMU_REG_IOLDO_CFG_1)|0x20);
}
经过测试原来227UA的电流降低到35UA,这个合理的水平,喜提一坑: