sc8886芯片寄存器读写

功能需求

使用stm32f103芯片与sc8886通信,实现控制充电参数设置与充电状态监测。

硬件连接

单片机I2c引脚选用 I2C1(PB6,PB7),sc8886硬件参考南芯官方文档

寄存器功能

通过阅读sc8886的数据手册,可了解到I2C地址为0x6b,充电参数设置大致如下:

寄存器地址 功能
0x00 芯片控制信息
0x20 充电状态
0x02 充电电流设置
0x04 充电电压设置
0x0c 最小系统电压
0x06 OTG电压
0x08 OTG电流
0x0e 当前输入电流
0x0a 当前输入电压
0x34 芯片控制含复位寄存器

代码实现

功能比较简单,直接贴代码。
I2C接口

  • I2C 通讯地址扫描
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    uint8_t I2C_IsDeviceReady(I2C_TypeDef* I2Cx1, uint8_t address, uint32_t timeout)
    {
    I2C_GenerateSTART(I2Cx1, ENABLE);

    while(!I2C_CheckEvent(I2Cx1, I2C_EVENT_MASTER_MODE_SELECT))
    {
    if((timeout--) == 0) return 0;
    }

    I2C_Send7bitAddress(I2Cx1, address << 1, I2C_Direction_Transmitter);

    while(!I2C_CheckEvent(I2Cx1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
    {
    if(I2C_GetFlagStatus(I2Cx1, I2C_FLAG_AF))
    {
    I2C_ClearFlag(I2Cx1, I2C_FLAG_AF);
    I2C_GenerateSTOP(I2Cx1, ENABLE);
    return 0;
    }

    if((timeout--) == 0) return 0;
    }

    I2C_GenerateSTOP(I2Cx1, ENABLE);
    return 1;
    }

    uint8_t I2C_Scan(I2C_TypeDef* I2Cx1)
    {
    uint8_t deviceFound = 0;
    uint8_t address;

    for(address = I2C_ADDRESS_START; address <= I2C_ADDRESS_END; address++)
    {
    jq_log("Scanning address: 0x%02X\r\n", address);

    if(I2C_IsDeviceReady(I2Cx1, address, 1000) == 1)
    {
    jq_log("Device found at address: 0x%02X\r\n", address);
    deviceFound = 1;
    }
    delay_ms(10);
    }

    if (!deviceFound)
    {
    jq_log("No I2C devices found.\n");
    }

    return deviceFound;
    }
  • I2C每次写寄存器前需要初始化总线
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    void i2c_pin_init(void)
    {
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    GPIO_SetBits(GPIOB, GPIO_Pin_6 | GPIO_Pin_7);

    /*!< Configure pins: SCL/SDL */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    }
  • I2C读写函数

参考例程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
uint32_t I2C_Mem_Write(uint16_t DevAddress,u8* pBuffer, u8 WriteAddr, u8 NumByteToWrite)
{
I2CTimeout = I2CT_LONG_TIMEOUT;

while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(4);
}

/* Send START condition */
I2C_GenerateSTART(I2Cx, ENABLE);

I2CTimeout = I2CT_FLAG_TIMEOUT;
/* Test on EV5 and clear it */
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(5);
}

/* Send EEPROM address for write */
I2C_Send7bitAddress(I2Cx, DevAddress, I2C_Direction_Transmitter);

I2CTimeout = I2CT_FLAG_TIMEOUT;
/* Test on EV6 and clear it */
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(6);
}

/* Send the EEPROM's internal address to write to */
I2C_SendData(I2Cx, WriteAddr);

I2CTimeout = I2CT_FLAG_TIMEOUT;
/* Test on EV8 and clear it */
while(! I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(7);
}

/* While there is data to be written */
while(NumByteToWrite--)
{
/* Send the current byte */
I2C_SendData(I2Cx, *pBuffer);

/* Point to the next byte to be written */
pBuffer++;

I2CTimeout = I2CT_FLAG_TIMEOUT;

/* Test on EV8 and clear it */
while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(8);
}
}

/* Send STOP condition */
I2C_GenerateSTOP(I2Cx, ENABLE);

return 0;
}

uint32_t I2C_Mem_Read(uint16_t DevAddress,u8* pBuffer, u8 ReadAddr, u16 NumByteToRead)
{

I2CTimeout = I2CT_LONG_TIMEOUT;

//*((u8 *)0x4001080c) |=0x80;
while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(9);
}

/* Send START condition */
I2C_GenerateSTART(I2Cx, ENABLE);
//*((u8 *)0x4001080c) &=~0x80;

I2CTimeout = I2CT_FLAG_TIMEOUT;
/* Test on EV5 and clear it */
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(10);
}

/* Send EEPROM address for write */
I2C_Send7bitAddress(I2Cx, DevAddress, I2C_Direction_Transmitter);

I2CTimeout = I2CT_FLAG_TIMEOUT;
/* Test on EV6 and clear it */
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(11);
}

/* Clear EV6 by setting again the PE bit */
I2C_Cmd(I2Cx, ENABLE);

/* Send the EEPROM's internal address to write to */
I2C_SendData(I2Cx, ReadAddr);


I2CTimeout = I2CT_FLAG_TIMEOUT;
/* Test on EV8 and clear it */
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(12);
}

/* Send STRAT condition a second time */
I2C_GenerateSTART(I2Cx, ENABLE);

I2CTimeout = I2CT_FLAG_TIMEOUT;
/* Test on EV5 and clear it */
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(13);
}

/* Send EEPROM address for read */
I2C_Send7bitAddress(I2Cx, DevAddress, I2C_Direction_Receiver);

I2CTimeout = I2CT_FLAG_TIMEOUT;
/* Test on EV6 and clear it */
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(14);
}

