ZHCACK6A june   2020  – may 2023 BQ25150 , BQ25155 , BQ25618 , BQ25619 , TS5A12301E , TS5A3157 , TS5A3159A , TS5A6542

 

  1.   1
  2.   使用 2 引脚接口为 TWS 高效充电
  3.   商标
  4. 引言
  5. 系统概述
    1. 2.1 充电盒
      1. 2.1.1 BQ25619
      2. 2.1.2 TLV62568P
      3. 2.1.3 TPS22910A
      4. 2.1.4 TS5A12301E
      5. 2.1.5 MCU
    2. 2.2 耳塞
      1. 2.2.1 BQ25155
      2. 2.2.2 TPS22910A
      3. 2.2.3 TS5A12301E
      4. 2.2.4 BT/SOC
  6. 充电盒算法实现方案
    1. 3.1 初始化和主代码
    2. 3.2 UART 中断和输出电压调节
  7. 耳塞算法实现方案
    1. 4.1 初始化和主代码
    2. 4.2 中断和传输
  8. 测试步骤
  9. 测试结果
    1. 6.1 动态电压调节
    2. 6.2 具有 4.6V 输出的 BQ25619
    3. 6.3 具有 5V 输出的标准升压
  10. 总结
  11. 原理图
  12. PCB 布局
  13. 10软件
    1. 10.1 充电盒 main.c
    2. 10.2 耳塞 main.c
  14. 11修订历史记录

耳塞 main.c

/* --COPYRIGHT--,BSD
 * Copyright (c) 2016, Texas Instruments Incorporated
 * All rights reserved.
 *
 * 只要符合下列条件,便可再次分发和使用源代码和
 * 二进制形式的以下内容,不管有无进行修改:
 *
 * *  在再次分发源代码时必须保留以上版权声明、
 *    本条件清单以及下述免责声明。
 *
 * *  在以二进制形式进行再次分发时,必须复制
 *    分发时提供的文件和/或其他材料中的以上
 *    版权声明、本条件清单以及下述免责声明。
 *
 * *  除非事先得到特别的书面许可,否则请勿
 *    使用德州仪器 (TI) 公司和贡献者的名称
 *    来支持和推广衍生自该软件的产品。
 *
 * 本软件由版权持有者和贡献者“按原样”
 * 提供,且不承认任何明示或暗示的保证,
 * 包括但不限于对适销性或在某特定用途
 * 方面的适用性的隐含保证。在任何情况下,
 * 对于任何因使用本软件而造成的直接、间接、附带、特殊,
 * 惩罚性或结果性损害(包括但不限于:
 * 替代性商品或服务的采购;使用、数据或利润的损失;
 * 或者业务中断),无论这些损害是如何引起的,
 * 在何种责任理论上,无论是出于合同、严格责任还是
 * 侵权行为(包括疏忽或其他),版权所有者或贡献者
 * 概不负责,即使已被提前告知出现这种损害的可能性。
 * --/COPYRIGHT--*/
