主题中讨论的其他器件:BQSTUDIO
您好!
我一直在尝试从 STM32412RE 与 bq759521进行通信。 我们已经检查了 MOSI、CS 和 CLK、一切看起来都很好。
但 BQ 没有响应、MISO 始终为1.8V。
硬件:
1. Breg 和 VREGIN 短接
REG18为1.8V。
随函附上代码。 请帮助我们从过去的5天开始就一直坚持这一点。
/*
* bq.c
*
* Created on: 14-Oct-2022
* Author: Pragnesh
*/
#include "bq.h"
#include "main.h"
#include <sys_time.h>
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define DEV_ADDR 0x10 // BQ769x2 address is 0x10 including R/W bit or 0x8 as 7-bit address
#define CRC_Mode 0 // 0 for disabled, 1 for enabled
#define MAX_BUFFER_SIZE 10
#define R 0 // Read; Used in DirectCommands and Subcommands functions
#define W 1 // Write; Used in DirectCommands and Subcommands functions
#define W2 2 // Write data with two bytes; Used in Subcommands function
/* USER CODE END PD */
/* USER CODE BEGIN PV */
UINT8 rxdata [2];
UINT8 RX_data [2] = {0x00, 0x00}; // used in several functions to store data read from BQ769x2
UINT8 RX_32Byte [32] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
//used in Subcommands read function
// Global Variables for cell voltages, temperatures, Stack voltage, PACK Pin voltage, LD Pin voltage, CC2 current
UINT16 bq_cellv [16] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
float Temperature [3] = {0,0,0};
UINT16 Stack_Voltage = 0x00;
UINT16 Pack_Voltage = 0x00;
UINT16 LD_Voltage = 0x00;
UINT16 Pack_Current = 0x00;
UINT16 AlarmBits = 0x00;
UINT8 value_SafetyStatusA; // Safety Status Register A
UINT8 value_SafetyStatusB; // Safety Status Register B
UINT8 value_SafetyStatusC; // Safety Status Register C
UINT8 value_PFStatusA; // Permanent Fail Status Register A
UINT8 value_PFStatusB; // Permanent Fail Status Register B
UINT8 value_PFStatusC; // Permanent Fail Status Register C
UINT8 FET_Status; // FET Status register contents - Shows states of FETs
UINT16 CB_ActiveCells; // Cell Balancing Active Cells
UINT8 UV_Fault = 0; // under-voltage fault state
UINT8 OV_Fault = 0; // over-voltage fault state
UINT8 SCD_Fault = 0; // short-circuit fault state
UINT8 OCD_Fault = 0; // over-current fault state
UINT8 ProtectionsTriggered = 0; // Set to 1 if any protection triggers
UINT8 LD_ON = 0; // Load Detect status bit
UINT8 DSG = 0; // discharge FET state
UINT8 CHG = 0; // charge FET state
UINT8 PCHG = 0; // pre-charge FET state
UINT8 PDSG = 0; // pre-discharge FET state
UINT32 AccumulatedCharge_Int; // in BQ769x2_READPASSQ func
UINT32 AccumulatedCharge_Frac;// in BQ769x2_READPASSQ func
UINT32 AccumulatedCharge_Time;// in BQ769x2_READPASSQ func
UINT32 bq_timer = 0;
/* USER CODE BEGIN PV */
void delayUS(uint32_t us) { // Sets the delay in microseconds.
bq_timer = SysTime_getUsec() ; // set the counter value a 0
while (!SysTime_usecIntervalElapsed(bq_timer,us)); // wait for the counter to reach the us input in the parameter
}
void CopyArray(uint8_t *source, uint8_t *dest, uint8_t count)
{
uint8_t copyIndex = 0;
for (copyIndex = 0; copyIndex < count; copyIndex++)
{
dest[copyIndex] = source[copyIndex];
}
}
unsigned char Checksum(unsigned char *ptr, unsigned char len)
// Calculates the checksum when writing to a RAM register. The checksum is the inverse of the sum of the bytes.
{
unsigned char i;
unsigned char checksum = 0;
for(i=0; i<len; i++)
checksum += ptr[i];
checksum = 0xff & ~checksum;
return(checksum);
}
unsigned char CRC8(unsigned char *ptr, unsigned char len)
//Calculates CRC8 for passed bytes. Used in i2c read and write functions
{
unsigned char i;
unsigned char crc=0;
while(len--!=0)
{
for(i=0x80; i!=0; i/=2)
{
if((crc & 0x80) != 0)
{
crc *= 2;
crc ^= 0x107;
}
else
crc *= 2;
if((*ptr & i)!=0)
crc ^= 0x107;
}
ptr++;
}
return(crc);
}
void SPI_WriteReg(uint8_t reg_addr, uint8_t *reg_data, uint8_t count) {
// SPI Write. Includes retries in case HFO has not started or if wait time is needed. See BQ76952 Software Development Guide for examples
uint8_t addr;
uint8_t TX_Buffer [MAX_BUFFER_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
unsigned int i;
unsigned int match;
unsigned int retries = 10;
match = 0;
addr = 0x80 | reg_addr;
for(i=0; i<count; i++) {
TX_Buffer[0] = addr;
TX_Buffer[1] = reg_data[i];
HAL_GPIO_WritePin(SD_SS_GPIO_Port, SD_SS_Pin, GPIO_PIN_RESET);
HAL_SPI_TransmitReceive(&hspi2, TX_Buffer, rxdata, 2,5);
//HAL_SPI_Receive(&hspi2,rxdata, 2,5);
HAL_GPIO_WritePin(SD_SS_GPIO_Port, SD_SS_Pin, GPIO_PIN_SET);
while ((match == 0) & (retries > 0)) {
delayUS(500);
HAL_GPIO_WritePin(SD_SS_GPIO_Port, SD_SS_Pin, GPIO_PIN_RESET);
HAL_SPI_TransmitReceive(&hspi2, TX_Buffer, rxdata, 2,5);
//HAL_SPI_Receive(&hspi2,rxdata, 2,5);
HAL_GPIO_WritePin(SD_SS_GPIO_Port, SD_SS_Pin, GPIO_PIN_SET);
if ((rxdata[0] == addr) & (rxdata[1] == reg_data[i]))
match = 1;
retries --;
}
match = 0;
addr += 1;
delayUS(500);
}
}
void SPI_ReadReg(uint8_t reg_addr, uint8_t *reg_data, uint8_t count) {
// SPI Read. Includes retries in case HFO has not started or if wait time is needed. See BQ76952 Software Development Guide for examples
uint8_t addr;
uint8_t TX_Buffer [MAX_BUFFER_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
unsigned int i;
unsigned int match;
unsigned int retries = 10;
match = 0;
addr = reg_addr;
for(i=0; i<count; i++) {
TX_Buffer[0] = addr;
TX_Buffer[1] = 0xFF;
HAL_GPIO_WritePin(SD_SS_GPIO_Port, SD_SS_Pin, GPIO_PIN_RESET);
HAL_SPI_TransmitReceive(&hspi2, TX_Buffer, rxdata, 2,1);
//HAL_SPI_Receive(&hspi2,rxdata, 2,5);
HAL_GPIO_WritePin(SD_SS_GPIO_Port, SD_SS_Pin, GPIO_PIN_SET);
while ((match == 0) & (retries > 0)) {
delayUS(500);
HAL_GPIO_WritePin(SD_SS_GPIO_Port, SD_SS_Pin, GPIO_PIN_RESET);
HAL_SPI_TransmitReceive(&hspi2, TX_Buffer, rxdata, 2,1);
//HAL_SPI_Receive(&hspi2,rxdata, 2,5);
HAL_GPIO_WritePin(SD_SS_GPIO_Port, SD_SS_Pin, GPIO_PIN_SET);
if (rxdata[0] == addr) {
match = 1;
reg_data[i] = rxdata[1];
}
retries --;
}
match = 0;
addr += 1;
delayUS(500);
}
}
void BQ769x2_SetRegister(uint16_t reg_addr, uint32_t reg_data, uint8_t datalen)
{
uint8_t TX_Buffer[2] = {0x00, 0x00};
uint8_t TX_RegData[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
//TX_RegData in little endian format
TX_RegData[0] = reg_addr & 0xff;
TX_RegData[1] = (reg_addr >> 8) & 0xff;
TX_RegData[2] = reg_data & 0xff; //1st byte of data
switch(datalen)
{
case 1: //1 byte datalength
SPI_WriteReg(0x3E, TX_RegData, 3);
delayUS(2000);
TX_Buffer[0] = Checksum(TX_RegData, 3);
TX_Buffer[1] = 0x05; //combined length of register address and data
SPI_WriteReg(0x60, TX_Buffer, 2); // Write the checksum and length
delayUS(2000);
break;
case 2: //2 byte datalength
TX_RegData[3] = (reg_data >> 8) & 0xff;
SPI_WriteReg(0x3E, TX_RegData, 4);
delayUS(2000);
TX_Buffer[0] = Checksum(TX_RegData, 4);
TX_Buffer[1] = 0x06; //combined length of register address and data
SPI_WriteReg(0x60, TX_Buffer, 2); // Write the checksum and length
delayUS(2000);
break;
case 4: //4 byte datalength, Only used for CCGain and Capacity Gain
TX_RegData[3] = (reg_data >> 8) & 0xff;
TX_RegData[4] = (reg_data >> 16) & 0xff;
TX_RegData[5] = (reg_data >> 24) & 0xff;
SPI_WriteReg(0x3E, TX_RegData, 6);
delayUS(2000);
TX_Buffer[0] = Checksum(TX_RegData, 6);
TX_Buffer[1] = 0x08; //combined length of register address and data
SPI_WriteReg(0x60, TX_Buffer, 2); // Write the checksum and length
delayUS(2000);
break;
}
}
void CommandSubcommands(uint16_t command) //For Command only Subcommands
// See the TRM or the BQ76952 header file for a full list of Command-only subcommands
{ //For DEEPSLEEP/SHUTDOWN subcommand you will need to call this function twice consecutively
uint8_t TX_Reg[2] = {0x00, 0x00};
//TX_Reg in little endian format
TX_Reg[0] = command & 0xff;
TX_Reg[1] = (command >> 8) & 0xff;
SPI_WriteReg(0x3E,TX_Reg,2);
delayUS(2000);
}
void Subcommands(uint16_t command, uint16_t data, uint8_t type)
// See the TRM or the BQ76952 header file for a full list of Subcommands
{
//security keys and Manu_data writes dont work with this function (reading these commands works)
//max readback size is 32 bytes i.e. DASTATUS, CUV/COV snapshot
uint8_t TX_Reg[4] = {0x00, 0x00, 0x00, 0x00};
uint8_t TX_Buffer[2] = {0x00, 0x00};
//TX_Reg in little endian format
TX_Reg[0] = command & 0xff;
TX_Reg[1] = (command >> 8) & 0xff;
if (type == R) {//read
SPI_WriteReg(0x3E,TX_Reg,2);
delayUS(2000);
SPI_ReadReg(0x40, RX_32Byte, 32); //RX_32Byte is a global variable
}
else if (type == W) {
//FET_Control, REG12_Control
TX_Reg[2] = data & 0xff;
SPI_WriteReg(0x3E,TX_Reg,3);
delayUS(1000);
TX_Buffer[0] = Checksum(TX_Reg, 3);
TX_Buffer[1] = 0x05; //combined length of registers address and data
SPI_WriteReg(0x60, TX_Buffer, 2);
delayUS(1000);
}
else if (type == W2){ //write data with 2 bytes
//CB_Active_Cells, CB_SET_LVL
TX_Reg[2] = data & 0xff;
TX_Reg[3] = (data >> 8) & 0xff;
SPI_WriteReg(0x3E,TX_Reg,4);
delayUS(1000);
TX_Buffer[0] = Checksum(TX_Reg, 4);
TX_Buffer[1] = 0x06; //combined length of registers address and data
SPI_WriteReg(0x60, TX_Buffer, 2);
delayUS(1000);
}
}
void DirectCommands(uint8_t command, uint16_t data, uint8_t type)
// See the TRM or the BQ76952 header file for a full list of Direct Commands
{ //type: R = read, W = write
uint8_t TX_data[2] = {0x00, 0x00};
//little endian format
TX_data[0] = data & 0xff;
TX_data[1] = (data >> 8) & 0xff;
if (type == R) {//Read
SPI_ReadReg(command, RX_data, 2); //RX_data is a global variable
delayUS(2000);
}
if (type == W) {//write
//Control_status, alarm_status, alarm_enable all 2 bytes long
SPI_WriteReg(command,TX_data,2);
delayUS(2000);
}
}
// Initlisation of BQ Module
void BQ769x2_Init() {
// Set CS Pin
HAL_GPIO_WritePin(SD_SS_GPIO_Port, SD_SS_Pin, GPIO_PIN_SET);
delayUS(10000);
// Reset BQ Registers
CommandSubcommands(BQ769x2_RESET);
delayUS(60000);
// Configures all parameters in device RAM
// Enter CONFIGUPDATE mode (Subcommand 0x0090) - It is required to be in CONFIG_UPDATE mode to program the device RAM settings
// See TRM for full description of CONFIG_UPDATE mode
CommandSubcommands(SET_CFGUPDATE);
// After entering CONFIG_UPDATE mode, RAM registers can be programmed. When programming RAM, checksum and length must also be
// programmed for the change to take effect. All of the RAM registers are described in detail in the BQ769x2 TRM.
// An easier way to find the descriptions is in the BQStudio Data Memory screen. When you move the mouse over the register name,
// a full description of the register and the bits will pop up on the screen.
// 'Power Config' - 0x9234 = 0x2D80
// Setting the DSLP_LDO bit allows the LDOs to remain active when the device goes into Deep Sleep mode
// Set wake speed bits to 00 for best performance
BQ769x2_SetRegister(PowerConfig, 0x2D80, 2);
// 'REG0 Config' - set REG0_EN bit to disable pre-regulator
BQ769x2_SetRegister(REG0Config, 0x00, 1);
// 'REG12 Config' - Enable REG1 with 3.3V output (0x0D for 3.3V, 0x0F for 5V)
BQ769x2_SetRegister(REG12Config, 0xDD, 1);
// Set SPI MISO Level to 3.3V
BQ769x2_SetRegister(SPIConfiguration,0x60,1);
// Swap to SPI
CommandSubcommands(SWAP_TO_SPI);
// swap comm mode
BQ769x2_SetRegister(SWAP_COMM_MODE,0x0F,1);
/*
// Set DFETOFF pin to control BOTH CHG and DSG FET - 0x92FB = 0x42 (set to 0x00 to disable)
BQ769x2_SetRegister(DFETOFFPinConfig, 0x42, 1);
// Set up ALERT Pin - 0x92FC = 0x2A
// This configures the ALERT pin to drive high (REG1 voltage) when enabled.
// The ALERT pin can be used as an interrupt to the MCU when a protection has triggered or new measurements are available
BQ769x2_SetRegister(ALERTPinConfig, 0x2A, 1);
// Set TS1 to measure Cell Temperature - 0x92FD = 0x07
BQ769x2_SetRegister(TS1Config, 0x07, 1);
// Set TS3 to measure FET Temperature - 0x92FF = 0x0F
BQ769x2_SetRegister(TS3Config, 0x0F, 1);
// Set HDQ to measure Cell Temperature - 0x9300 = 0x07
BQ769x2_SetRegister(HDQPinConfig, 0x00, 1); // No thermistor installed on EVM HDQ pin, so set to 0x00
// 'VCell Mode' - Enable 16 cells - 0x9304 = 0x0000; Writing 0x0000 sets the default of 16 cells
BQ769x2_SetRegister(VCellMode, 0x0000, 2);
// Enable protections in 'Enabled Protections A' 0x9261 = 0xBC
// Enables SCD (short-circuit), OCD1 (over-current in discharge), OCC (over-current in charge),
// COV (over-voltage), CUV (under-voltage)
BQ769x2_SetRegister(EnabledProtectionsA, 0xBC, 1);
// Enable all protections in 'Enabled Protections B' 0x9262 = 0xF7
// Enables OTF (over-temperature FET), OTINT (internal over-temperature), OTD (over-temperature in discharge),
// OTC (over-temperature in charge), UTINT (internal under-temperature), UTD (under-temperature in discharge), UTC (under-temperature in charge)
BQ769x2_SetRegister(EnabledProtectionsB, 0xF7, 1);
// 'Default Alarm Mask' - 0x..82 Enables the FullScan and ADScan bits, default value = 0xF800
BQ769x2_SetRegister(DefaultAlarmMask, 0xF882, 2);
// Set up Cell Balancing Configuration - 0x9335 = 0x03 - Automated balancing while in Relax or Charge modes
// Also see "Cell Balancing with BQ769x2 Battery Monitors" document on ti.com
BQ769x2_SetRegister(BalancingConfiguration, 0x03, 1);
// Set up CUV (under-voltage) Threshold - 0x9275 = 0x31 (2479 mV)
// CUV Threshold is this value multiplied by 50.6mV
BQ769x2_SetRegister(CUVThreshold, 0x31, 1);
// Set up COV (over-voltage) Threshold - 0x9278 = 0x55 (4301 mV)
// COV Threshold is this value multiplied by 50.6mV
BQ769x2_SetRegister(COVThreshold, 0x55, 1);
// Set up OCC (over-current in charge) Threshold - 0x9280 = 0x05 (10 mV = 10A across 1mOhm sense resistor) Units in 2mV
BQ769x2_SetRegister(OCCThreshold, 0x05, 1);
// Set up OCD1 Threshold - 0x9282 = 0x0A (20 mV = 20A across 1mOhm sense resistor) units of 2mV
BQ769x2_SetRegister(OCD1Threshold, 0x0A, 1);
// Set up SCD Threshold - 0x9286 = 0x05 (100 mV = 100A across 1mOhm sense resistor) 0x05=100mV
BQ769x2_SetRegister(SCDThreshold, 0x05, 1);
// Set up SCD Delay - 0x9287 = 0x03 (30 us) Enabled with a delay of (value - 1) * 15 �s; min value of 1
BQ769x2_SetRegister(SCDDelay, 0x03, 1);
// Set up SCDL Latch Limit to 1 to set SCD recovery only with load removal 0x9295 = 0x01
// If this is not set, then SCD will recover based on time (SCD Recovery Time parameter).
BQ769x2_SetRegister(SCDLLatchLimit, 0x01, 1);
*/
// Exit CONFIGUPDATE mode - Subcommand 0x0092
CommandSubcommands(EXIT_CFGUPDATE);
// delay for 10sec
delayUS(10000);
// Sleep mode is enabled by default. For this example, Sleep is disabled to
// demonstrate full-speed measurements in Normal mode.
CommandSubcommands(SLEEP_DISABLE);
delayUS(60000); delayUS(60000); delayUS(60000); delayUS(60000); //wait to start measurements after FETs close
}
// ********************************* BQ769x2 Measurement Commands *****************************************
uint16_t BQ769x2_ReadVoltage(uint8_t command)
// This function can be used to read a specific cell voltage or stack / pack / LD voltage
{
//RX_data is global var
DirectCommands(command, 0x00, R);
if(command >= Cell1Voltage && command <= Cell16Voltage) {//Cells 1 through 16 (0x14 to 0x32)
return (RX_data[1]*256 + RX_data[0]); //voltage is reported in mV
}
else {//stack, Pack, LD
return 10 * (RX_data[1]*256 + RX_data[0]); //voltage is reported in 0.01V units
}
}
void BQ769x2_ReadAllVoltages()
// Reads all cell voltages, Stack voltage, PACK pin voltage, and LD pin voltage
{
int cellvoltageholder = Cell1Voltage; //Cell1Voltage is 0x14
for (int x = 0; x < 16; x++){//Reads all cell voltages
bq_cellv[x] = BQ769x2_ReadVoltage(cellvoltageholder);
cellvoltageholder = cellvoltageholder + 2;
}
Stack_Voltage = BQ769x2_ReadVoltage(StackVoltage);
Pack_Voltage = BQ769x2_ReadVoltage(PACKPinVoltage);
LD_Voltage = BQ769x2_ReadVoltage(LDPinVoltage);
}
uint16_t BQ769x2_ReadCurrent()
// Reads PACK current
{
DirectCommands(CC2Current, 0x00, R);
return (RX_data[1]*256 + RX_data[0]); // current is reported in mA
}
float BQ769x2_ReadTemperature(uint8_t command)
{
DirectCommands(command, 0x00, R);
//RX_data is a global var
return (0.1 * (float)(RX_data[1]*256 + RX_data[0])) - 273.15; // converts from 0.1K to Celcius
}
void BQ769x2_ReadPassQ(){ // Read Accumulated Charge and Time from DASTATUS6
Subcommands(DASTATUS6, 0x00, R);
AccumulatedCharge_Int = ((RX_32Byte[3]<<24) + (RX_32Byte[2]<<16) + (RX_32Byte[1]<<8) + RX_32Byte[0]); //Bytes 0-3
AccumulatedCharge_Frac = ((RX_32Byte[7]<<24) + (RX_32Byte[6]<<16) + (RX_32Byte[5]<<8) + RX_32Byte[4]); //Bytes 4-7
AccumulatedCharge_Time = ((RX_32Byte[11]<<24) + (RX_32Byte[10]<<16) + (RX_32Byte[9]<<8) + RX_32Byte[8]); //Bytes 8-11
}
uint16_t BQ769x2_ReadAlarmStatus() {
// Read this register to find out why the ALERT pin was asserted
DirectCommands(AlarmStatus, 0x00, R);
return (RX_data[1]*256 + RX_data[0]);
}
void BQ769x2_ReadSafetyStatus() { //good example functions
// Read Safety Status A/B/C and find which bits are set
// This shows which primary protections have been triggered
DirectCommands(SafetyStatusA, 0x00, R);
value_SafetyStatusA = (RX_data[1]*256 + RX_data[0]);
//Example Fault Flags
UV_Fault = ((0x4 & RX_data[0])>>2);
OV_Fault = ((0x8 & RX_data[0])>>3);
SCD_Fault = ((0x8 & RX_data[1])>>3);
OCD_Fault = ((0x2 & RX_data[1])>>1);
DirectCommands(SafetyStatusB, 0x00, R);
value_SafetyStatusB = (RX_data[1]*256 + RX_data[0]);
DirectCommands(SafetyStatusC, 0x00, R);
value_SafetyStatusC = (RX_data[1]*256 + RX_data[0]);
if ((value_SafetyStatusA + value_SafetyStatusB + value_SafetyStatusC) > 1) {
ProtectionsTriggered = 1; }
else {
ProtectionsTriggered = 0; }
}
void BQ769x2_ReadPFStatus() {
// Read Permanent Fail Status A/B/C and find which bits are set
// This shows which permanent failures have been triggered
DirectCommands(PFStatusA, 0x00, R);
value_PFStatusA = (RX_data[1]*256 + RX_data[0]);
DirectCommands(PFStatusB, 0x00, R);
value_PFStatusB = (RX_data[1]*256 + RX_data[0]);
DirectCommands(PFStatusC, 0x00, R);
value_PFStatusC = (RX_data[1]*256 + RX_data[0]);
}
// BQ State Machine -> All measurement functions will be called here
void bq_state_machine(void)
{
// Check if FULLSCAN is complete. If set, new measurements are available
// Read All Cell Voltages, Stack Voltage, Pack Voltage, Load Voltage
BQ769x2_ReadAllVoltages();
// Read Battery Pack Current
Pack_Current = BQ769x2_ReadCurrent();
// Read Cell temperature
Temperature[0] = BQ769x2_ReadTemperature(TS1Temperature);
// Read Cell temperature
Temperature[1] = BQ769x2_ReadTemperature(TS3Temperature);
// Clear Full scan bit
DirectCommands(AlarmStatus, 0x0080, W); // Clear the FULLSCAN bit
}

