一、SH367309芯片介绍
注意:配置AFE的EEPROM寄存器时只能写入一次,写入前读取00-19H的数据,如果写入的内容和读取的内容相同,则不写入
二、SH367309的四种工作模式
(1)保护模式
MODE管脚外接低电平V L-MODE ,SH367309工作在保护模式,SH367309开启内置保护功能模块,开启平衡功能,关闭看门狗和TWI通讯模块。
(2)采集模式
MODE管脚外接高电平V H-MODE ,SH367309工作在采集模式。开启内置保护功能模块,开启TWI通讯模块,MCU可通过TWI通讯模块操作SH367309内部寄存器。
(3)仓运模式
SHIP管脚外接低电平V L-SHIP ,SH367309进入仓运模式。关闭充放电MOSFET,同时关闭所有功能模块。
(4)烧写模式
VPRO管脚外接EEPROM烧写电压V PRO ,且延时10mS,SH367309进入烧写模式。关闭充放电MOSFET及内置保护功能模块。此时其他设备可通过TWI接口读/写内置EEPROM,且内置EEPROM仅在烧写模式下方可进行写操作。
注意:
1、ROM中数据不需要等待ALARM管脚信号采集到数据后在读取
2、改写AFE的ROM数据后,需要复位才能生效
SH367309_SHIP_Lo;
delay_1ms(100);
SH367309_SHIP_Hi;
delay_1ms(100);
3、SH367309默认关闭CADC电流采集,如果需要电流采集的功能,主动配置0x40寄存器
4、退出仓运模式或者进入烧写模式时,需要延时10ms以上等待电路稳定
SH367309_SHIP_Hi; //使能AFE的SHIP引脚,AFE退出休眠
delay_1ms(100); //延迟100mS
SH367309_VPRO_Hi; //使能AFE的VPRO烧写功能(可以对EEPROM进行配置)
delay_1ms(100); //延时,等待电平稳定
三、ALARM管脚
ALARM 管脚:SH367309处于采集模式下,ALARM管脚为对外通讯管脚;处于保护模式下,ALARM管脚为高阻态。采集模式下,ALARM管脚正常输出逻辑高电平。出现下表中的系统状态,ALARM管脚输出一个低电平脉冲。出现下表中的系统状态,ALARM管脚输出一个低电平脉冲。
将ALARM引脚配置为外部中断,下降沿触发,即每触发一次中断,说明数据已经采集完成一次。
void Afe_Init(void)
{
gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_0); //ALARM检测口配置为外部中断
nvic_irq_enable(EXTI0_IRQn, 2U, 0U); //使能外部中断0线程
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOA, GPIO_PIN_SOURCE_0);//配置外部中断对应端口
exti_init(EXTI_0, EXTI_INTERRUPT, EXTI_TRIG_FALLING); //外部中断0,下降沿触发
exti_interrupt_flag_clear(EXTI_0); //清外部中断0标志
}
void EXTI0_IRQHandler(void)
{
if(RESET != exti_interrupt_flag_get(EXTI_0))
{
exti_interrupt_flag_clear(EXTI_0);
tagSBS.bAlarm = ISYES; //ALARM置1
}
}
void ReadAFEData()
{
static unsigned char afecomcount1,afecomcount2;
if(tagSBS.bAlarm == ISYES)
{
tagSBS.bAlarm = ISNO;
TwiRead(0x34,0X00,26,tagBMS.ucReadAFERegister);
TwiRead(0x34,0X40,51,tagBMS.ucReadAFERegister+0X40))
}
}
void task4(void *pvParameters)
{
while(1)
{
vTaskDelay(1000); //1S
ReadAFEData();//读AFE寄存器
}
}
四、工作流程
1、退出仓运模式
2、初始化AFE ROM参数,读AFE ROM 00-19H数据,判断初始化参数和读取参数是否一致,不一致写入数据
3、配置0x40寄存器,使能CADC进行电流采集,判断ALARM引脚是否采集数据完毕,完毕后读AFE RAM数据
五、通信协议代码
TWI通讯中,SH367309作为从机,其固定地址是 0x34( | )最低位0写1读
twi.c
#include "twi.h"
unsigned char const CRC8Table[]=
{ //120424-1 CRC Table
0x00,0x07,0x0E,0x09,0x1C,0x1B,0x12,0x15,0x38,0x3F,0x36,0x31,0x24,0x23,0x2A,0x2D,
0x70,0x77,0x7E,0x79,0x6C,0x6B,0x62,0x65,0x48,0x4F,0x46,0x41,0x54,0x53,0x5A,0x5D,
0xE0,0xE7,0xEE,0xE9,0xFC,0xFB,0xF2,0xF5,0xD8,0xDF,0xD6,0xD1,0xC4,0xC3,0xCA,0xCD,
0x90,0x97,0x9E,0x99,0x8C,0x8B,0x82,0x85,0xA8,0xAF,0xA6,0xA1,0xB4,0xB3,0xBA,0xBD,
0xC7,0xC0,0xC9,0xCE,0xDB,0xDC,0xD5,0xD2,0xFF,0xF8,0xF1,0xF6,0xE3,0xE4,0xED,0xEA,
0xB7,0xB0,0xB9,0xBE,0xAB,0xAC,0xA5,0xA2,0x8F,0x88,0x81,0x86,0x93,0x94,0x9D,0x9A,
0x27,0x20,0x29,0x2E,0x3B,0x3C,0x35,0x32,0x1F,0x18,0x11,0x16,0x03,0x04,0x0D,0x0A,
0x57,0x50,0x59,0x5E,0x4B,0x4C,0x45,0x42,0x6F,0x68,0x61,0x66,0x73,0x74,0x7D,0x7A,
0x89,0x8E,0x87,0x80,0x95,0x92,0x9B,0x9C,0xB1,0xB6,0xBF,0xB8,0xAD,0xAA,0xA3,0xA4,
0xF9,0xFE,0xF7,0xF0,0xE5,0xE2,0xEB,0xEC,0xC1,0xC6,0xCF,0xC8,0xDD,0xDA,0xD3,0xD4,
0x69,0x6E,0x67,0x60,0x75,0x72,0x7B,0x7C,0x51,0x56,0x5F,0x58,0x4D,0x4A,0x43,0x44,
0x19,0x1E,0x17,0x10,0x05,0x02,0x0B,0x0C,0x21,0x26,0x2F,0x28,0x3D,0x3A,0x33,0x34,
0x4E,0x49,0x40,0x47,0x52,0x55,0x5C,0x5B,0x76,0x71,0x78,0x7F,0x6A,0x6D,0x64,0x63,
0x3E,0x39,0x30,0x37,0x22,0x25,0x2C,0x2B,0x06,0x01,0x08,0x0F,0x1A,0x1D,0x14,0x13,
0xAE,0xA9,0xA0,0xA7,0xB2,0xB5,0xBC,0xBB,0x96,0x91,0x98,0x9F,0x8A,0x8D,0x84,0x83,
0xDE,0xD9,0xD0,0xD7,0xC2,0xC5,0xCC,0xCB,0xE6,0xE1,0xE8,0xEF,0xFA,0xFD,0xF4,0xF3
};
void Delay4us(void) //4uS
{
uint32_t ticks;
uint32_t told,tnow,reload,tcnt=0;
reload = SysTick->LOAD; //获取重装载寄存器值
ticks = 4 * (SystemCoreClock / 1000000); //计数时间值 括号里的代表1us秒嘀嗒定时器的value会向下降多少值
told=SysTick->VAL; //获取当前数值寄存器值(开始时数值)
while(1)
{
tnow=SysTick->VAL; //获取当前数值寄存器值
if(tnow!=told) //当前值不等于开始值说明已在计数
{
if(tnow<told) //当前值小于开始数值,说明未计到0
tcnt+=told-tnow; //计数值=开始值-当前值
else //当前值大于开始数值,说明已计到0并重新计数
tcnt+=reload-tnow+told; //计数值=重装载值-当前值+开始值 (已从开始值计到0)
told=tnow; //更新开始值
if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出.
}
}
}
void TWI_Init(void)
{
rcu_periph_clock_enable(RCU_GPIOB);
gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
TWI_DAT_HIGH;
TWI_CLK_HIGH;
}
void TwiStart(void)
{
TWI_DAT_HIGH;
TWI_CLK_HIGH;
TWI_DAT_OUT;
TWI_CLK_OUT;
TWI_DAT_LOW;
Delay4us();
TWI_CLK_LOW;
}
void TwiReStart(void)
{
TWI_DAT_HIGH;
TWI_CLK_HIGH;
Delay4us();
TWI_DAT_LOW;
Delay4us();
TWI_CLK_LOW;
}
void TwiStop(void)
{
TWI_DAT_OUT;
TWI_DAT_LOW;
Delay4us();
TWI_CLK_HIGH;
Delay4us();
TWI_DAT_HIGH;
Delay4us();
TWI_DAT_IN;
TWI_CLK_IN;
}
//SH367309接收好数据后,释放SCL信号线,所以此函数可以判断通信是否成功
unsigned char TwiChkClkRelease(void)
{
unsigned int TimeoutCnt=1000; //If Clock is not released within 4ms, is considered overtime
unsigned char result=0;
TWI_CLK_IN;
while(TimeoutCnt--)
{
Delay4us();
if(TWI_RD_CLK)
{
result = 1;
break;
}
}
TWI_CLK_HIGH;
TWI_CLK_OUT;
return result;
}
unsigned char TwiSendData(unsigned char Data, unsigned char ClkFlg)
{
unsigned char i;
unsigned char result=0;
//1. After sending the Start signal, there is no need to detect Clock is released, And sending the first bit
if(Data&0x80)
{
TWI_DAT_HIGH;
}
else
{
TWI_DAT_LOW;
}
if(ClkFlg == 1)
{
Delay4us();
if(TwiChkClkRelease())
{
TWI_CLK_LOW;
}
else
{
return result;//返回0表示通信失败
}
}
else
{
Delay4us();
TWI_CLK_HIGH;
Delay4us();
TWI_CLK_LOW;
}
//2. Send the remaining seven bit
Data = Data<<1;
for(i=0; i<7; i++)
{
if(Data&0x80)
{
TWI_DAT_HIGH;
}
else
{
TWI_DAT_LOW;
}
Data = Data<<1;
Delay4us();
TWI_CLK_HIGH;
Delay4us();
TWI_CLK_LOW;
}
TWI_DAT_IN;
Delay4us();
for(i=0; i<10; i++)
{
if(TWI_RD_DAT == 0)
{
result = 1;
break;
}
}
TWI_CLK_HIGH;
Delay4us();
TWI_DAT_LOW;
TWI_DAT_OUT;
TWI_CLK_LOW;
Delay4us();
return result;//返回1有应答,返回0无应答
}
unsigned char TwiGetData(unsigned char AckFlg)
{
unsigned char i, RdData=0;
TWI_DAT_IN;
Delay4us();
for(i=0; i<8; i++)
{
TWI_CLK_HIGH;
Delay4us();
if(TWI_RD_DAT)
{
RdData |= (1<<(7-i));
}
TWI_CLK_LOW;
Delay4us();
}
TWI_DAT_OUT;
if(AckFlg != 0)
{
TWI_DAT_LOW;
}
else
{
TWI_DAT_HIGH;
}
TWI_CLK_HIGH;
Delay4us();
TWI_CLK_LOW;
Delay4us();
return RdData;
}
unsigned char CRC8cal(unsigned char *p, unsigned char Length) //look-up table calculte CRC
{
unsigned char crc8 = 0;
for(; Length > 0; Length--)
{
crc8 = CRC8Table[crc8^*p];
p++;
}
return(crc8);
}
unsigned char TwiWrite(unsigned char SlaveID, unsigned int WrAddr, unsigned char Length, unsigned char *WrBuf)
{
unsigned char i;
unsigned char TempBuf[4];
unsigned char result = 0;
TempBuf[0] = SlaveID;
TempBuf[1] = (unsigned char)WrAddr;
TempBuf[2] = *WrBuf;
TempBuf[3] = CRC8cal(TempBuf, 3);
if(Length > 0)
{
TwiStart();
if(!TwiSendData(SlaveID, 1)) //Send Slave ID
{
goto WrErr;
}
if(TwiSendData(WrAddr, 0)) //Send Write Address(Low 8bit)
{
result = 1;
for(i=0; i<Length; i++)
{
if(TwiSendData(*WrBuf, 0)) //Send Write Data
{
WrBuf++;
}
else
{
result = 0;
break;
}
}
if(!TwiSendData(TempBuf[3], 0)) //write CRC
{
result = 0;
}
}
WrErr:
TwiStop();
}
return result;
}
unsigned char TwiRead(unsigned char SlaveID, unsigned int RdAddr, unsigned char Length, unsigned char *RdBuf)
{
unsigned char i;
unsigned char result=0;
unsigned char TempBuf[46];
unsigned char RdCrc=0;
TempBuf[0] = SlaveID;
TempBuf[1] = (unsigned char)RdAddr;
TempBuf[2] = Length;
TempBuf[3] = SlaveID | 0x01;
if(Length > 0)
{
TwiStart(); //开始
if(!TwiSendData(SlaveID, 1)) //Send Slave ID
{
goto RdErr;
}
if(!TwiSendData(RdAddr, 0)) //Send Read Address(Low 8bit)
{
goto RdErr;
}
if(!TwiSendData(Length, 0))
{
goto RdErr;
}
TwiReStart();
if(TwiSendData(SlaveID|0x1, 0)) //Send Slave ID
{
result = 1;
for(i=0; i<Length+1; i++)
{
if(i == Length)
{
RdCrc = TwiGetData(0); //Get Data
}
else
{
TempBuf[4+i] = TwiGetData(1); //Get Data
}
}
if(RdCrc != CRC8cal(TempBuf, 4+Length))
{
result = 0;
}
else
{
for(i=0; i<Length; i++)
{
*RdBuf = TempBuf[4+i];
RdBuf++;
}
}
}
RdErr:
TwiStop();
}
return result;
}
/*特别注意,写eeprom必须要使能烧写模式,其他的读eeprom,ram或者写ram正常即可
void WriteAFEData_EEPROM()
{
TwiRead(0x34,0X00,26,tagBMS.ucReadAFERegister); //读AFE的EEPROM寄存器,地址(00H-19H 即 0-25),共26个8位寄存器
if(memcmp(tagBMS.ucWriteAFERegister,tagBMS.ucReadAFERegister,25)!=0) //存储区str1、str2前n个字节比较,0=无区别,!=0有区别
{
UCHAR i;
SH367309_VPRO_Hi; //使能AFE的VPRO烧写功能(可以对EEPROM进行配置)
delay_1ms(100); //延时,等待电平稳定
for(i=0; i<25; i++) //写AFE的EEPROM寄存器,地址(00H-19H 即 0-25),共26个8位寄存器
{
delay_1ms(5);
if(!TwiWrite(0x34, i, 1, tagBMS.ucWriteAFERegister+i))
{
tagSBS.bAFEFaultStatus = ISYES;//写AFEROM出现错误
}
}
SH367309_VPRO_Lo; //关闭AFE的VPRO烧写,进入正常读AFE模式
改写AFEROM参数后,需要复位后才能生效
SH367309_SHIP_Lo;
delay_1ms(100);
SH367309_SHIP_Hi;
delay_1ms(100);
}
}*/
twi.h
#ifndef _TWI_H_
#define _TWI_H_
#include "head.h"
#define TWI_CLK_HIGH gpio_bit_set(GPIOB, GPIO_PIN_8)
#define TWI_CLK_LOW gpio_bit_reset(GPIOB, GPIO_PIN_8)
#define TWI_DAT_HIGH gpio_bit_set(GPIOB, GPIO_PIN_9)
#define TWI_DAT_LOW gpio_bit_reset(GPIOB, GPIO_PIN_9)
#define TWI_CLK_OUT gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8) //时钟口设置为输出
#define TWI_DAT_OUT gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9) //数据口设置为输出
#define TWI_CLK_IN gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_8) //时钟口设置为输入
#define TWI_DAT_IN gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_9) //数据口设置为输入
#define TWI_RD_CLK gpio_input_bit_get(GPIOB,GPIO_PIN_8)
#define TWI_RD_DAT gpio_input_bit_get(GPIOB,GPIO_PIN_9)
void TWI_Init(void);
void Delay4us(void); //4uS
void TwiStart(void);
void TwiReStart(void);
void TwiStop(void);
unsigned char TwiChkClkRelease(void);
unsigned char TwiSendData(unsigned char Data, unsigned char ClkFlg);
unsigned char TwiGetData(unsigned char AckFlg);
unsigned char CRC8cal(unsigned char *p, unsigned char Length); //look-up table calculte CRC
unsigned char TwiWrite(unsigned char SlaveID, unsigned int WrAddr, unsigned char Length, unsigned char *WrBuf);
unsigned char TwiRead(unsigned char SlaveID, unsigned int RdAddr, unsigned char Length, unsigned char *RdBuf);
void InitTwi(void);
#endif