// ======== main.c ========
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include <string.h>
#include <stdio.h>
#include "board_functions.h"
#include <stdlib.h>
#include <msp430.h>
#include "driverlib.h"
#include "StdI2C.h"
#include "BQ25150.h"
//#include "board_timer.h"
#include "USB_config/descriptors.h"
#include "USB_API/USB_Common/device.h"
#include "USB_API/USB_Common/usb.h"                 // USB-specific functions
#include "USB_API/USB_CDC_API/UsbCdc.h"
#include "USB_app/usbConstructs.h"
#include "OLED/Oled_SSD1306.h"
#include "StdPollI2C.h"
#include "hal.h"
// Function declarations
uint8_t retInString(char* string);
void initTimer(void);
void setTimer_A_Parameters(void);
// Global flags set by events
volatile uint8_t bCDCDataReceived_event = FALSE; // Indicates data has been rx'ed
// without an open rx operation
char str[50];
#define NUM_SAMPLES 8
#define clkspeed 3          // 24Mhz Clock Select
//#define clkspeed 1        // 8Mhz Clock Select
short samples[NUM_SAMPLES] ;
int sample_index = 0 ;
char str[50];
char cmdstring[5];
char cs2[3];
uint8_t response = 0;
//unsigned char buffer[10]={1,2,3,4,5,6,7,8,9,10};
volatile char wholeString[MAX_STR_LENGTH] = "";
volatile uint8_t modecounter = 0;
volatile uint8_t pluscounter = 0;
///Timer_A_initUpModeParam Timer_A_params = {0};
int i;
unsigned char* PRxData;                     // Pointer to RX data
unsigned char RXByteCtr;
volatile unsigned char RxBuffer[128];       // Allocate 128 byte of RAM
unsigned char* PTxData;                     // Pointer to TX data
unsigned char TXByteCtr;
const unsigned char TxData[] =              // Table of data to transmit
{
 0x11,
 0x22,
 0x33,
 0x44,
 0x55
};
volatile uint8_t Button_Released = 0;
unsigned int result;
const char* hexstring = "0xabcdef0";
int raddr;
int rdata;
char buf[5];
uint8_t uraddr;
uint8_t urdata;
uint16_t Err;
// Holds the outgoing string
char outString[MAX_STR_LENGTH] = "";
uint8_t connectedflag = 0;
uint8_t echoflag = 1;
int ubtncounter = 0;
uint8_t RegValueLSB = 0;
uint8_t RegValueMSB = 0;
uint8_t VbatMSB = 0;
uint8_t ADCCheck = 0;
uint32_t stobusf;
uint8_t ADCcount = 0;
uint8_t VinReadA = 0;
uint8_t VinReadB = 0;
uint8_t VinReadC = 0;
uint8_t VinLow = 0;
double vIn = 0;
char vBat[];
//uint8_t RegValueM = 0;
//uint8_t RegValueL = 0;
uint32_t stobuf;
uint32_t stobuf1;
double stobuf2;
uint8_t RegValues = 0;
//__delay_cycles(1000); 1000 = 100us
uint8_t rthex;
// Set/declare toggle delays
//uint16_t SlowToggle_Period = 20000 - 1;
//uint16_t FastToggle_Period = 1000 - 1;
/*
 * ======== main ========
 */
void main(void)
{
    WDT_A_hold(WDT_A_BASE); // Stop watchdog timer
    // Minumum Vcore setting required for the USB API is PMM_CORE_LEVEL_2 .
    PMM_setVCore(PMM_CORE_LEVEL_2);
    USBHAL_initPorts();           // Config GPIOS for low-power (output low)
    USBHAL_initClocks(8000000 * clkspeed);   // Config clocks.MCLK=SMCLK=FLL=8MHz; ACLK=REFO=32kHz
    //USBHAL_initClocks(24000000);
    initTimer();           // Prepare timer for LED toggling
    //USB_setup(TRUE, TRUE); // Init USB & events; if a host is present, connect
    initI2C();
//  ======== UART Setup ========
    P3SEL = BIT3+BIT4;                        // P3.4,5 = USCI_A0 TXD/RXD
    __delay_cycles(20000);
    UCA0CTL1 |= UCSWRST;                      // **Put state machine in reset**
    UCA0CTL1 |= UCSSEL_2;                     // SMCLK
    UCA0BR0 = 6;                              // 1MHz 9600 (see User's Guide)
    UCA0BR1 = 0;                              // 1MHz 9600
    UCA0MCTL = UCBRS_0 + UCBRF_13 + UCOS16;   // Modln UCBRSx=0, UCBRFx=0,
                                              // over sampling
    UCA0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**
//  ======== GPIO Setup ========
    //LED Setup
    GPIO_setAsOutputPin(LED_1);
    GPIO_setOutputLowOnPin(LED_1);
    GPIO_setAsOutputPin(LED_2);
    GPIO_setOutputLowOnPin(LED_2);
    GPIO_setAsOutputPin(LED_3);
    GPIO_setOutputLowOnPin(LED_3);
    //GPIO Setup
    GPIO_setAsInputPinWithPullUpResistor(BQ_INT);
    GPIO_setAsInputPin(BQ_PG);
    GPIO_setAsInputPinWithPullUpResistor(BQ_START);
    GPIO_setAsOutputPin(BQ_CE);
    GPIO_setOutputLowOnPin(BQ_CE);
    GPIO_setAsOutputPin(BQ_LP);
    GPIO_setOutputHighOnPin(BQ_LP);
    GPIO_setAsOutputPin(BQ_G1);
    GPIO_setOutputLowOnPin(BQ_G1);
    GPIO_toggleOutputOnPin(LED_1);
    waitms(500);
    GPIO_toggleOutputOnPin(LED_1);
    waitms(500);
    GPIO_toggleOutputOnPin(LED_1);
    waitms(500);
    GPIO_toggleOutputOnPin(LED_1);
//  ======== BQ25155 Register Setup ========
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_MASK0, 0x5E, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_MASK1, 0xBF, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_MASK2, 0xF1, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_MASK3, 0x77, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_VBAT_CTRL, 0x3C, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_ICHG_CTRL, 0x50, &Err);//sets Ichg to 200mA, 0xA0 for 200mA, 0x50 for 100mA, 0x20 for 40mA
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_PCHRGCTRL, 0x02, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_TERMCTRL, 0x14, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_BUVLO, 0x00, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_CHARGERCTRL0, 0x92, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_CHARGERCTRL1, 0xC2, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_ILIMCTRL, 0x06, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_MRCTRL, 0x2A, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_ICCTRL0, 0x10, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_ICCTRL1, 0x00, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_ICCTRL2, 0x40, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_ADCCTRL0, 0x40, &Err); //0x58 Sets ADC to continuous with 3ms conv., 0x40 sets to continuous with 24ms conv.
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_ADCCTRL1, 0x00, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_ADCALARM_COMP1_M, 0x23, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_ADCALARM_COMP1_L, 0x20, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_ADCALARM_COMP2_M, 0x38, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_ADCALARM_COMP2_L, 0x90, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_ADCALARM_COMP3_M, 0x00, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_ADCALARM_COMP3_L, 0x00, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_ADC_READ_EN, 0xFE, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_TS_FASTCHGCTRL, 0x34, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_TS_COLD, 0x7C, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_TS_COOL, 0x6D, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_TS_WARM, 0x38, &Err);
    StdI2C_TX_Single(BQ25150_ADDR, BQ25150_TS_HOT, 0x28, &Err);
