目录
1. TJA1145 工作原理简介
TJA1145的唤醒工作原理主要依赖于其内部的唤醒机制和外部硬件的配合,主要涉及低功耗状态、选择性唤醒功能。TJA1145是一款CAN收发器,它支持多种工作模式,包括Normal、Standby、Sleep等。在这些模式中,Sleep模式是最低功耗状态,而Normal模式则是最活跃的状态,其中Standby模式处于两者之间,允许一定的功能操作但功耗较低。TJA1145可以通过接收特定的唤醒源从Sleep或Standby模式唤醒到Standby或Normal模式。
-
唤醒源的检测与处理: TJA1145内部有一个唤醒逻辑,当CAN总线上有特定的唤醒帧或者满足特定条件时,TJA1145会检测到这些唤醒源。例如,当报文过滤器的报文与唤醒帧寄存器相匹配时,TJA1145会认为检测到唤醒帧,然后通过内部逻辑使INH引脚置位变成高电平,以此来使能外部的电源芯片。INH引脚的高电平输出可以间接地使能电源芯片,从而唤醒ECU。
-
硬件拓扑与电源管理: TJA1145的唤醒过程还需要外部硬件的支持。带唤醒功能的CAN收发器需要12V常电供电,并且INH脚需要连接到电源芯片的使能脚。这样,当CAN总线上有网络管理帧时,INH引脚变成高电平,去唤醒电源芯片,完成了一次完整的网络管理唤醒过程。
-
状态切换与控制: TJA1145的状态切换可以通过SPI指令进行控制。例如,通过发送特定的SPI指令,可以将TJA1145从Normal或Sleep状态切换到Standby状态,或者从Standby状态切换到Sleep状态。在Sleep状态下,如果检测到有效的唤醒源,TJA1145会自动切换到Standby状态,从而通过INH引脚的高电平唤醒MCU和其他相关模块。
综上所述,TJA1145的唤醒工作原理涉及内部唤醒逻辑的检测与处理、外部硬件的支持以及通过SPI指令进行的状态切换控制。这些机制共同作用,使得TJA1145能够在需要时被唤醒,以支持汽车的网络通信和系统管理。
2. SPI通讯
TJA1145是通过SPI通信来配置相关寄存器的,SPI配置为全双工数据传输。开发过程中首先要保证MCU和TJA1145之间的通信正常,这个可以通过读取设备ID来验证,寄存器 0x7E 存储的为TJA1145的 ID code,如下图所示,时钟线空闲时为低电平(CPOL = 0),且在第二个跳变沿采样(CPHA = 1)。SPI使用四个接口信号进行同步和数据传输,在此不做赘述。
(1) 硬件SPI读写函数:
/*******************************************************************************
* 函数名 : tja1145_write_regster
* 描 述 : write寄存器
* 输 入 : -
* 输 出 : -
* 返回值 : 无
*******************************************************************************/
uint8_t tja1145_write_regster(uint8_t reg_addr, uint8_t value)
{
status_t ret = STATUS_SUCCESS;
uint8_t txdata[2] = {0};
uint8_t rxData[2] = {0};
reg_addr = reg_addr << 1;
txdata[0] = reg_addr;
txdata[1] = value;
ret = LPSPI_DRV_MasterTransferBlocking(2, txdata, rxData, 2, 1000);
if (ret != STATUS_SUCCESS)
{
log("tja1145_write_regster spi write fail!\r\n");
}
return;
}
/*******************************************************************************
* 函数名 : tja1145_read_regster
* 描 述 : read寄存器
* 输 入 : -
* 输 出 : -
* 返回值 : 无
*******************************************************************************/
uint8_t tja1145_read_regster(uint8_t reg_addr)
{
uint8_t txdata[2] = {0};
uint8_t rxData[2] = {0};
reg_addr = (reg_addr << 1) | 0x01;
txdata[0] = reg_addr;
txdata[1] = 0xFF;
LPSPI_DRV_MasterTransferBlocking(2, txdata, rxData, 2, 1000);
return rxData[1];
}
(2) 软件(GPIO)模拟SPI读写函数:
/*******************************************************************************
* 函数名 : soft_spi_send16bit
* 描 述 : SPI读写16bit(未使用)
* 输 入 : -
* 输 出 : -
* 返回值 : 无
*******************************************************************************/
uint8_t soft_spi_send16bit(uint16_t *txData, uint16_t *rxData, uint8_t bytes)
{
uint8_t i, j;
E521_CS_LOW; // CS =0;// DHGPIOWrite(PTB, 17, 0);
E521_CLK_LOW; // CLK =0; // DHGPIOWrite(PTB, 14, 0);
Delay_us(e521_DelayCnt);
for (i = 0; i < bytes; i++)
{
for (j = 0; j < 16; j++)
{
E521_CLK_HIGH; // CLK_Write(1);
DHGPIOWrite(PTB, 16, (txData[i] >> (15 - j) & 0x01)); // MOSI_Write( (txData[i]>>(7-j) & 0x01) );
Delay_us(e521_DelayCnt);
E521_CLK_LOW;
rxData[i] |= (DHGPIORead(PTB, 15) << (15 - j)); // rxData[i] |= ( MISO_Read() << (7-j) );
//log("[%d] = %x\r\n",j,DHGPIORead(PTB, 15) << (15 - j));
Delay_us(e521_DelayCnt);
}
}
E521_CS_HIGH; // CS_Write(1);
Delay_us(e521_DelayCnt);
return 0;
}
/*******************************************************************************
* 函数名 : soft_spi_ReadWrite_byte
* 描 述 : SPI读写一个字节
* 输 入 : -
* 输 出 : -
* 返回值 : 无
*******************************************************************************/
uint8_t soft_spi_send(uint8_t *txData, uint8_t *rxData, uint8_t bytes)
{
uint8_t i, j;
// CS_Write(0);
DHGPIOWrite(PTA, 9, 0);
// CLK_Write(0);
DHGPIOWrite(PTE, 15, 0);
Delay_us(DelayCnt);
for (i = 0; i < bytes; i++)
{
for (j = 0; j < 8; j++)
{
// CLK_Write(1);
DHGPIOWrite(PTE, 15, 1);
// MOSI_Write( (txData[i]>>(7-j) & 0x01) );
DHGPIOWrite(PTA, 8, (txData[i] >> (7 - j) & 0x01));
Delay_us(DelayCnt);
// CLK_Write(0);
DHGPIOWrite(PTE, 15, 0);
// rxData[i] |= ( MISO_Read() << (7-j) );
rxData[i] |= (DHGPIORead(PTE, 16) << (7 - j));
Delay_us(DelayCnt);
}
}
// CS_Write(1);
DHGPIOWrite(PTA, 9, 1);
Delay_us(DelayCnt);
return 0;
}
/*******************************************************************************
* 函数名 : soft_spi_ReadWrite_byte
* 描 述 : SPI写一个字节到寄存器
* 输 入 : -
* 输 出 : -
* 返回值 : 无
*******************************************************************************/
uint8_t soft_spi_ReadWrite_byte(uint8_t txData)
{
uint8_t rxData = 0;
soft_spi_send(&txData, &rxData, 1);
return rxData;
}
/*******************************************************************************
* 函数名 : soft_Spi_write_reg
* 描 述 : SPI写一个字节到寄存器
* 输 入 : -
* 输 出 : -
* 返回值 : 无
*******************************************************************************/
void soft_Spi_write_reg(uint8_t addr, uint8_t Tx_buf)
{
uint8_t txdata[2] = {0};
uint8_t rxData[2] = {0};
addr = addr << 1;
txdata[0] = addr;
txdata[1] = Tx_buf;
soft_spi_send(txdata, rxData, 2);
return;
}
/*******************************************************************************
* 函数名 : soft_Spi_read_reg
* 描 述 : SPI从寄存器读一个字节
* 输 入 : -
* 输 出 : -
* 返回值 : 无
*******************************************************************************/
uint8_t soft_Spi_read_reg(uint8_t addr)
{
uint8_t rxData[2] = {0};
uint8_t txdata[2] = {0};
addr = (addr << 1) | 0x01;
txdata[0] = addr;
txdata[1] = 0xFF;
soft_spi_send(txdata, rxData, 2);
return rxData[1];
}
/*******************************************************************************
* 函数名 : soft_Spi_Write_buffer
* 描 述 : SPI写多个字节到地址
* 输 入 : -
* 输 出 : -
* 返回值 : 无
*******************************************************************************/
void soft_Spi_Write_buffer(uint8_t addr, uint8_t *Tx_buf, uint8_t lens)
{
uint8_t txdata[] = {0};
uint8_t rxData[] = {0};
txdata[0] = (addr << 1);
for (int i = 0; i < lens; i++)
{
txdata[i + 1] = Tx_buf[i];
}
soft_spi_send(txdata, rxData, lens + 1);
return;
}
/*******************************************************************************
* 函数名 : soft_Spi_read_buffer
* 描 述 : SPI写多个字节到地址
* 输 入 : -
* 输 出 : -
* 返回值 : 无
*******************************************************************************/
void soft_Spi_read_buffer(uint8_t addr, uint8_t *Rx_buf, uint8_t lens)
{
uint8_t txdata[] = {0};
uint8_t rxData[] = {0};
txdata[0] = (addr << 1) | 0x01;
for (int i = 0; i < lens; i++)
{
txdata[i + 1] = 0xFF;
}
soft_spi_send(txdata, rxData, lens + 1);
Rx_buf = &rxData[1];
return;
}
3. 唤醒源配置
唤醒源配置需要将唤醒帧信息填入 TJA1145 芯片相应的寄存器中,有以下两种配置方式:配置一:只校验唤醒帧的 ID;配置二:校验唤醒帧 ID 且校验唤醒帧数据字段;
配置一:
顾名思义,如果只校验唤醒帧的 ID,当 TJA1145 芯片收到唤醒帧的ID就会拉高INH脚,进入唤醒流程。该配置主要涉及0x29、0x2A、0x2D、0x2E 、0x2F、0x68~0x6F等寄存器。通过对这几个寄存器的配置可以实现特定帧唤醒和任意帧唤醒。0x29、0x2A寄存器配置唤醒帧ID验证码; 0x2D、0x2E寄存器配置唤醒ID掩码; 0x2F寄存器可配置唤醒条件是否需要校验数据位; 当采用方式一配置唤醒源时,0x68~0x6F寄存器需要全配置为0XFF。寄存器信息见附件TJA1145数据手册。
本文示例为特定帧唤醒(标准帧),唤醒源为:0x400~0x47F 、0x285、0x286。具体配置如下:
tja1145_write_regster(0X0A, 0x00); /* < 写保护寄存器,解锁-0x00,上锁-0xFF */
tja1145_write_regster(0X23, 0x03); /* < 唤醒检测使能 */
tja1145_write_regster(0X26, 0x05); /* < 波特率寄存器 */
tja1145_write_regster(0X2F, 0x00); /* < 唤醒条件不需要判断数据位,长度设置为0,PNDM位 置0 */
tja1145_write_regster(0X04, 0x06);
/* 当唤醒条件设置为只判断帧ID时,需要通过该类寄存器设置唤醒帧ID验证码,见tja1145文档page19、20 */
tja1145_write_regster(0X27, 0x00); /* < 27寄存器,配置唤醒帧ID验证码(扩展帧) */
tja1145_write_regster(0X28, 0x00); /* < 28寄存器,配置唤醒帧ID验证码(扩展帧) */
tja1145_write_regster(0X29, 0x00); /* < 29寄存器,配置唤醒帧ID验证码(标准帧/扩展帧)验证码:0x400 */
tja1145_write_regster(0X2A, 0x10); /* < 2A寄存器,配置唤醒帧ID验证码(标准帧/扩展帧)验证码:0x400 */
/* 当支持多帧ID唤醒时,需要通过该类寄存器设置唤醒帧ID掩码,见tja1145文档page19、20 */
tja1145_write_regster(0X2B, 0x00); /* < 2B寄存器,配置唤醒ID掩码; */
tja1145_write_regster(0X2C, 0x00); /* < 2C寄存器,配置唤醒ID掩码; */
/* 支持285,286,165,以及NM报文唤醒 */
tja1145_write_regster(0X2D, 0xfc); /* < 2D寄存器,配置唤醒ID掩码;掩码:0x37f */
tja1145_write_regster(0X2E, 0x0d); /* < 2E寄存器,配置唤醒ID掩码;掩码:0x37f*/
/* 当唤醒条件设置为要判断唤醒帧字段时,需要配置该类寄存器,当唤醒条件设置为只判断帧ID时,只需要需要配置该类寄存器为0XFF */
tja1145_write_regster(0X68, 0xff); /* < 68寄存器,配置唤醒帧数据位掩码 */
tja1145_write_regster(0X69, 0xff); /* < 69寄存器,配置唤醒帧数据位掩码,该寄存器第0位设置为1表示唤醒帧数据位第1个字节第0位置1才可以唤醒MCU */
tja1145_write_regster(0X6A, 0xff); /* < 6A寄存器,配置唤醒帧数据位掩码 */
tja1145_write_regster(0X6B, 0xff); /* < 6B寄存器,配置唤醒帧数据位掩码 */
tja1145_write_regster(0X6C, 0xff); /* < 6C寄存器,配置唤醒帧数据位掩码,该寄存器第0位、第1位设置为1表示唤醒帧数据位第4个字节的第0位或者第1位置1才可以唤醒MCU */
tja1145_write_regster(0X6D, 0xff); /* < 6D寄存器,配置唤醒帧数据位掩码 */
tja1145_write_regster(0X6E, 0xff); /* < 6E寄存器,配置唤醒帧数据位掩码 */
tja1145_write_regster(0X6F, 0xff); /* < 6F寄存器,配置唤醒帧数据位掩码 */
tja1145_write_regster(0X20, 0x71); /* 使能CAN指定ID唤醒 */
tja1145_write_regster(0X01, 0x07); /* < 模式选择寄存器, Sleep mode-0x01,Standby mode-0x04,Normal mode-0x07 */
tja1145_write_regster(0X0A, 0xff); /* < 写保护寄存器,解锁-0x00,上锁-0xFF */
配置二:
如果校验唤醒帧 ID 且校验唤醒帧数据字段,当 TJA1145 芯片收到唤醒帧的ID且收到特定的数据才会拉高INH脚,进入唤醒流程。该配置涉及寄存器与方法一相同。主要有0x29、0x2A、0x2D、0x2E 、0x2F、0x68~0x6F 等寄存器。通过对这几个寄存器的配置可以实现特定帧唤醒和任意帧唤醒。0x29、0x2A寄存器配置唤醒帧ID验证码; 0x2D、0x2E寄存器配置唤醒ID掩码; 0x2F寄存器可配置唤醒条件是否需要校验数据位; 当采用方式一配置唤醒源时,0x68~0x6F寄存器需要全配置为0XFF。寄存器信息见附件TJA1145数据手册。
本文示例为特定帧唤醒(标准帧),唤醒源为:0x400~0x47F 、0x285、0x286且数据字段第4个字节必须为0x03.。具体配置如下:
tja1145_write_regster(0X0A, 0x00); /* < 写保护寄存器,解锁-0x00,上锁-0xFF */
tja1145_write_regster(0X23, 0x03); /* < 唤醒检测使能 */
tja1145_write_regster(0X26, 0x05); /* < 波特率寄存器 */
tja1145_write_regster(0X2F, 0x48); /* < 唤醒条件不需要判断数据位,长度设置为0,PNDM位 置0 */
tja1145_write_regster(0X04, 0x06);
/* 当唤醒条件设置为只判断帧ID时,需要通过该类寄存器设置唤醒帧ID验证码,见tja1145文档page19、20 */
tja1145_write_regster(0X27, 0x00); /* < 27寄存器,配置唤醒帧ID验证码(扩展帧) */
tja1145_write_regster(0X28, 0x00); /* < 28寄存器,配置唤醒帧ID验证码(扩展帧) */
tja1145_write_regster(0X29, 0x00); /* < 29寄存器,配置唤醒帧ID验证码(标准帧/扩展帧)验证码:0x400 */
tja1145_write_regster(0X2A, 0x10); /* < 2A寄存器,配置唤醒帧ID验证码(标准帧/扩展帧)验证码:0x400 */
/* 当支持多帧ID唤醒时,需要通过该类寄存器设置唤醒帧ID掩码,见tja1145文档page19、20 */
tja1145_write_regster(0X2B, 0x00); /* < 2B寄存器,配置唤醒ID掩码; */
tja1145_write_regster(0X2C, 0x00); /* < 2C寄存器,配置唤醒ID掩码; */
/* 支持285,286,165,以及NM报文唤醒 */
tja1145_write_regster(0X2D, 0xfc); /* < 2D寄存器,配置唤醒ID掩码;掩码:0x37f */
tja1145_write_regster(0X2E, 0x0d); /* < 2E寄存器,配置唤醒ID掩码;掩码:0x37f*/
/* 当唤醒条件设置为要判断唤醒帧字段时,需要配置该类寄存器,当唤醒条件设置为只判断帧ID时,只需要需要配置该类寄存器为0XFF */
tja1145_write_regster(0X68, 0x00); /* < 68寄存器,配置唤醒帧数据位掩码,该寄存器第0位设置为1表示唤醒帧数据位第0个字节第0位置1才可以唤醒MCU */
tja1145_write_regster(0X69, 0x00); /* < 69寄存器,配置唤醒帧数据位掩码,该寄存器第0位设置为1表示唤醒帧数据位第1个字节第0位置1才可以唤醒MCU */
tja1145_write_regster(0X6A, 0x00); /* < 6A寄存器,配置唤醒帧数据位掩码,该寄存器第0位设置为1表示唤醒帧数据位第2个字节第0位置1才可以唤醒MCU */
tja1145_write_regster(0X6B, 0x00); /* < 6B寄存器,配置唤醒帧数据位掩码,该寄存器第0位设置为1表示唤醒帧数据位第3个字节第0位置1才可以唤醒MCU */
tja1145_write_regster(0X6C, 0x03); /* < 6C寄存器,配置唤醒帧数据位掩码,该寄存器第0位、第1位设置为1表示唤醒帧数据位第4个字节的第0位或者第1位置1才可以唤醒MCU */
tja1145_write_regster(0X6D, 0x00); /* < 6D寄存器,配置唤醒帧数据位掩码,该寄存器第0位设置为1表示唤醒帧数据位第5个字节第0位置1才可以唤醒MCU */
tja1145_write_regster(0X6E, 0x00); /* < 6E寄存器,配置唤醒帧数据位掩码,该寄存器第0位设置为1表示唤醒帧数据位第6个字节第0位置1才可以唤醒MCU */
tja1145_write_regster(0X6F, 0x00); /* < 6F寄存器,配置唤醒帧数据位掩码,该寄存器第0位设置为1表示唤醒帧数据位第7个字节第0位置1才可以唤醒MCU */
tja1145_write_regster(0X20, 0x71); /* 使能CAN指定ID唤醒 */
tja1145_write_regster(0X01, 0x07); /* < 模式选择寄存器, Sleep mode-0x01,Standby mode-0x04,Normal mode-0x07 */
tja1145_write_regster(0X0A, 0xff); /* < 写保护寄存器,解锁-0x00,上锁-0xFF */
4. 休眠配置
安装好唤醒源后就可以配置TJA1145进入低功耗模式,在进入休眠前要注意先清除所有事件状态位,涉及的寄存器地址有0x60、0x61、0x63、0x64,清除事件状态位的方法为:向0x60、0x61、0x63、0x64寄存器写0xFF。本文示例如下:
tja1145_write_regster(0x0A, 0x00); /*解锁*/
tja1145_write_regster(0X60, 0X00); /*清全局事件状态寄存器*/
tja1145_write_regster(0X61, 0xff); /*清系统事件状态寄存器*/
tja1145_write_regster(0X63, 0xff); /*清发送事件状态寄存器*/
tja1145_write_regster(0X64, 0xff); /*清唤醒事件状态寄存器*/
tja1145_write_regster(0X01, 0X01); /* tja1445进入sleep模式 */