/* While there is data to be read */
while(NumByteToRead)
{
if(NumByteToRead == 1)
{
/* Disable Acknowledgement */
I2C_AcknowledgeConfig(I2Cx, DISABLE);

/* Send STOP Condition */
I2C_GenerateSTOP(I2Cx, ENABLE);
}

/* Test on EV7 and clear it */
I2CTimeout = I2CT_LONG_TIMEOUT;

while(I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED)==0)
{
if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);
}
{
/* Read a byte from the EEPROM */
*pBuffer = I2C_ReceiveData(I2Cx);

/* Point to the next location where the byte read will be saved */
pBuffer++;

/* Decrement the read bytes counter */
NumByteToRead--;
}
}

/* Enable Acknowledgement to be ready for another reception */
I2C_AcknowledgeConfig(I2Cx, ENABLE);

return 0;
}
  • 寄存器读写接口

寄存器读写函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static uint32_t chg_write_reg(uint8_t reg, uint8_t len, uint8_t *buf)
{
uint32_t ret;
i2c_pin_init();
ret = I2C_Mem_Write(CHARGE_ADDR<<1,buf,reg,len);

return ret;

}

static uint32_t chg_read_regs(uint8_t reg, uint8_t len, uint8_t *buf)
{
uint32_t ret;
i2c_pin_init();
ret = I2C_Mem_Read(CHARGE_ADDR<<1,buf,reg,len);

return ret;

}

字节读写函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
uint16_t read_chg_data(uint8_t reg)
{
uint8_t buf[2];
uint16_t data = 0;
buf[0]=0x00;
buf[1]=0x00;

if(chg_read_regs(reg, 2, buf) == 0)
{
data = (buf[1] << 8) | buf[0];
SysTick_Delay_Ms(10);

return data;
}
else
{
init_I2c();
SysTick_Delay_Ms(10);
return READ_CHG_ERR;
}


}

uint16_t write_chg_data(uint8_t reg,uint16_t data)
{
uint8_t buf[2];
buf[0]= data & 0xff;
buf[1]= data >> 8;

if(chg_write_reg(reg, 2, buf) == 0)
{
SysTick_Delay_Ms(10);

return data;
}
else
{
I2C_ResetBus();
SysTick_Delay_Ms(500);
return 1;
}
}

读取数据举例:
根据数据手册了解,充电电流设置每位是以64mA为单位,并且低6位是默认为0,所以计算时,应该右移6位,再乘以64,得到实际值。


读取充电电流

1
2
3
4
5
6
7
8
uint16_t chg_get_current(chg_info* jq_chg_info)
{
uint16_t ret = 0;
ret = read_chg_data(CG_CHARGE_CURRENT);
jq_chg_info->chg_current = ((ret >> 6) * 64);jq_chg_info->chg_current);

return 0;
}

设置充电电流

1
2
3
4
5
6
7
8
uint16_t chg_set_current(chg_info* jq_chg_info)
{
uint16_t value;

value = ((jq_chg_info->chg_current / 64) << 6);
write_chg_data(CG_CHARGE_CURRENT,value);
return 0;
}
  • 头文件部分参数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    // 充电管理芯片地址
    #define CHARGE_ADDR 0x6b

    // CHARGE reg address
    #define CG_CHARGE_OPTION 0x00 // 芯片控制 WDTWR_ADJ<看门狗>置0
    #define CG_CHARGE_STATUS 0x20 // 芯片状态
    #define CG_CHARGE_CURRENT 0x02 // 设置的充电电流
    #define CG_CHARGE_VOLTAGE 0x04 // 设置的充电电流
    #define CG_CHARGE_MIN_SYS_VOTAG 0x0c // 最小系统电压
    #define CG_CHARGE_OTG_VOLTAGE 0x06 // OTG电压
    #define CG_CHARGE_OTG_CURRENT 0x08 // OTG电流
    #define CG_CHARGE_INPUT_CURRENT 0x0E // 当前输入电流
    #define CG_CHARGE_VINREG_VOLTAGE 0x0A // 当前输入电压

    #define CG_CHARGE_OPTION2 0x32 // 充电选项

    #define CG_CHARGE_OPTION3 0x34 // 芯片控制 复位寄存器


    // #define CG_CHARGE_PROCHOT 0x38 // DPM测试

    // AC_STAT

    #define AC_STAT_OFF 0
    #define AC_STAT_ON 1
    #define AC_STAT_ERR 2

    // 充电芯片初始配置

    #define DEFAULT_CHARGE_CURRENT 2000 // 默认充电电流
    #define DEFAULT_MAX_CHARGE_VOLTAGE 12600 // 默认充电电压
    #define DEFAULT_MIN_SYS_VOTAGE 9000 // 默认系统电压
    #define DEFAULT_OTG_VOLTAGE 9000 // 默认OTG电压
    #define DEFAULT_OTG_CURRENT 500 // 默认OTG电流
    #define DEFAULT_INPUT_CURRENT 5000 // 默认输入电流
    #define DEFAULT_INPUT_VOLTAGE 13800 // 默认输入电压

    // #define FIRST_CHARGE_CURRENT 1000 // 预设充电电流
    #define READ_CHG_ERR 1234
    extern int gi_sleep_flag;

    typedef struct {
    uint16_t chg_option;
    uint16_t chg_status;
    uint16_t chg_current;
    uint16_t chg_voltage;
    uint16_t chg_min_sys_voltage;
    uint16_t chg_otg_voltage;
    uint16_t chg_otg_current;
    uint16_t chg_input_current;
    uint16_t chg_input_voltage;
    uint16_t chg_old_status;
    }chg_info;

问题

待续…