//  ======== Ready while loop ========
    //This while loop holds the program with the interrupts disabled.This allows synchronization with the case
    //short BQ_START pin 3.7 to ground to exit loop
    while(GPIO_getInputPinValue(BQ_START) == 1) //Wait on start condition before enabling interrupts
    {
        GPIO_toggleOutputOnPin(LED_2);
        __delay_cycles(3000000);
        GPIO_toggleOutputOnPin(LED_2);
        __delay_cycles(3000000);
        GPIO_toggleOutputOnPin(LED_2);
        __delay_cycles(3000000);
        GPIO_toggleOutputOnPin(LED_2);
        __delay_cycles(24000000);
    }
    GPIO_setOutputLowOnPin(LED_2);
//  ======== Interrupt Enables ========
    __enable_interrupt();  // Enable interrupts globally
    GPIO_enableInterrupt(BQ_INT);
    GPIO_selectInterruptEdge(BQ_INT, GPIO_HIGH_TO_LOW_TRANSITION);
    GPIO_clearInterrupt(BQ_INT);
//  ======== Main while loop ========
    //reads Vbat, waits for case to drive Vin low
    while(1)
    {
         StdI2C_P_RX_Single(BQ25150_ADDR, BQ25150_ADCDATA_VBAT_M, &VbatMSB, &Err); //Read Vbat
         StdI2C_P_RX_Single(BQ25150_ADDR, BQ25150_ADCDATA_VIN_M, &RegValueMSB, &Err); //Read Vin
         GPIO_toggleOutputOnPin(LED_1);
         __delay_cycles(12000000);
    }
}
/*
 * ======== TIMER1_A0_ISR ========
 */
