0、前言
本设计的内容是一个DRP设备,需要完成SINK(受电放)和SRC(供电方)的代码。在完成SRC部分代码的时候,还需要搭配Charge IC和电池,完成对外放电,因此本章节先完成Charge IC的API函数,同时完成OLED显示部分,方便充电状态的实时监控。
1、充放电管理模块
这里充电管理模块指的是,对电池的充放电管理,STM32控制Charge IC结合PD协议,完成电池的充电和放电。Charge IC需要选择具有buck-boost工作模式,且具有对外放电和充电功能的,因为2节电池电压为7-8V,而PD协议SPR支持的电压为5V~21V。
1.1、充放电模块的OTG功能
本设计是45W的放电功率,在设备处于SRC模式下,充放电模块的OTG功能能够实现以下的PDO电压:
Fix PDO: 5V-3A、9V-3A、15V-3A
PPS: 5V(5V~5.9V)、9V(5V~11V)、15V(5V~16V)
我这里还没时间去做PPS,如果要做PPS的话,那么对Charge IC就有更高的需求,输出精度至少需要20mV/bit
1.2、充电功能
2节18650电池的充电电压一般为8.4V,因此在设备处于SNK模式下,设备请求的PDO最好为9V,这样才能保证最大的充电效率。
2、Charge IC的API函数
不同的Charge IC其寄存器定义基本上不一样,但尽量封装以下的API功能函数
/** SNK 部分API函数 **/
HAL_StatusTypeDef dcdc_set_charge_current(uint16_t value);
HAL_StatusTypeDef dcdc_set_max_charge_voltage(uint16_t value);
HAL_StatusTypeDef dcdc_set_input_current_limit(uint16_t value);
HAL_StatusTypeDef dcdc_set_input_voltage_limit(uint16_t value);
HAL_StatusTypeDef dcdc_snk_switch(uint8_t value);
HAL_StatusTypeDef dcdc_snk_mode_init(void);
/** SRC 部分API函数 **/
HAL_StatusTypeDef dcdc_set_discharge_voltage(uint16_t value);
HAL_StatusTypeDef dcdc_otg_voltage(uint16_t value);
HAL_StatusTypeDef dcdc_otg_current(uint16_t value);
HAL_StatusTypeDef dcdc_otg_en(uint8_t value);
HAL_StatusTypeDef dcdc_src_switch(uint8_t value);
HAL_StatusTypeDef dcdc_src_mode_init(void);
3、OLED模块
采用IIC驱动的1.3寸OLED显示屏12864液晶屏(SH1106驱动)
4、OLED驱动代码
代码是集合了DCDC驱动部分和PD部分的内容,移植了UGUI,仅作参考!!!
/* USER CODE BEGIN Header_Start_OLED_Task */
/**
* @brief Function implementing the OLED_Task thread.
* @param argument: Not used
* @retval None
*/
//I2C写入字节参数
void OLED_WriteByte(uint8_t dat,uint8_t cmd)
{
HAL_I2C_Mem_Write(&hi2c1, 0x78, cmd, I2C_MEMADD_SIZE_8BIT, &dat, 1, 0xFFFF);
}
//SH1106 driver
void OLED_Init()
{
osDelay(100);
OLED_WriteByte(0xAE, OLED_CMD); //--display off
OLED_WriteByte(0xAE, OLED_CMD); //set display display ON/OFF,AFH/AEH
OLED_WriteByte(0x02, OLED_CMD);
OLED_WriteByte(0x10, OLED_CMD);
OLED_WriteByte(0x40, OLED_CMD); //set display start line:COM0
OLED_WriteByte(0xB0, OLED_CMD);
OLED_WriteByte(0x81, OLED_CMD); //set contrast control
OLED_WriteByte(0xCF, OLED_CMD);
OLED_WriteByte(0xA1, OLED_CMD); //entire display on: A4H:OFF/A5H:ON
OLED_WriteByte(0xC0, OLED_CMD); //set show direction
//OLED_WriteByte(0xC8, OLED_CMD);
OLED_WriteByte(0xAF, OLED_CMD);
OLED_WriteByte(0xA7, OLED_CMD); //set normal/inverse display: A6H:normal/A7H:inverse
OLED_WriteByte(0xA8, OLED_CMD); //set multiplex ratio
OLED_WriteByte(0x3F, OLED_CMD); //1/64duty
OLED_WriteByte(0xD3, OLED_CMD); //set display offset
OLED_WriteByte(0x00, OLED_CMD); //
OLED_WriteByte(0xD5, OLED_CMD); //set display clock divide ratio/oscillator frequency
OLED_WriteByte(0x80, OLED_CMD); //105Hz
OLED_WriteByte(0xD9, OLED_CMD); //Dis-charge /Pre-charge Period Mode Set
OLED_WriteByte(0xF1, OLED_CMD); //
OLED_WriteByte(0xDA, OLED_CMD); //Common Pads Hardware Configuration Mode Set
OLED_WriteByte(0x12, OLED_CMD); //
OLED_WriteByte(0xDB, OLED_CMD); //set vcomh deselect level
OLED_WriteByte(0x40, OLED_CMD); //VCOM = β X VREF = (0.430 + A[7:0] X 0.006415) X VREF
OLED_WriteByte(0xA4, OLED_CMD);
OLED_WriteByte(0xA6, OLED_CMD);
OLED_WriteByte(0xAF, OLED_CMD); //set display display ON/OFF,AEH/AFH
}
uint8_t OLED_GRAM[128][8];
static void OLED_Screen_Clear()
{
uint8_t i,n;
for(i = 0; i < 8; i++)
{
OLED_WriteByte (0xb0+i, OLED_CMD); //设置页地址(0~7)
OLED_WriteByte (0x01, OLED_CMD); //设置显示位置—列低地址
OLED_WriteByte (0x10, OLED_CMD); //设置显示位置—列高地址
for(n = 0; n < 130; n++)
{
OLED_WriteByte(0x00, OLED_DATA); //write 0x00;
}
}
}
static void OLED_Ram_Clear(void)
{
uint8_t i,n;
for(i = 0; i < 8; i++)
{
for(n = 0; n < 128; n++)
{
OLED_GRAM[n][i] = 0X00;
}
}
}
void OLED_Refresh_Gram(void)
{
uint8_t i,n;
for(i = 0; i < 8; i++)
{
OLED_WriteByte (0xb0+i, OLED_CMD); //设置页地址(0~7)
OLED_WriteByte (0x01, OLED_CMD); //设置显示位置—列低地址
OLED_WriteByte (0x10, OLED_CMD); //设置显示位置—列高地址
for(n = 0; n < 128; n++)
{
OLED_WriteByte(OLED_GRAM[n][i], OLED_DATA); //write 0x00;
}
}
}
void OLED_DrawPoint(int16_t x,int16_t y,uint32_t t)
{
uint8_t pos, bx,temp=0;
if(x > 128 || y > 64)
{
return;//超出范围了.
}
pos = 7 - y/8;
bx = y%8;
temp = 1<<(7-bx);
if(t)
{
OLED_GRAM[x][pos]|=temp;
}
else
{
OLED_GRAM[x][pos]&=~temp;
}
}
/* USER CODE END Header_Start_OLED_Task */
void Start_OLED_Task(void const * argument)
{
/* USER CODE BEGIN Start_OLED_Task */
uint32_t time = 0;
char show_str[32];
UG_GUI gui ; // Global GUI structure
OLED_Init();
OLED_Screen_Clear();
UG_Init(&gui, OLED_DrawPoint, 128 , 64);
UG_SelectGUI(&gui);
// printf("\
//************************* \r\n\
// adc_system_power: %d mv \r\n\
// adc_input_voltage: %d mv \r\n\
// adc_bat_discharge_cur: %d ma \r\n\
// adc_bat_charge_cur: %d ma \r\n\
// adc_cmpin_voltage: %d mv \r\n\
// adc_input_cur: %d ma \r\n\
// adc_bat_voltage: %d mv \r\n\
// adc_system_voltage: %d mv \r\n\
//*************************",\
// dcdc_adc_conv_data.adc_system_power,
// dcdc_adc_conv_data.adc_input_voltage,
// dcdc_adc_conv_data.adc_bat_discharge_cur,
// dcdc_adc_conv_data.adc_bat_charge_cur,
// dcdc_adc_conv_data.adc_cmpin_voltage,
// dcdc_adc_conv_data.adc_input_cur,
// dcdc_adc_conv_data.adc_bat_voltage,
// dcdc_adc_conv_data.adc_system_voltage);
/* Infinite loop */
for(;;)
{
OLED_Ram_Clear();
//Title
UG_FontSelect(&FONT_8X12);
UG_PutString(40, 1, "PD LED");
UG_DrawRoundFrame(5, 0, 123, 13, 3, C_WHITE);
//DCDC
UG_FontSelect(&FONT_6X10);
UG_PutString(2, 17, "|Name | mV | mA |");
UG_DrawLine(5, 28, 122, 28, C_WHITE);
sprintf(show_str, "|Bus |%05d|%04d|", dcdc_adc_conv_data.adc_input_voltage, dcdc_adc_conv_data.adc_input_cur);
UG_PutString(2, 30, show_str);
if (DPM_Params[0].PE_PowerRole == USBPD_PORTPOWERROLE_SRC)
{
sprintf(show_str, "|Batt |%05d|%04d|", dcdc_adc_conv_data.adc_bat_voltage, dcdc_adc_conv_data.adc_bat_discharge_cur);
}else
{
sprintf(show_str, "|Batt |%05d|%04d|", dcdc_adc_conv_data.adc_bat_voltage, dcdc_adc_conv_data.adc_bat_charge_cur);
}
UG_PutString(2, 40, show_str);
//Time
UG_FontSelect(&FONT_6X8);
sprintf(show_str, "Time:%04d", time++);
UG_PutString(60, 54, show_str);
OLED_Refresh_Gram();
osDelay(1000);
}
/* USER CODE END Start_OLED_Task */
}
5、 实物展示