• 基于S32K144实现TPS929120的基本控制功能


    前言

    最近拜访一些车灯客户时,发现使用最多的多通道LED Driver是TI的TPS929120,恰好我们代理的国产线正在做对标TPS929120的产品。为了方便后面的车灯方案推广,笔者和同事参考TI官网的资料,做了一套TPS929120的demo板,同时主控MCU采用了现在最火的车规通用MCU–S32K144,并配套编写了简单的软件demo。
    如下是笔者学习TPS929120时整理的内容,希望对需要快速熟悉TPS929120的读者有所帮助。

    1.TPS92910简介

    TPS929120是TI公司在2019年4月份发布的具有FlexWire接口的12通道汽车级40V高边LED驱动芯片,其主要特点如下:

    • 12路高边精确电流输出通道
      • 供电电压4.5V到40V
      • 电阻预设电流最高到75mA
      • 2bit全局的,6bit独立的电流设置
      • 输出电流在5mA到75mA时,精度可达±5%
      • 输出电流在1mA时,精度为±10%
      • 输出电流50mA时的压降为0.5V
      • 12bit独立PWM调光
      • 可编程的PWM频率最高可达20kHz
      • 支持线性和指数两种调光方式
    • FlexWire控制接口
      • 最高1MHz的时钟频率
      • FlexWire总线最多可挂16个器件
      • 一帧命令最多可以传输8字节的寄存器数据
      • 内部集成5V LDO可以给CAN收发器供电
    • 诊断和保护功能
      • 可编程的fail-safe状态
      • LED开路检测
      • LED短路检测
      • 单颗LED短路诊断
      • 可编程的欠压检测
      • 开漏模式的ERR引脚,可以通知主机是否发生故障
      • 用于判断FlexWire通信是否正常的看门狗和CRC校验
      • 用于引脚电压测量的8bit的ADC
      • 过温保护

    2.硬件调试平台

    笔者用来调试TPS929120所制作的DEMO板,整体框图如下:

    DEMO板分为驱动板和灯板两部分,接近客户的实际使用情况。其中,

    • 驱动板上面主要是给灯板供电的DCDC,给S32K和TJA1044供电的LDO;
    • 灯板上面主要是灯驱TPS929120和LED灯驱;
    • 驱动板和灯板之间通过差分总线进行通信,抗干扰能力相比传统的I2C,SPI提高很多;
    • 由于给灯板供电只有6.5V,所以TPS929120的整体功耗相比正常的12V供电系统能降低不少。

    另外,该Demo板也预留了MCU的UART引脚作为测试点,方便查看S32K144和TPS929120的通信数据,从而在调试时更快的锁定问题。

    2.1 灯板原理图

    灯板原理图参考如下官方demo板进行设计:

    2.2 参考电流

    参考电流计算公式如下,其中Vref为1.235V(数据手册典型值),Kref默认值为512(可以通过修改寄存器CONF_MISC1中的CONF_REFRANGE进行调整)。

    根据查表,此电路中TPS929120默认的参考电流为50mA(灯板上TPS929120贴的REF电阻为12.4K,计算值为51mA)。

    2.3 器件地址

    TPS929120可以使用外部地址,也可以使用内部EEPROM预烧写的地址,此次DEMO使用外部地址。两片TPS929120的ADDR0,ADDR1,ADDR2引脚电压分别为000,100;根据下面的器件地址设置表格可以知道(EEP_DEVADDR[3:0]的默认值为0000b),两片TPS929120的地址分别为0和1。

    3.TPS929120通信协议

    TPS929120使用的通信方式,TI称之为FlexWire,其实就是UART(数据链路层)+CAN物理层,然后在传输层增加一些自定义的帧格式,目前其他厂家新出的多通道LED Driver基本都是采用这种通信方式。

    3.1 物理层

    FlexWire的物理层使用CAN收发器,主要的作用就是将普通的串行信号转换成差分信号(时序图如下),比较常用的有TJA1044,TCAN1042等。

    3.2 数据链路层

    FlexWire的数据链路层使用的是UART通信,因为TPS929120内部的时钟最高为1MHz,为了通信稳定,所以MCU内部的UART配置的波特率为500K;其它配置为8bit数据位,无奇偶校验,1bit停止位。

    3.3 传输层

    2.3.1 读写时序
    • 主机向TPS929120写数据:

    • 主机从TPS929120读数据:

    需要注意的是,如果加了CAN收发器,由于CAN收发器自带的回环功能,实际上主机也会收到他自身发的数据,所以实际主机收到的数据应该是自身发的数据+TPS929120响应的数据

    当一次性写8个寄存器数据时,MCU的UART_RX收到的数据最多,为MCU发出的12字节+2字节

    2.3.2 帧格式说明
    • 总体格式如下:

    • 其中DEV_ADDR的组成元素较多,如下所示,其他都是单一元素组成。

    2.3.3 寄存器lock与unlock

    配置其他寄存器之前需要先配置CONF_LOCK Register(61h)进行解锁,主要是如下四个4bit,这4个bit上电之后默认为1,处于lock状态,需要清0进行unlock。

    这4个bit分别能够lock与unlock的寄存器如下图:

    2.3.4 输出通道控制
    • CONF_EN0(50h),CONF_EN1(51h)分别控制通道0-7,8-11的使能;默认值为0h。

    • IOUT0(00h)到IOUT11(0Bh)分别通道0到11的电流,一共64-step,实际的通道输出电流的计算公式如下,其中此电路板的I(FULL_RANGE)为50mA;这些寄存器在reset之后加载对应的EEPROM中EEPIx寄存器的值,EEPIx默认为3Fh。

    • PWM0(20h)到PWM11(2Bh),PMWL0(40h)到PWML11(4Bh)都是控制通道0到11的PWM占空比,前者用于粗调,后者用于微调,计算公式如下;前者在reset之后加载对应的EEPROM中EEPPx寄存器的值,为FFh,后者的默认值为Fh。

    根据上面的分析,如果只配置通道使能,其他寄存器不设置,使能的通道会输出50mA的电流。

    TPS929120在配置通道使能的情况下,即使配置PWM占空比为0,也会有微亮的情况。

    4.使用S32K144驱动TPS92910

    接下来,将基于S32K144介绍如何使用MCU驱动TPS929120,实现一些基本的灯光控制功能。

    4.1 实现命令帧格式

    4.1.1 写寄存器的帧格式

    写寄存器的帧格式如2.3.2章节所述,先发SYNC(0x55),然后是DEV_ADDR(由4种元素组成),然后是REG_ADDR(数据手册种寄存器的地址),然后是DATA(要写入寄存器的数据),最后是CRC

    整体实现代码如下:

    void FlexWrite(uint8_t DEV_ADDR_x, uint8_t registerAddr, uint8_t DATA_BYTES[], uint8_t DATA_LENGTH_x, uint16_t checkResponse)
    {
        uint8_t DEV_ADDR=0x00, REG_ADDR;
        uint8_t commandFrame[12] = {0};// one longest command frame length is 12
        uint8_t dataLength=0, frameLength=0, responseLength = 0;
        uint16_t i;
    
        DEV_ADDR = (FLEX_Write | DATA_LENGTH_x | DEV_ADDR_x);
        REG_ADDR = registerAddr;
    
        commandFrame[0] = 0x55; //SYNC byte
        commandFrame[1] = DEV_ADDR;
        commandFrame[2] = REG_ADDR;
    
        switch (DATA_LENGTH_x)
        {
        case 0:
            {
                dataLength = 1;
                break;
            }
        case 16:
            {
                dataLength = 2;
                break;
            }
        case 32:
            {
                dataLength = 4;
                break;
            }
        case 48:
            {
                dataLength = 8;
                break;
            }
        default : break;
        }
    
        for(i=0;i<dataLength;i++)
        {
            commandFrame[i+3] = DATA_BYTES[i];
        }
    
        commandFrame[i+3] = CRC_LUT(commandFrame+1,i+2); //calculate CRC of all the command frame bytes
        frameLength = i+4; //store the entire command frame byte length
        responseLength=frameLength+2;
        
        uartWrite(commandFrame, frameLength, checkResponse, responseLength); //data will be stored in responseData[]
    }
    
    • 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

    有关TPS929120的CRC算法,可以参考之前的文章:

    TPS929120的CRC校验的三种实现方法_Auto FAE进阶之路的博客-CSDN博客

    4.1.1 读寄存器的帧格式

    读寄存器的帧格式和写寄存器的帧格式相近,这里就不赘述了。

    4.2 实现Uart串口收发

    硬件上使用LPUART1模块,引脚选择PTC8和PTC9,软件配置为波特率500K,8N1。同时,因为使用了CAN收发器,所以串口实际接收到的数据是发送的数据加上TPS929120响应的数据。

    整体实现代码如下:

    /*********************************************************************************************************
    ** Function name:       uartWrite
    ** Descriptions:        N/A
    ** input parameters:    N/A
    ** output parameters:   N/A
    ** Returned value:      N/A
    *********************************************************************************************************/
    void uartWrite(uint8_t commandFrame[], uint16_t frameLength, uint16_t checkResponse, uint16_t responseLength)
    {
        uint8_t i =0;
    
        /* Enable  the receiver and receive data full interrupt of LPUART1*/
        LPUART_DRV_ReceiveData(INST_LPUART1, responseData, 1u);
        /* The function does not return until the transmit is complete or timeout(2ms) occured*/
        LPUART_DRV_SendDataBlocking(INST_LPUART1, commandFrame, frameLength, 2);
        if(checkResponse == TRUE)
        {
            /* launch timer and set wait time = 2000us
    	       * This time should be larger than the time to receive all the response bytes,
    	       * And the response receiving time depends on the buard rate and the number of response byte,
    	       * For example, baurd rate = 500000, 2 response byte, so the wait time should be larger than 2*10*1/500000 = 40us
    	       * Why 2*10 because for each byte there are additional 1 start bit and 1 stop bit
    	       */
            timeOut(2000);
    	
            /* received all response byte or the wait time exceeds the specified time */
            while(!((timeOutFlag == 1) || (receiveByteNum == responseLength)));
    
            /*Take some action when successfully received response*/
            if(receiveByteNum == responseLength)
            {
                /* Turn off Red LED and turn on Green LED*/
                PINS_DRV_WritePin(LED_PORT, RED_LED, 1);
                PINS_DRV_TogglePins(LED_PORT, 1<<GREEN_LED);
            }
            /* You can take some action once the response has not been received */
            else
            {
                /* Toggle Red LED and turn off Green LED */
                PINS_DRV_TogglePins(LED_PORT, 1<<RED_LED);
                PINS_DRV_WritePin(LED_PORT, GREEN_LED, 1);
            }
    
            /* Clear data sent */
            for(i=0; i<frameLength; i++)
            {
                commandFrame[i] = 0x00;
            }
            /* Clear response data*/
            for(i=0; i<responseLength; i++)
            {
                responseData[i] = 0x00;
            }
            /* reset number of response data*/
            receiveByteNum = 0;
    
            /* Disable the receiver and receive data full interrupt of LPUART1 */
            LPUART_DRV_AbortReceivingData(INST_LPUART1);
        }
    }
    
    /*********************************************************************************************************
    ** Function name:       Lpuart1RxCallback
    ** Descriptions:        N/A
    ** input parameters:    N/A
    ** output parameters:   N/A
    ** Returned value:      N/A
    *********************************************************************************************************/
    void Lpuart1RxCallback(void *driverState, uart_event_t event, void *userData)
    {
        /* Unused parameters */
        (void)driverState;
        (void)userData;
    
        /* Check the event type */
        if (event == UART_EVENT_RX_FULL)
        {
            /* Update the buffer index and the rx buffer */
        	receiveByteNum++;
        	LPUART_DRV_SetRxBuffer(INST_LPUART1, &responseData[receiveByteNum], 1U);
    
        }
    }
    
    • 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

    4.3 实现基本的控制功能

    4.3.1 操作TPS92910的基本函数

    实现写寄存器的函数之后,就可以基于该函数实现一些基本的操作TPS929120函数了,主要是如下几个:

    如下代码中包含了很多寄存器的宏定义,没有进行展开,读者可以借助数据手册进行对比。

    1. unlock与lock寄存器的函数实现如下:

    2. 清除错误状态和标志的函数实现如下:

    3. 配置通道电流的函数实现如下:

    4. 打开通道的函数实现如下:

    5. 关闭通道的函数实现如下:

    6. 配置通道的PWM占空比的函数实现如下:

    4.3.1 TPS929120控制流程

    实现了上述基本控制函数之后,接下来就是控制TPS929120实现基本的灯效了。

    首先需要对TPS92910进行初始化,主要流程参考如下代码:

    然后就是在主循环中实现灯效,这里以呼吸效果为例,代码如下:

    下面代码在测试TPS92910竞品更改过,PWM占空比为0时仍有漏电流的问题被修复了。如果驱动TPS929120,建议在PWM占空比到0时,增加关闭通道的操作。

    5.参考资料

  • 相关阅读:
    pve8群晖rr方式安装(编译失败检查网络或磁盘空间error 23:200问题解决)
    Maven基础篇2
    【C++入门到精通】右值引用 | 完美转发 C++11 [ C++入门 ]
    beego框架自学笔记1
    JavaScript基础之十JavaScript的DOM操作
    常见的7种分布式事务解决方案(2pc,3pc,Tcc,Seta、本地事务....)
    c# 异步进阶———— paralel [二]
    计算机毕业设计Python+django 宠物领养中心小程序(源码+系统+mysql数据库+Lw文档)
    上海控安SmartRocket系列产品推介(五):SmartRocket Scanner软件成分分析工具
    小程序毕设作品之微信体育馆预约小程序毕业设计成品(5)任务书
  • 原文地址:https://blog.csdn.net/bjxdbz/article/details/127935501