#if defined(__TI_COMPILER_VERSION__) || (__IAR_SYSTEMS_ICC__)
#pragma vector=TIMER0_A0_VECTOR
__interrupt void TIMER0_A0_ISR (void)
#elif defined(__GNUC__) && (__MSP430__)
void __attribute__ ((interrupt(TIMER0_A0_VECTOR))) TIMER0_A0_ISR (void)
#else
#error Compiler not found! 
#endif
{
    // wake on CCR0 count match
    TA0CCTL0 = 0;
    __bic_SR_register_on_exit(LPM0_bits|GIE);
}
//  ======== BQ25155 Interrupt ========
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=PORT1_VECTOR
__interrupt
#elif defined(__GNUC__)
__attribute__((interrupt(PORT1_VECTOR)))
#endif
void Port_1(void)
{
    switch(__even_in_range(P1IV,0x03))
    {
    case P1IV_P1IFG3: 
        StdI2C_P_RX_Single(BQ25150_ADDR, BQ25150_STAT0, &RegValueMSB, &Err); //Read STAT0 REG
        RegValueMSB &= 0x01; // Check if VIN_PGOOD_STAT is asserted
        if(RegValueMSB == 0x00){ //if VIN_PGOOD_STAT is not asserted check to see if Vin has been driven low
//                __delay_cycles(6000000);
//
//                StdI2C_P_RX_Single(BQ25150_ADDR, BQ25150_ADCDATA_VIN_M, &RegValueMSB, &Err); //Read Vin
//                stobuf = RegValueMSB;
//                stobuf1 = stobuf * 6;
//                vIn = (double)stobuf1 / 256;
          ADCcount = 0;
          VinLow = 0;
          VinReadA = 5;
          VinReadB = 5;
          VinReadC = 5;
          while(ADCcount < 85 && VinLow == 0){
            __delay_cycles(120000);
            StdI2C_P_RX_Single(BQ25150_ADDR, BQ25150_ADCDATA_VIN_M, &RegValueMSB, &Err); //Read Vin
            stobuf = RegValueMSB;
            stobuf1 = stobuf * 6;
            vIn = (double)stobuf1 / 256;
            VinReadC = VinReadB;
            VinReadB = VinReadA;
            VinReadA = vIn;
            ADCcount++;
            if(VinReadA < 3 && VinReadB < 3 && VinReadC < 3){
                VinLow = 1;
                VinReadA = 0;
                VinReadB = 0;
                VinReadC = 0;
                vIn = 0;
            }
          }
                if(VinLow == 1){//if Vin is <3V begin communication process
                    StdI2C_P_RX_Single(BQ25150_ADDR, BQ25150_STAT0, &RegValueMSB, &Err); //Read STAT0 REG
                    RegValueMSB &= 0x20; // Check if CHARGE_DONE_STAT is asserted
//                    RegValueMSB = 0x20; //uncomment to assert charge complete bit for testing
                    if(RegValueMSB){                       // if CHARGE_DONE_STAT is asserted transmit 0xD5, 0xD5 = Vbat = 5v, this is chosen as the arbitrary charge complete bit for simplicity
                        GPIO_setOutputHighOnPin(BQ_G1);    // Enter Comms Mode
                        __delay_cycles(4000);
                        while (!(UCA0IFG&UCTXIFG));        // USCI_A0 TX buffer ready? 
                        UCA0TXBUF = 0xD5;                  // TX -> 0xD5 = 5V, out of charge range
                        __delay_cycles(4000);
                        GPIO_toggleOutputOnPin(LED_3);
                        GPIO_setOutputLowOnPin(BQ_G1);     //Enter Power Mode
                        GPIO_toggleOutputOnPin(LED_2);
                        __delay_cycles(4000000);
                        GPIO_toggleOutputOnPin(LED_2);
                        __delay_cycles(4000000);
                        GPIO_setOutputHighOnPin(LED_2);
                    }
                    else{
                    stobuf = VbatMSB;
                    stobuf1 = stobuf * 6;
                    stobuf2 = (double)stobuf1 / 256; //stobuf2 = double of Vbat
                    GPIO_setOutputHighOnPin(BQ_G1);    // Enter Comms Mode
                    __delay_cycles(4000);
                    while (!(UCA0IFG&UCTXIFG));        // USCI_A0 TX buffer ready? 
                    UCA0TXBUF = VbatMSB;               // TX -> vBat MSB
                    __delay_cycles(4000);
                    GPIO_toggleOutputOnPin(LED_3);
                    GPIO_setOutputLowOnPin(BQ_G1);     //Enter Power Mode
                    }
                }
        }
        //Clear all interrupt flags
        StdI2C_P_RX_Single(BQ25150_ADDR, BQ25150_FLAG0, &RegValues, &Err);
        StdI2C_P_RX_Single(BQ25150_ADDR, BQ25150_FLAG1, &RegValues, &Err);
        StdI2C_P_RX_Single(BQ25150_ADDR, BQ25150_FLAG2, &RegValues, &Err);
        StdI2C_P_RX_Single(BQ25150_ADDR, BQ25150_FLAG3, &RegValues, &Err);
        GPIO_clearInterrupt(BQ_INT);
        break;
    default: 
        break;
    }
}
//Released_Version_5_10_00_17