主题中讨论的其他器件: BQ78350、 BQ76940、 MSP430G2553、 BQ76940EVM、 MSP430FR5994、 MSP430WARE、 MSP-EXP430FR5994、 MSP430F5338、 BQSTUDIO
TRM 负责电池管理的各个方面、包括配置/编程寄存器以启用/禁用某些功能。 但是、由于我只需要从微控制器连接 BMS 即可从 BQ78350-R1的配置值中获取/查询数据、因此是否需要进行一系列初始化或配置才能读取所需的值?
This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
TRM 负责电池管理的各个方面、包括配置/编程寄存器以启用/禁用某些功能。 但是、由于我只需要从微控制器连接 BMS 即可从 BQ78350-R1的配置值中获取/查询数据、因此是否需要进行一系列初始化或配置才能读取所需的值?
您好、Jan、
不幸的是、我无法披露整个代码。
简而言之、我们有一个状态机、它通过 SMBus 定期读取电池状态、然后对这些读数执行操作。
我可以共享位和段:
例如、要读取电池电压、您需要执行以下操作:
// 0x09对应于电池电压的寄存器地址 //第二个参数(0)对应于我们发送的命令的索引(用于回调) // batt_data_buffer 是存储数据的缓冲区 // battery_rx_done 是 SMBus 接口调用的回调函数,用于指示数据已被读取 smbus_requestWord (0x09、0、batt_data_buffer、batter_rx_done);
和读取操作状态(通过制造商访问命令完成):
// 0x0054参数对应于制造商访问中的命令 ID //第二个参数(1)对应于我们发送的命令的索引(用于回调) // batt 数据缓冲区是存储数据的缓冲区 // battery_rx_done 是 SMBus 接口调用的回调函数,用于指示数据已读取 smbus_requestMfrAccessReg (0x0054、1、batt 数据缓冲区、battery_rx_done);
SMBus 使用 BATTERY_Rx_DONE 回调函数来指示数据已接收。 它包含一个切换用例、然后将数据复制到适当的位置
void battery_rx_done (unsigned char id){ switch (id){ case 0://voltage 电压= batt 数据缓冲器[0]|(batt 数据缓冲器[1]<< 8); 中断; 案例1://操作状态 operation_status[0]= batt_data_buffer[6]; operation_status[1]= batt_data_buffer[5]; operation_status[2]= batt_data_buffer[4]; operation_status[3]= batt_data_buffer[3]; //检查是否存在永久性故障 if (operation_status[2]和0x10){ //检测到永久故障 Failure_Detected = 1; } 中断; … ...} }
您需要确定您将需要什么、在哪里以及如何使用您读取的数据。
此致、
米歇尔
您好、Terry 和 Michel。
我正在尝试使用 MSP430FR5994 (Launch Pad)实施一个非常简单的代码、以便通过 SMBus 读取 bq76940EVM 的电池电压。
我以电池充电器的 SLAA476A 示例和 Michel 提供的代码为基础。
不幸的是、我没有取得任何成功。 当我运行代码时、发送的唯一帧是下面显示的帧。
当我在代码中放置一些断点时,它 总是停留在 SMBus_NotReady()中,或者在中断的情况4中,当我注释它时。
我出了什么问题? 你有什么建议吗? 我应该在哪里查看?
代码:
smbus.h
#ifndef SMBus_H_ #define SMBus_H_ //通用定义 #define SMBus_MASTER_MODE_WRITE 0x00 #define SMBus_MASTER_MODE_ADDRE 0x01 #define SMBus_SLAVE_MODE_WRITE 0x02 #define SMBus_SLAVE_MODE_READ 0x03 #define SMBus_MODE_ADDR_ADDRE 0x09 #define PM_PACE_ADDRE #define 0x16 #define PM_FAIL_T=#define 000#define 000#define PM_FAIL #define 000#define SMBus_MODE_FLAG #define 000#define 000#define 000#define SMBus_ CRC8_Poly 0x07 #define CRC8_INIT_REM 0x0 // SMBus 数据声明 #define SMBus_DATA_TO_SLAVE 32 //发送表长度 #define SMBus_DATA_FIT_SLAVE 32 //接收表长度 // SMBus 命令定义 #define SBS_CMD_TEMP_TEMD_TEMP_VOLTAGE 0x09 #define SBS_CURRENT_0X0CCS_CURRENT #define CCS_CMD_CURRENT#define 0_CMD_CURRENT#define 0x14 #define SBS 0x16 #define SBS_CMD_Design_VOLTAGE 0x19 #define SBS_CMD_FTORY_NAME 0x20 #define SBS_CMD_DEVICE_NAME 0x21 #define SBS_CMD_CELL_VOLTAGE_4 0x3C #define SBS_CMD_CELL_VOLTAGE_3 0x3D #define SBS_CMD_COLL_STATEART_BYTCG #define 电池充电寄存器0x3E #define SBS #define COUNT_STATEARTTER/SBS COUNT_STATEART/ COYTC_BATERTTER1 SBS_REG_BATTERYSTATUS_FC BIT5 //完全充电 #define BUS_1 1 #define BUS_1 2 #define SMBus_MASTER_MODE 0 #define SMBus_SLAVE_MODE 1 #define SMBus_START_FLAG_SET 1 #define SMBus_START_FLAG_RESET 0 //外部全局变量声明 extern SMBus_Data_SLAVE_TXCH_DATA_SLAVE_SET 1 #define SMBus_SLAVE_ST_SLAVE_RT_SLAVE_RT_DATA_SET 1 #define 0 //从 SMBus_SLAVE_TXCH_SLAVE_OBJART/SLAVE_TXQ_SLAVE_RT_DATA_SLAVE_OBJART/无 符号 extern unsigned char * pRXData;//指向 RX 数据的指针 extern unsigned char RXByteCounter; extern unsigned char RXFlag;//数据接收标志 extern unsigned char TXFlag;//数据传输标志 extern unsigned char RWFlag;//读取/写入标志 extern unsigned char SMBus_Start_Flag; extern unsigned char size_bytes;extern unsigned char size_bytes; //函数原型 unsigned char SMBUS_NotReady(); 静态 unsigned short crc8MakeBitwise (unsigned char CRC、unsigned char Poly、unsigned char *Pmsg、unsigned int Msg_size); #endif /*SMBus_H_*/
main.c
#include #include //全局变量 unsigned char SMBus_Data_to_Slave[SMBus_data_to_Slave]; unsigned char SMBus_Data_fin_Slave[SMBus_data_fin_slave]; unsigned char * pTXData = 0x0; //指向 TX 数据的指针 unsigned char TXByteCounter = 0; unsigned char * pRXData = 0x0; //指向 RX 数据的指针 unsigned char RXByteCounter = 0; unsigned char RXFlag = flag_FAIL; //数据接收标志 unsigned char TXFlag = FLAG_FAIL; //数据传输标志 unsigned char RWFlag = SMBus_master_mode_read; //读取/写入标志 无符号 char SMBus_Start_Flag = SMBus_start_FLAG;//是否已启动事务? unsigned char smbus_access_status = 0x0; unsigned char crc_msg_size = 0; unsigned char crc_master_generated = 0; unsigned char crc_slave_generated = 0; unsigned char size_in_bytes; unsigned char smbus_command; static short crc8Makebwise char msg (char char cCRC、Bitg、unsigned pig、unsigned pig unsigned int i、j、carry; unsigned char msg; CRC =* Pmsg++; //加载到"crc"中的第一个字节 for (i = 0;i < Msg_size-1;I ++) { MSG =* Pmsg++; //在"msg"中加载下一个字节 for (j = 0;j < 8;j++) { 进位= CRC 和0x80; //检查 MSB=1 CRC =(CRC << 1)|(msg >> 7);//将下一个字节的1位移入 CRC if (carry) CRC ^= Poly; //如果 MSB=1,执行 XOR MSG <<= 1; //左移 msg 字节1 } } //前一个循环计算输入位流的 CRC。 为此、 //填充8个尾随零,结果值的 CRC 为 //已计算。 这给出了输入位流的最终 CRC。 for (j = 0;j < 8;j++) { 进位= CRC 和0x80; CRC <<= 1; if (carry) CRC ^= Poly; } return (CRC); } unsigned char SMBus_NotReady() { return ((UCB1STATW 和 UCBBUSY)||(UCB1STATW 和 UCSCLLOW)); } int main (void) { WDTCTL = WDTPW | WDTHOLD; //停止 WDT //禁用 GPIO 上电默认高阻抗模式以激活 //先前配置的端口设置 PM5CTL0 &=~LOCKLPM5; //时钟设置 CSCTL0_H = CSKKEY_H; //解锁 CS 寄存器 CSCTL1 = DCOFSEL_0; //将 DCO 设置为 x MHz CSCTL2 = SELA_VLOCLK | SELESS__DCOCLK | SELM_DCOCLK;//设置 SMCLK = M0CLK = DCO、ACLK = VLOCLK CSCTL3 = DIVA__1 | DIVM_1 | DIVM__1;//设置分频器 CSCTL2 = SELA_VLOCLK | SELESS__DCOCLK | SELM_DCOCLK;//设置 SMCLK = M0CLK = DCO、ACLK = VLOCLK CSCTL0_H = 0; //锁定 CS 寄存器 //将 UCB 设置为 I2C 模式 P5SEL0 |= BIT0 | BIT1; P5SEL1 &=~(BIT0 | BIT1); UCB1CTLW0 = UCSWRST; //将 eUSCI_B 置于复位状态 UCB1CTLW0 |= UCMODE_3 | UCMST | UCSSEL_SMCLK; // I2C 主控模式、SMCLK UCB1BRW = 0x0A; //波特率= SMCLK/UCBxBRW (十六进制) UCB1CTLW0 &=~UCSWRST; //清除复位寄存器 UCB1IFG &= 0xFF; //清除所有标志 UCB1I2CSA = BQ78350_ADDR; //从机地址是有效载荷的第一个字节 UCB1CTLW1 &=~UCSWRST; //清除 SW 复位,恢复操作 UCB1IE |= UCTXIE0 | UCNACKIE | UCRXIE; //发送和 NACK 中断使能 _enable_interrupt (); while (1) { while (SMBus_NotReady()); //等待 SMBus 变为空闲状态 SMBus_COMMAND = SBS_CMD_VOLTAGE; size_in_bytes = 2; TXByteCounter = 1; //目前 SMBus 命令的 TX 1字节 pTXData =&(smbus_command); //根据 SBS 命令规范接收字节数 //为 PEC 加一个额外的字节 RXByteCounter = size_in_bytes + 1; //将 RX 字节存储在全局数组中 pRXData =(unsigned char*) SMBus_Data_fin_Slave; //初始化标志以失败,除非成功 RXFlag = FLAG_FAIL; RWFlag = SMBus_MASTER_MODE_READ; //读取模式 while (UCB1CTL1 & UCTXSTP); //是否发送停止条件? TA1CTL = TASSEL_ACLK + TACLR + MC_UP; //开始计时器 A 测量超时 SMBus_Start_Flag = SMBus_start_FLAG_set; UCB1CTL1 |= UCTR + UCTXSTT; //开始! while (UCB1CTL1 & UCTXSTT); //确保发送起始条件 //从设备是否确认了自己的地址? 如果是、则继续 //使用命令和数据包。 if ((RXFlag!= FLAG_NACK)&&(RXFlag!= FLAG_FAIL)){ _bis_SR_register (LPM0_bits + GIE); //输入带中断的 LPM0 } // PEC 字节处理 CRC_msg[0]= SMBus_SLAVE_ADDRESS << 1; // R/W 位为低电平的从器件地址 crc_msg[1]= smbus_command; //命令字节 CRC_msg[2]=(SMBus_SLAVE_ADDRESS << 1)+ 1;//从地址、R/W 位为高电平 CRC_msg[3]= SMBus_Data_From_Slave[0]; //第一个字节 RX CRC_msg[4]= SMBus_Data_From_Slave[1]; //第二个字节 RX CRC_SLAVE_Generated = SMBus_Data_From_Slave[2];//存储从器件的 PEC 字节 CRC_msg_SIZE = 5; //字节数 /* CRC 函数调用,生成 CRC 字节与从器件 CRC*/进行比较 crc_master_generated = crc8MakeBitwise (CRC8_INIT_REM、CRC8_Poly、CRC_msg、CRC_msg_size); // PEC 字节验证 if (crc_master_generated = crc_slave_generated) {smbus_access_status = SMBus_PEC_Pass; // PEC 字节已验证 } 其他 {smbus_access_status = SMBus_PEC_FAIL; // PEC 测试失败 } } /* *函数名称:I2C/SMBus 中断矢量服务例程(USCI B1) * 说明:此 ISR 在 主从配置中将 MSP430 I2C USCI 模块配置为 RX 和 TX * SMBus 命令和数据。 *** / #pragma vector = USCI_B1_vector __interrupt void USCI_B1_ISR (void) { switch (_even_in_range (UCB1IV、12)) }{ 情况0: 中断; 案例2: // ALIFG 中断; 案例4: // NACKIFG UCB1CTLW1 |= UCTXSTP; //发送停止 UCB1IFG &=~UCNACKIFG; //清除 NACK 标志 _BIC_SR_REGISTER_ON_EXIT (LPM0_BITS);//退出 LPM0 RXFlag = FLAG_NACK; //数据操作失败 TXFlag = FLAG_NACK; //数据操作失败 中断; 案例6: // STTIFG TA1CTL = TASSEL_ACLK + TACLR + MC_UP; SMBus_Start_Flag = SMBus_start_FLAG_set; 中断; 案例8: // STPIFG _BIC_SR_REGISTER_ON_EXIT (LPM0_BITS);//退出 LPM0 RXFlag = FLAG_SUCCESS; //将接收到的数据 RX 标志设置为成功 中断; 案例10: // RXIFG IF (RWFlag = SMBus_MASTER_MODE_READ) { RXByteCounter--; //递减 RX 字节计数器 IF (RXByteCounter) { *pRXData+= UCB1RXBUF; //剩余要接收的多个字节 TA1CTL = tassel_ACLK + TACLR + MC_UP; } 其他 { *pRXData= UCB1RXBUF; //要接收的最后一个字节 _BIC_SR_REGISTER_ON_EXIT (LPM0_BITS);//退出 LPM0 RXFlag = FLAG_SUCCESS; //将接收到的数据 RX 标志设置为成功 } if (RXByteCounter = 1) //只剩下一个字节? UCB1CTLW1 |= UCTXSTP; //生成 I2C 停止条件 //如果只读取1个字节,则停止条件 //应该已经发送 } 否则、如果(RWFlag = SMBus_SLAVE_MODE_WRITE) { *pRXData++= UCB1RXBUF; //将每个接收到的字节填充到数组 中} 中断; 情况12: // TXIFG //当 UCB1TXBUF 为空时置1 IF (RWFlag = SMBus_MASTER_MODE_READ) { IF (TXByteCounter) //如果有要传输的内容 { UCB1TXBUF =*pTXData++; //加载 TX 缓冲区 TXByteCounter--; //测量 TX 字节计数器 RXFlag = FLAG_SUCCESS; } 其他 //无需再传输 { UCB1CTLW1 &=~UCTR; //接收器模式 UCB1IFG &=~UCTXIFG; //清除 USCI_B0 TX int 标志 UCB1CTLW1 |= UCTXSTT; // I2C 重新启动(SMBus 协议) if (RXByteCounter = 1){ //只接收一个字节? //请参阅 USCI - I2C 模式中的 I2C 主接收器模式部分 // 5xx 系列用户指南的一章(SLAU208G -第607页) while (UCB1CTLW1和 UCTXSTT); //已发送开始条件轮询 UCB1CTLW1 |= UCTXSTP; //生成 I2C 停止条件 } } 否则、如果(RWFlag = SMBus_MASTER_MODE_WRITE) { IF (TXByteCounter) //如果有要传输的内容 { UCB1TXBUF =*pTXData++; //加载 TX 缓冲区 TXByteCounter--; //测量 TX 字节计数器 TXFlag = FLAG_SUCCESS; } 其他 { UCB1CTLW1 |= UCTXSTP; // I2C 停止条件 UCB1IFG &=~UCTXIFG; //清除 USCI_B0 TX int 标志 _BIC_SR_REGISTER_ON_EXIT (LPM0_BITS);//退出 LPM0 TXFlag = FLAG_SUCCESS; } } TA1CTL = TASSEL_ACLK + TACLR + MC_UP; 中断; 默认值: 中断; }
谢谢、
您好、Mauro、
我看到两个可能的问题:
要检查它是否是上升沿问题、请降低波特率。 (您可以使用的最小值为10kHz)
如果是外设逻辑问题、我建议您查看 Resource Explorer 中的示例代码。
在 CCS 中、在"View"菜单下或在以下地址在线:
在 Resource Explorer 中、转到以下路径:
软件-> MSP430Ware XXXX ->开发工具-> MSP-EXP430FR5994 ->外设示例->寄存器级-> MSP430FR5994
在该文件夹下、查找以 msp430fr599x_euscib_i2c._xxx 开头的文件
此外、请在附近查看 MSP430FR599x 的用户指南以进行检查
此致、
米歇尔
代码如下:
#include #include #define BQ78350_ADDR (0x16>>>1) #define MFR_BLK_ACCESS 0x44 #define MAX_queue 75 #define MAX_RX_LEN 50 #define MAX_TX_LEN 10 #define CRC8_Poly 0x07 #define CRC8_INIT_REM 0x00 int flag_50ms; int flag_100ms; int flag_500ms; int flag_1000ms; int flag_2000ms; int time = 0; int count_50ms; int count_100ms; int count_500ms; int count_1000ms; int count_2000ms; unsigned char batt_data_buffer[2]={0x00、0x00}; unsigned char 电压; typedef struct message_t{ 无符号字符地址; unsigned char mess_dir; unsigned char is 块; unsigned char TX_DATA[MAX_TX_LEN]; unsigned int tx_cnt; unsigned char * rx_data; unsigned int rx_cnt; unsigned char nack; void (* rx_done 回调)(unsigned char); unsigned char id; }message; typedef struct message_queue_t{ 消息 mess_q[MAX_QUE]; unsigned int mess_q_cnt; unsigned int mess_q_in; unsigned int Poly_q_out; }message_queue; 静态 unsigned char crc8MakeBitwise (unsigned char CRC、unsigned char Poly、unsigned char * Pmsg、unsigned int Msg_size); smbus_state cur_state = smbus_idle; unsigned int error_flag; unsigned int TX_don_flag; unsigned int mess_delay unsigned long nack_cntr; volatile message * cur_mess; message_queue smbus_queue; volatile unsigned char * ptxData; volatile unsigned char TXByteCtr; volatile unsigned char RXByteCtr; unsigned char read_mess= 1; unsigned char write_mess= 0; unsigned int smbus_Is_running (void){void} return (smbus_queue.mess_q_cnt > 0); } unsigned int smbus_get_error (void){ 返回 ERROR_FLAG; } void smbus_read_nack (void){ __no_operation(); } 无符号字符 smbus_is_queue_full (void){ if (smbus_queue.mess_q_cnt = MAX_queue) 返回1; 返回0; } smbus_state smbus_get_state (void){ 返回 cur_state; } void smbus_q_init (void){ SMBus_queue.mess_q_cnt = 0; SMBus_queue.mess_q_in = 0; SMBus_queue.mess_q_out = 0; } //此函数假设 SMBus 模块已正确配置。 void smbus_reset (void){ UCB2CTLW0 |= UCTXSTP;//立即强制停止 TA1CTL = MC__STOP; //停止超时计数器 UCB2CTLW0 |= UCSWRST; //重置 SMBus 模块 CUR_ST态= smbus_down; } void smbus_resume (void){ UCB2CTLW0 &=~UCSWRST;//启用 SMBus 模块 } //返回1 (如果有正在处理的消息)。 否则、返回0。 unsigned char smbus_process (void){ //unsigned char stream[MAX_RX_LEN + MAX_TX_LEN]; 如果(ERROR_FLAG = 1){ CUR_ST态= smbus_no_resp; } 如果(ERROR_FLAG = 2){ CUR_ST态= smbus_failure; } CUR_mess=&smbus_queue.mess_q[smbus_queue.mess_q_out]; 开关(cur_state){ 案例 smbus_idle: case smbus_wait: if (smbus_queue.mess_q_cnt > 0){ IF (UCB2STAT 和 UCBBUSY){ CUR_ST态= smbus_wait; } 否则{ ERROR_FLAG = 0; mess_DONE_FLAG = 0; TX_DONE_FLAG = 0; Timer_delay_over = 1; CUR_ST态= smbus_delay; } } 否则{ CUR_ST态= smbus_idle; 返回0; } 中断; case smbus_delay: 如果(timer_delay_over = 1){ PTxData =(unsigned char *)(&cur_mess->TX_DATA[0]); PRxData =(unsigned char *)(&cur_mess->rx_data[0]); TXByteCtr = cur_mess->TX_cnt; RXByteCtr = cur_mess->rx_cnt; IF (UCB2CTLW0和 UCTXSTP){ //是否发送停止条件? 中断; } CUR_ST态= smbus_tx; //更改发送状态 TA1CTL = tassel_SMCLK | TACLR | MC__Continuous | TAIE;//开始延迟 UCB2CTLW0 |= UCTR | UCTXSTT; // I2C TX,启动条件 } 中断; 案例 smbus_done: /* *目前已忽略 PEC if (cur_mess->mess_dir = read_mess){ STREAM [0]=(UCB2I2CSA << 1)&~0x01; stream[1]= cur_mess->address; 对于(i = 0;i < cur_mess->TX_cnt;i++){ stream[i + 1]= cur_mess->TX_DATA[i]; } stream[cur_mess->tx_cnt]= cur_mess->address | 0x01; 对于(i = 0;i < cur_mess->rx_cnt;i++){ stream[i + cur_mess->TX_cnt + 1]= cur_mess->rx_data[i]; } PEC = crc8MakeBitwise (CRC8_INIT_REM、CRC8_Poly、&(STREAM[0])、 cucr_mess->tx_cnt + cur_mess->rx_cnt + 2); if (PEC =* PRxData){ //更新状态机的 BQ78350数据 //P1OUT ^= 0x01; } //否则{PEC}中的//错误 } * //else {我们有一条仅 TX 消息} 如果(smbus_queue.mess_q_cnt > 0){//删除最后一个消息队列 if (cur_mess->mess_dir = read_mess){ if (cur_mess->NACK) Nack_cntr++; //无错误地完成读取,跳转至回调(如果有)。 否则(cur_mess->rx_done 回调) (CUR_mess->Rx_DONE_CALLBACE)(cur_mess->id); } 否则(cur_mess->nack){ Nack_cntr++; } SMBus_queue.mess_q_out++; if (smbus_queue.mess_q_out = MAX_queue) smbus_queue.mess_q_out = 0; SMBus_queue.mess_q_cnt--; } CUR_ST态= smbus_wait; 中断; 案例 smbus_tx: 如果(TX_DONE_FLAG) cur_state = smbus_Rx; //Drop Through 案例 smbus_Rx: 如果(mess_done 标志) cur_state = smbus_done; 中断; 案例 smbus_down: 案例 smbus_failure: 案例 smbus_no_resp: //不执行任何操作 中断; 默认值: CUR_ST态= smbus_wait; 中断; } 返回1; } void battery_rx_done (unsigned char id){ switch (id){ case 0://voltage 电压= batt_data_buffer[0]|(batt_data_buffer[1]<< 8); break; 默认值:break; } void smbus_requestWord (unsigned char addr、unsigned char id、unsigned char * return_data、void (* rx_do回 调)(unsigned char))){ 消息*new_mess; if (smbus_queue.mess_q_cnt < MAX_queue){ new_mess=&smbus_queue.mess_q[smbus_queue.mess_q_in]; //将数据添加到消息队列 new_mess->address = BQ78350_ADDR; new_mess->mess_dir = read_mess; new_mess->is 块= 0; new_mess->TX_DATA[0]= addr; new_mess->TX_cnt = 1; new_mess->Rx_data = return_data; new_mess->rx_cnt = 3; new_mess->NACK = 0; new_mess->Rx_done 回调= Rx_done 回调; new_mess->id = id; //更新队列指针 SMBus_queue.mess_q_in++; 如果(smbus_queue.mess_q_in = MAX_queue) smbus_queue.mess_q_in = 0; SMBus_queue.mess_q_cnt++; } } void smbus_init (void){ TA1CCTL0 = CCIE; //启用 TACCR0中断 TA1CCTL1 = CCIE; //启用 TACCR1中断 TA1CCTL2 = CCIE; //启用 TACCR2中断 TA1CCR0 = 5000; TA1CCR1 = 1000; TA1CCR2 = 12500; TA1CTL = tassel_SMCLK | MC__Continuous; // SMCLK、连续模式 //为 I2C 配置 GPIO P7SEL0 |= BIT0 | BIT1; P7SEL1 &=~(BIT0 | BIT1); //将 USCI_B2配置为 I2C 模式 UCB2CTLW0 = UCSWRST; //将 eUSCI_B 置于复位状态 UCB2CTLW0 |= UCMODE_3 | UCMST | UCSSEL_SMCLK;// I2C 主控模式、SMCLK UCB2BRW = 0x64; //波特率= SMCLK /8 UCB2CTLW0 &=~UCSWRST; //清除复位寄存器 UCB2IE |= UCTXIE0 | UCNACKIE; //发送和 NACK 中断使能 UCB2I2CSA = BQ78350_ADDR; //配置从地址 CUR_ST态= smbus_idle; ERROR_FLAG = 0; Timer_delay_over = 0; TX_DONE_FLAG = 0; mess_DONE_FLAG = 0; Nack_cntr = 0; SMBus_q_init(); } int main (void){ WDTCTL = WDTPW | WDTHOLD; //停止 WDT //配置 GPIO P1DIR |= BIT0; // LED 1.0 P1OUT |= BIT0; P1DIR |= BIT1; // LED 1.2 P1OUT |= BIT1; P1DIR |= BIT2; // GPIO 1.2 P1OUT |= BIT2; PJSEL0 |= BIT4 | BIT5; // XT1 //禁用 GPIO 上电默认高阻抗模式以激活 //先前配置的端口设置 PM5CTL0 &=~LOCKLPM5; //时钟 CSCTL0_H = CSKKEY_H; //解锁 CS 寄存器 CSCTL1 = DCOFSEL_0; //将 DCO 设置为1MHz CSCTL2 = SELA_LFXTCLK | SELESS__DCOCLK | SELM_DCOCLK;//设置 ACLK = XT1;MCLK = DCO CSCTL3 = DIVA__1 | DIVM_1 | DIVM__1;//将所有分频器设置为1 CSCTL4 &=~LFXTOFF; 操作 { CSCTL5 &=~LFXTOFFG; //清除 XT1故障标志 SFRIFG1 &=~OFIFG; } while (SFRIFG1和 OFIFG); //测试振荡器故障标志 CSCTL0_H = 0; //锁定 CS 寄存器 _enable_interrupt (); SMBus_init(); SMBus_resume(); while (1){ SMBus_requestWord (0x09、0、batt_data_buffer、battery_rx_done); SMBus_process(); } } #if defined (__TI_Compiler_version__)|| defined (__IAR_systems_ICC__) #pragma vector = EUSCI_B2_vector __interrupt void USCI_B2_ISR (void) #Elif defined (__GNU__) void __attribute__(interrupt (eUSCI_B2_vector))#USCI_B2_ISR (void Ub2_vector) )(void UCI_B2_BUF Compiler_B2_b2_b2_b2)(void Ub2编译器# #endif { 开关(__evo_in_range (UCB2IV、USCI_I2C_UCBIT9IFG)) { USCI_NONE 案例: 中断; //向量0:无中断 USCI_I2C_UCALIFG 案例:中断; //向量2:ALIFG USCI_I2C_UCNACKIFG 案例: //向量4:NACKIFG UCB2CTLW0 |= UCTXSTP; //发送停止 UCB2IFG &=~UCNACKIFG; //清除 NACK 标志 TA1CTL = MC__STOP; //关闭超时计时器 CUR_mess->NACK = 1; //事务失败。 ISR 处理后无需唤醒。 mess_DONE_FLAG = 1; 中断; 案例 USCI_I2C_UCSTTIFG:中断; //向量6:STTIFG 案例 USCI_I2C_UCSTPIFG:中断; //向量8:STPIFG USCI_I2C_UCRXIFG3案例:中断; //向量10:RXIFG3 USCI_I2C_UCTXIFG3案例:中断; //向量12:TXIFG3 USCI_I2C_UCRXIFG2案例:中断; //向量14:RXIFG2 USCI_I2C_UCTXIFG2案例:中断; //向量16:TXIFG2 USCI_I2C_UCRXIFG1案例:中断; //向量18:RXIFG1 USCI_I2C_UCTXIFG1案例:中断; //向量20:TXIFG1 USCI_I2C_UCRXIFG0案例: //向量22:RXIFG0 TA1CTL = MC__STOP; //关闭超时计时器 RXByteCtr---; //递减 RX 字节计数器 if (RXByteCtr){ * PRxData = UCB2RXBUF; //剩余要接收的多个字节 PRxData++; //if 块访问、第一个字节是块长度 RXByteCtr =(cur_mess->is 块)? (UCB2RXBUF +1):RXByteCtr; //复位是块标志,因此长度只更新一次 CUR_mess->IS 块= 0; TA1CTL = tassel_SMCLK | TACLR | MC__Continuous | TAIE;//开始延迟 } 否则{ * PRxData = UCB2RXBUF; //要接收的最后一个字节 mess_DONE_FLAG = 1; //显示读取完成的标志 __BIC_SR_REGISTER_ON_EXIT (LPM3_BITS);//退出 LPM3,因为在读取后有一些处理要执行 } IF (RXByteCtr = 1) //只剩下一个字节? UCB2CTLW0 |= UCTXSTP; //生成 I2C 停止条件 //如果只读取1个字节,则停止条件 //应该已经发送 中断; USCI_I2C_UCTXIFG0案例: //向量24:TXIFG0 TA1CTL = MC__STOP; //关闭超时计时器 IF (TXByteCtr) //如果有要传输的内容 { UCB2TXBUF =* PTxData++; //加载 TX 缓冲区 TXByteCtr --; //测量 TX 字节计数器 TA1CTL = tassel_SMCLK | TACLR | MC__Continuous | TAIE;//开始延迟 } //无需再传输 否则、如果(cur_mess->mess_dir = read_mess){//更改为接收模式 UCB2CTLW0 &=~UCTR; //接收器模式 UCB2IFG &=~UCTXIFG; //清除 USCI_B0 TX int 标志 UCB2CTLW0 |= UCTXSTT; // I2C 重新启动(SMBus 协议) TX_DONE_FLAG = 1; TA1CTL = tassel_SMCLK | TACLR | MC__Continuous | TAIE;//开始延迟 } 否则{ //仅发送,停止传输 UCB2CTLW0 &=~UCTR; UCB2IFG &=~UCTXIFG; //清除 USCI_B0 TX int 标志 UCB2CTLW0 |= UCTXSTP; //生成 I2C 停止条件 CUR_ST态= smbus_done; //显示读取完成的标志 mess_Done 标志= 1; } 中断; 案例 USCI_I2C_UCBCNTIFG:break; //向量26:BCNTIFG USCI_I2C_UCCLTOIFG 案例:中断; //向量28:时钟低电平超时 USCI_I2C_UCBIT9IFG 案例:中断; //向量30:第9位 默认值:break; } } #pragma vector = Timer1_A0_vector __interrupt void Timer1_A0_ISR (void) { count_50ms++; count_100ms++; count_500ms++; count_1000ms++; count_2000ms++; 如果(count_50ms>=5){count_50ms = 0;flag_50ms = 1;} 如果(count_100ms>=10){count_100ms = 0;flag_100ms = 1;} 如果(count_500ms>=50){count_500ms = 0;flag_500ms = 1;} 如果(count_1000ms>=100){count_1000ms = 0;flag_1000ms = 1;} 如果(count_2000ms>=200){count_2000ms = 0;flag_2000ms = 1;} TA1CCR0 += 5000; } #pragma vector = Timer1_A1_vector //针对 CCR1、CCR2 __interrupt void Timer1_A1_ISR (void) { switch (TA1IV){ 情况0x02: // CCR1 TA1CTL = MC__STOP; //关闭超时计时器 TA1CCTL1 = 0; //禁用此比较点 Timer_delay_over = 1; //将标志设置为结束延迟 TA1CCR1 += 1000; 中断; 情况0x04: // CCR2 TA1CTL = MC__STOP; //关闭超时计时器 UCB2CTLW0 |= UCTXSTP; //在 SDA 线上发送 STOP UCB2CTLW0 |= UCSWRST; //启用 SW 复位 UCB2CTLW0 &=~UCSWRST; UCB2IE |= UCTXIE + UCRXIE + UCNACKIE;//启用 TX、RX、NACK 中断 ERROR_FLAG = 2; //超时标志 TA1CCR2 += 12500; 中断; } }
您好、Mauro、
参考手册中给出的 BQ78350地址已移位。 即:当您从器件读取时、微控制器应发送0x16作为其第一个字节、并在您写入时发送0x17。 MSP430F5338微控制器需要用户设置从器件地址、以在主模式下设置 I2C。 在有效载荷中、包含地址的第一个字节必须移回才能正确发送。 请确保阅读 UC 用户指南以正确配置 I2C 外设。
SDA 和 SCL 线路在发送或接收后都应变为高电平、如果情况并非如此、那么您的微控制器会出于某种原因挂起线路。 我假设您在线路上有上拉电阻器。
根据您提供的信息、可能是 I2C 外设未配置或逻辑不正确。
遗憾的是、我没有时间检查代码和进行调试、但下面是建议的调试步骤:
打开 Software -> MSP430Ware xxx -> Libraries -> SMBusLib
下载内容:
此致、
米歇尔
您好、Michel。
每次发送 smbus_requestWord ()/smbus_requestMfrAccessReg ()时,我都必须调用 smbus_process()函数,还是有其他方法可以执行这些任务?
我问这个、因为我看到有队列逻辑、但我不理解。
int main (void){ WDTCTL = WDTPW | WDTHOLD; //停止 WDT init_gpio(); init_clock(); init_timer0(); _enable_interrupt (); SMBus_init(); SMBus_resume(); while (1){ SMBus_requestWord (0x0E、0、batt_data_buffer、battery_rx_done); SMBus_process(); SMBus_requestWord (0x09、1、batt_data_buffer、battery_rx_done); SMBus_process(); } }
谢谢、
您好、Mauro、
该过程是一个状态机、用于监控发送/接收的消息。 所有的消息处理都是通过中断(在后台)完成的、并且过程会检查中断的哪个步骤。 因此、每次运行该函数时、它只能"前进"一步。
简单地说、器件进入该过程、检查状态是否已更改、在必要时执行任何操作并退出。 这使得 SMBus 的工作方式与非阻塞功能相似。 这使您可以在发送 SMBus 消息时执行其他操作。 例如、您可以同时处理 UART 端口上的数据。
当您有操作系统时、所有这些操作都将在后台处理、而无需注意任何事项。 如果没有操作系统、则需要显式执行此操作。
话虽如此、您必须尽可能频繁地进入 smbus_process。 如果您有任何类似 SMBus 的其他进程、请确保它们也不会阻止、因为它会阻止 SMBus 执行并导致错误。 这也意味着您不能在代码中使用任何睡眠函数。
那么、这里有一些应该有所帮助的伪代码
while (1){ //更新状态机 smbus_process (); //出于 此伪代码的目的,我们假设您有一个运行 //的计时器,并且您正在检查该时间段是否已过去。 if (一些电池超时){ //这将排队两条消息。 它们将一个接一个地被处理。 //您可以将许多消息排队。 您可以检查数据是否已保存 //通过 SMBus 回调设置一些变量来接收。 //只需确保队列中的消息数不会超过 smbus 可以处理的消息数。 //(即确保超时时间足够长,以便在排队新消息之前发送所有消息) SMBus_requestWord (0x09、1、batt_data_buffer、battery_rx_done); SMBus_requestWord (0x0E、0、batt_data_buffer、battery_Rx_done); }
我希望这能为您清除问题。
此致、
米歇尔
您好、Michel。
很抱歉打扰了您、但您的帮助对我的项目非常重要。
我正在执行一些测试、现在我尝试从 bq76940EVM 读取一些变量并发送一些命令。 例如、我每500ms 读取一次电池的电压、并每2000ms 发送一条命令来打开/关闭 FET。 但是、在我获得读数后、当 FET 环路被执行时、我的 uC 会进入 ISR_TRAP 状态。 命令已发送、但代码在发送后停止。
;------------------------------------------------------------------ ;--如果用户不提供,则缺省 ISR 处理程序 ;--只需将设备放入 lpm0 中;--------------------------- .sect".text:_ISR:_TI_ISR_TRAP" .align2 .global__TI_ISR_TRAP __TI_ISR_TRAP: BIS.W #(0x0010)、SR JMP _TI_ISR_TRAP NOP
我的代码结构如下:
while (1){ SMBus_process(); if (timer&(1<<2)){//此循环每500ms 执行一次 SMBus_requestWord (CellVoltage15、_CellVoltage15、batt_data_buffer、battery_rx_done); SMBus_requestWord (CellVoltage14、_CellVoltage14、batt_data_buffer、battery_rx_done); SMBus_requestWord (CellVoltage13、_CellVoltage13、batt_data_buffer、battery_rx_done); SMBus_requestWord (CellVoltage12、_CellVoltage12、batt_data_buffer、battery_rx_done); SMBus_requestWord (CellVoltage11、_CellVoltage11、batt_data_buffer、battery_rx_done); SMBus_requestWord (CellVoltage10、_CellVoltage10、batt_data_buffer、battery_rx_done); SMBus_requestWord (CellVoltage9、_CellVoltage9、batt_data_buffer、battery_rx_done); SMBus_requestWord (CellVoltage8、_CellVoltage8、batt_data_buffer、battery_rx_done); SMBus_requestWord (CellVoltage7、_CellVoltage7、batt_data_buffer、battery_rx_done); SMBus_requestWord (CellVoltage6、_CellVoltage6、batt_data_buffer、battery_rx_done); SMBus_requestWord (CellVoltage5、_CellVoltage5、batt_data_buffer、battery_rx_done); SMBus_requestWord (CellVoltage4、_CellVoltage4、batt_data_buffer、battery_rx_done); SMBus_requestWord (CellVoltage3、_CellVoltage3、batt_data_buffer、battery_rx_done); SMBus_requestWord (CellVoltage2、_CellVoltage2、batt_data_buffer、battery_rx_done); SMBus_requestWord (CellVoltage1、_CellVoltage1、batt_data_buffer、battery_rx_done); 定时器&=~BIT2;//flag = 0; P1OUT ^= BIT1;//呈绿色闪烁 LED; } if (timer&(1<<4)){//此循环每2000ms 执行一次 SMBus_requestMfrAccessReg (0x22、100、batt_data_buffer、battery_Rx_DONE);//FET 控制 定时器&=~BIT4;//flag = 0; P1OUT ^= BIT1;//闪烁红色 LED; }
我出了什么问题?
谢谢、
您好、Mauro、
我不会有太大帮助、因为这是一个微控制器问题、您必须自行对其进行调试。
但是、您可以查看以下一些内容:
很抱歉、我无法提供更多帮助。
此致、
米歇尔
您好、Mauro、
遗憾的是、我没有时间查看您提供的流程图。 我认为状态图更适合表示 SMBus 状态机的逻辑。 这将更易于理解。
[引用 user="Mauro Basquera"-我应该如何处理 smbus_failure? 为此,我将使用 smbus_init()和 smbus_resume()。 是否有更好的方法来重新初始化通信?
我复位我的 SMBus。 因此,SMBUS_init()后跟 SMBUS_RESUME 是最好的方法。 我们遇到了一个问题、即25ms 超时并不总是由 BQ78350重新指定、因此我们会经常进入这个条件。 我们通过将延迟增加到26ms 来解决了该问题。
[引用 user="Mauro Basquera"]-首次运行后 timer_delay_over 标志永远不会变为零。 为什么? 此代码标志的功能是什么?
我不是最后一个处理代码的人、但你认为它似乎没有被使用是对的。 BQ78350规定了 SMBus 通信之间的最小延迟为2ms。 该标志用于插入该延迟。 我不确定最后一位用于实施延迟的人员是什么。
此致、
米歇尔
您好、Michel。
我正在尝试使用 smbus_writeWord 函数更新 RemainingCapacity、但该命令不起作用。
我必须在命令之前或之后发送命令吗? 我已经选中、器件为"完全访问模式"。
unsigned char * dado; unsigned char x1; x1 = 0; dado =&x1; smbus_writeWord (0x0F、dado);
我有点困惑、因为我能够通过 bqStudio 更新 RemainingCapacity、但在 d/s 中、此命令仅显示为一个读取字。
同时作为 R/W 字:
谢谢、
您好、Mauro、
有几件事情看起来是错误的:
首先、17.17说明说它是一个读取字函数(而不是读取-写入)。 但是、您似乎可以通过 BQ Studio 对其进行配置。 这可能需要 TI 员工澄清。
是否确定未配置剩余电量警报?
我会将您转至论坛:看看是否有其他人已经问过这个问题、如果没有、则发布一个新问题。 通过提出新问题、其他论坛用户将查看并可能回答您的问题(IMO、此主题太长、包含的问题和答案太多。 我喜欢有一个主题、一个问题、一个答案)。
其次、您尝试发送一个字(2个字节)、但将一个无符号字符(1个字节)传递给函数。 我不确定该函数的行为是什么、但很可能是在 SMBus 线路上发送1个额外的字节的垃圾。
为了使您的代码更紧凑、我建议使用如下所示的代码:
//根据您的微控制器、这可能是 unsigned int 或 unsigned short。 //使用 uint16_t (有时是 uint16)更安全、uint16_t x1 = 0; smbus_writeWord (0x0F、&x1);
此致、
米歇尔
您好、Michel。
好的、我将尝试与 TI 员工进行澄清。
很抱歉这个长线程、但我在这里问它、因为它与您善意提供的代码相关。 你无法想象它对我有多大帮助。 我对嵌入式编程非常陌生、您可能已经注意到了这一点。
关于您建议的解决方案、它不起作用。 我将其作为 char 放置、因为该函数的输入被声明为 char。 当我放置 uint16_t 时、我收到警告:"#169-D 参数"类型为 unsigned short *"与"unsigned char *"类型的参数不兼容
void smbus_writeWord (unsigned char addr、unsigned char *数据){ 消息*new_mess; if (smbus_queue.mess_q_cnt < MAX_queue){ new_mess=&smbus_queue.mess_q[smbus_queue.mess_q_in]; new_mess->address = BQ78350_ADDR; new_mess->mess_dir = write_mess; new_mess->is 块= 0; new_mess->TX_DATA[0]= addr; new_mess->TX_DATA[1]=*数据; new_mess->TX_DATA[2]=*(data + 1); //更新队列指针 SMBus_queue.mess_q_in++; 如果(smbus_queue.mess_q_in = MAX_queue) smbus_queue.mess_q_in = 0; SMBus_queue.mess_q_cnt++; } }
我还尝试了其他变体、将数据直接放在 TX_DATA[1]和[2]上、但它不起作用。
我很确定用于更新 RemainingCapacity 的命令是0x0F、因为我在 bqStudio 中使用它。
非常感谢你的帮助。 我非常感谢。
您好、Mauro、
对于剩余的容量问题、请尝试写入另一个寄存器以确保写入字函数正常工作。
我使用 unsigned char *使函数成为通用函数。
然后、用户可以将其与 uint16_t (带有 cast)或 uint8_t 的数组一起使用 请参阅以下示例:
// uint16_t uint16_t x = 0x1234; smbus_writeWord (0x0F、(unsigned char *)&x); //使用数组 uint8_t y[2]; y[0]= 0x34; y[1]= 0x12; smbus_writeWord (0x0F、y);
最好使用第二种方法、因为您可以控制端字节序(使代码可移植、避免在您更改为其他微控制器时出现意外)。
此致、
米歇尔
编辑:此外、为了确保 BQ Studio 发送的内容、请使用逻辑分析仪。 您将能够准确地查看发送的命令。
您好、Michel。
非常感谢您的帮助。
它恰好是 smbus_writeWord 函数中缺少的一行。
new_mess->address = BQ78350_ADDR; new_mess->mess_dir = write_mess; new_mess->is 块= 0; new_mess->TX_cnt = 3;//我添加了此行 new_mess->TX_DATA[0]= addr; new_mess->TX_DATA[1]=*数据; new_mess->TX_DATA[2]=*(data+1);
我之所以解决这个问题、是因为我使用了示波器、uC 只是发送 bq78350地址、然后将线路拉高以获取任何地址和数据。
我不知道它是如何处理您的产品的、但我添加了它、它运行得很好。
感谢您的提示!
此致、
我对 SMB (或 I2C)没有太多经验、但可能有一些指导。 首先、在 I2C 基础上增加了一些 SMB 限制、因此熟悉 I2C 是一个很好的起点。 如果不仔细检查、我就看不到 I2C 所需的 Start、Ack、Nak 或 Stop 位。 我建议在 TI 网站上查看 slua475.pdf、作为一个良好的起点。
此外、我不确定有多少读者会浏览您发布的代码量。 或者更详细地说、我个人不会编写这么多的代码、直到我进行通信。 如果不深入研究、我不知道您是在 SW 中生成每个位、还是使用和 I2C 控制器、但无论采用哪种方法、可能有20行代码允许您生成读取请求并获得应答。 在这种情况下工作之前、其余部分会分散注意力。 更好的方法是执行写请求、因为它更简单、但它必须是导致可观察行为的东西、以便您可以判断它何时起作用。 最简单的写入请求只有四个字节长。