基于瑞萨MCU的汽车LIN通讯模拟器设计

描述

本文转载自立创商城,作者白羊座超越

一、项目功能介绍

此项目是为辅助汽车通讯电子行业人员调试LIN通讯逻辑和测试产品而设计。

在汽车电子通讯行业,用到LIN通讯的功能模块可能包括雨量传感器、雨刮开关、前大灯、座椅位置马达、压力传感器、车门的后视镜、窗户控制等这些对通讯速度要求不高(和CAN通讯相比)的产品。车厂需要根据不同使用场景协调所有车身传感器和执行器运行不同逻辑,在整车设计验证阶段,车厂会分解不同的任务和通讯信号,然后下发给对应的供应商,供应商需要根据上下节点通讯设备的理论设计来针对性调试自己家设备的逻辑,而此时,就会用到LIN通讯相关的调试设备来模拟上下节点的LIN命令。

本项目旨在提供一种对于调试和测试LIN设备较为方便和友好的嵌入式设备,且设计和生产成本控制较低。设备的方便性体现在设备小型化,手持即可,并且集成12V电池可充电设计。友好性体现在有直观的按键和屏幕显示进行交互,使用简单,学习成本很低,对于需要频繁调试LIN通讯逻辑的工作效率很高。

二、项目属性

项目为近期设计验证,首次公开且为原创。

三、开源协议

GPL 3.0

四、硬件部分

4.1本设备的原理图

PCB,3D外壳和面板全部用立创EDA设计,硬件设定的需求如下:

a. 支持LIN2.1通讯功能。

b. 设备在室内使用12V电源外部供电,保证全天可靠工作。

c. 设备在室外使用电池续航4小时,电池既给本身供电,也给从机供电,工作功耗平均大概4.5W(12V,375mA)。

d. 有ADC分压电路,设备自己能检测正极和负极之间电压。

e. 有屏幕显示设备主机和从机信号状态。

f. 有按键能方便切换自身信号状态。

g. 能在2个月内的业余时间完成,因为我7月底才开始有想法设计这个项目,近期工作出差又较多。

考虑到开发周期并不充裕,还要给软件设计留有时间,所以单元电路基本都是采用几种较为成熟的方案来验证实际效果是否可行。目前电路和PCB设计验证过程修改过几次,下面我将遇到过的问题,解决方法和需要注意的地方介绍一下。

4.2电路主要为以下几个部分

4.2.1 供电电路设计,12V转5V,5V转3.3V电路如下图

此部分电路变更过2次,第1次直接使用12V转3.3V的AMS1117,验证后发现发烫比预想严重,手摸上去无法承受的那种烫,手边没有温度检测仪,没测具体温度,设计之前也大概了解LDO在压差较大并且带有较大负载的情况下会发热,但没想到100多毫安就能这么烫,所以后续改成两次降压。

第2次电路和下面截图这种方案唯一的区别就是12V转5V芯片使用的也是AMS1117系列,LDO在PCB位置打孔和开窗加快散热后,温度已经能接受,但搜到的几款AMS1117输入电压要求都在18V以下,本设备常规使用时供电电压在9V~16V,但考虑到设备在极端测试时,会有16V到20V供电的场景,所以换成如图中CJ7805。最大输入电压可以到35V,在PCB上增加LDO周围铺铜填充面积,同样打孔和开窗后,温度可以接受,手摸上去可以感受到发热,但不至于烫手。

图中可以看到有两路保险丝,是因为在第一版PCB到手后调试过程发生过短路,板子上LDO,MCU和LIN等芯片全部烧毁,为避免之后在发生此类情况,增加了这两路保险,第一路是保险丝是单板调试时使用,能承受最大电压16V,保持电流100mA跳闸电流250mA,第二路保险丝是全场景工作时使用,能承受最大电压60V,保持电流500mA,跳闸电流1A。增加的保险丝也确实在我后来的几次调试中发挥了重要作用,所以就保留了下来,根据实际负载情况,用跳线帽选择其中一路。

LINLIN

4.2.2 LIN通讯电路设计如下图

参照芯片手册典型应用设计,其中2号SLP_N引脚用来控制芯片休眠和唤醒,高电平正常工作模式,低电平休眠,指导手册里SLP_N和MCU引脚相连,这样可以控制LIN芯片休眠和唤醒。本项目设备开机就是要使用LIN通讯,所以没有LIN芯片休眠需求,就接高电平了,注意不能悬空。

LIN

4.2.3 编码旋钮和轻触按键电路设计如下图

目前设备只用到轻触按键,编码旋钮是为了给以后其他交互需求预留。原理图很简单,这里需要注意的是,选择MCU检测按键的引脚时,一定要确认对应引脚是支持输入模式的。

LIN

4.2.4 LCD屏幕接口电路如下图

图中LCD底座接口为了使用立创商城编号C5329587的显示屏,商城截图如下,最后几个库存被我全买了,不知道商城多久会补货,淘宝上也有线序和通讯型号一样的LCD可以选购,插到PCB上显示功能正常,但是尺寸有差异,和本项目一起设计的3D外壳可能会不匹配。

此部分电路设计需要注意的是LCD是3.3V供电,LCD的通讯引脚支持的最大电压也是3.3V,所以MCU的供电也要选择3.3V。

至于图中LCD右侧支撑件是为了保证屏幕安装到PCB上面后,两端高度一样,所以在PCB上对应位置放置一个没有任何连接的接插件。这里需要注意的是,选择MCU与LCD通讯的引脚时,一定要确认引脚是支持输出模式的。

LINLIN

4.2.5 12V电池充放电电路设计如下图

图中充放电管理IC用的是CM1033-DS。是一款专用于 3 串锂/铁电池或聚合物电池包的充放电保护芯片,串联3节1500mAH以上的18650电池,能满足设备续航需求。图中网络标识12V和12V_02连接是一个滑动开关,上述LDO电路中有具体连接方式,LIN通讯电路里也有用到。

LIN

4.2.6 MCU主控选用瑞萨RL78系列型号R5F10BGGLFB

此型号是瑞萨RL78/F13系列的车规级芯片。

LIN

4.2.7 ADC检测电路如下图

实际电池正极12V_02和GND之间的电压=VOLTAG*15,受限于分压电阻精度和电源电压波动,最终值预计会有0.3V的误差。作为辅助设备,此功能只是用来评估设备大概电池电量,已经够用。

LIN

4.2.8 预留TTL串口输出调试接口

LIN

五、软件部分

封面图片是调试LCD成功后先显示了这次比赛的官方图片。因为这张图片占用ROM空间达23K多,为了给需求功能代码保留足够ROM空间,此张图片仅用来单独做展示。

LIN

为了方便说明,功能演示视频里只用了两个设备,一个主机和一个从机,只选用了两个容易理解的参数。演示软件也是在此基础上进行的裁剪和调整。实际应用时一个主机会和多个从机通讯,然后主机根据不同从机的参数综合判断,执行不同的逻辑。

5.1本设备的软件使用瑞萨官方编译工具

CS+ for CC (RL78,RX,RH850)设计,硬件调试工具使用瑞萨官方下载器E2_Lite。软件设定的需求如下:

a. 主机有发送LIN数据帧和接收LIN数据帧的任务。

b. 从机有接收LIN数据帧和回复LIN数据帧的任务。

c. 有计时器任务。

d. 有检测按键任务。

e. 有屏幕刷新任务。

f. 有ADC检测任务,显示正极和负极之间电压值。

g. 上述每个功能任务被调用时持续阻塞时间不能超过10毫秒。

5.2从机软件几个任务设计思路如下

5.2.1 检测按键key任务

key.c代码

左右滑动查看完整内容

 

SYS_Key_Task    comm_Key;                                                                             /*结构体变量定义*/
/*按键检测任务*/
void Comm_Key_Task(void)
{
switch( comm_Key.mode )                                                                      /*comm_Key.mode:任务状态值*/
{
case  ST_Check_Key_first:                                                               /*第1步:实时检测按键状态*/
if( (0 == KEY_01 )||(0 == KEY_02 )||(0 == KEY_03 )||(0 == KEY_04 )||(0 == KEY_05 )||(0 == KEY_06 )||(0 == BUTTON_01 )||(0 == BUTTON_02 )||(0 == BUTTON_03 ) )
{
comm_Key.time = 0;
comm_Key.mode = ST_Check_Key_second;                 /*检测到任一按键按下,切换comm_Key.mode的值进入ST_Check_Key_second状态*/
}
break;
case  ST_Check_Key_second:                                                          /*第2步:延时后再次检测,即按键去抖*/
if( comm_Key.time >= 20 )                                                    /*去抖时间20毫秒*/
{
comm_Key.mode = 0;                                                 /*避免后续无出口进入此状态的无线循环,先设置初始值,如果确实有按键,则会重新修改此值*/
comm_Key.time = 0;
if( KEY_01 == 0 )
{
comm_Key.mode = ST_Confirm_Key_01;          /*确认按键K1被按下,切换comm_Key.mode的值进入ST_Confirm_Key_01状态*/
}
if( KEY_02 == 0 )
{
comm_Key.mode = ST_Confirm_Key_02;          /*同上*/
}
if( KEY_03 == 0 )
{
comm_Key.mode = ST_Confirm_Key_03;
}
if( KEY_04 == 0 )
{
comm_Key.mode = ST_Confirm_Key_04;
}
if( KEY_05 == 0 )
{
comm_Key.mode = ST_Confirm_Key_05;
}
if( KEY_06 == 0 )
{
comm_Key.mode = ST_Confirm_Key_06;
}
if( BUTTON_01 == 0 )
{
comm_Key.mode = ST_Confirm_Button_01;       /*确认按键B1被按下,切换comm_Key.mode的值进入ST_Confirm_Button_01状态*/
}
if( BUTTON_02 == 0 )
{
comm_Key.mode = ST_Confirm_Button_02;       /*同上*/
}
if( BUTTON_03 == 0 )
{
comm_Key.mode = ST_Confirm_Button_03;
}
}
break;
case  ST_Confirm_Key_01:                                                       /*按键K1按下后逻辑*/
LIN_config.Num_00 = LIN_config.Num_00 + 1;            /*LIN_config.Num_00的值就是雨刷开关的参数*/
if( LIN_config.Num_00 >= 2 )                                        /*LIN_config.Num_00=0,雨刷开关参数=关;  LIN_config.Num_00=1,雨刷开关参数=开;*/
{
LIN_config.Num_00 = 0;                                       /*LIN_config.Num_00的值超出范围后,赋初值*/
}
comm_Key.mode = ST_Check_End;                               /*按键K1按下逻辑处理结束,切换comm_Key.mode的值进入ST_Check_End状态*/
break;
case  ST_Confirm_Key_02:                                                       /*按键K2按下后逻辑*/
LIN_config.Num_01 = LIN_config.Num_01 + 1;            /*LIN_config.Num_01的值就是雨量大小的参数*/
if( LIN_config.Num_01 >= 4 )                                        /*LIN_config.Num_01=0,雨量参数=无雨;  LIN_config.Num_01=1,雨量参数=小雨;*/
{                                                                                     /*LIN_config.Num_01=2,雨量参数=中雨;  LIN_config.Num_01=3,雨量参数=大雨;*/
LIN_config.Num_01 = 0;
}
comm_Key.mode = ST_Check_End;
break;
case  ST_Confirm_Key_03:                                  /**/
comm_Key.mode = ST_Check_End;
break;
case  ST_Confirm_Key_04:                                  /**/
comm_Key.mode = ST_Check_End;
break;
case  ST_Confirm_Key_05:                                  /**/
comm_Key.mode = ST_Check_End;
break;
case  ST_Confirm_Key_06:                                  /**/
comm_Key.mode = ST_Check_End;
break;
case  ST_Confirm_Button_01:                               /**/
comm_Key.mode = ST_Check_End;
break;
case  ST_Confirm_Button_02:                               /**/
comm_Key.mode = ST_Check_End;
break;
case  ST_Confirm_Button_03:                               /**/
comm_Key.mode = ST_Check_End;
break;
case  ST_Check_End:                                                              /*按键按下后等待松开的逻辑*/
if( (1 == KEY_01 )&&(1 == KEY_02 )&&(1 == KEY_03 )&&(1 == KEY_04 )&&(1 == KEY_05 )&&(1 == KEY_06 )&&(1 == BUTTON_01 )&&(1 == BUTTON_02 )&&(1 == BUTTON_03 ) )
{
comm_Key.mode = ST_Check_Key_first;             /*只有按键松开后,才会进入下一次按键检测,能避免按键连击*/
}
break;
default:
comm_Key.mode = ST_Check_Key_first;
break;
}
}
key.h代码
/*状态机预设的枚举变量*/
enum{
    ST_Check_Key_first,
    ST_Check_Key_second,
    ST_Confirm_Key_01,
    ST_Confirm_Key_02,
    ST_Confirm_Key_03,
    ST_Confirm_Key_04,
    ST_Confirm_Key_05,
    ST_Confirm_Key_06,
    ST_Confirm_Button_01,
    ST_Confirm_Button_02,
    ST_Confirm_Button_03,
    ST_Check_End
};
/*按键宏定义*/
#define KEY_01    P3_bit.no2
#define KEY_02    P7_bit.no0
#define KEY_03    P7_bit.no1
#define KEY_04    P7_bit.no3
#define KEY_05    P14_bit.no0
#define KEY_06    P0_bit.no0
#define BUTTON_01    P6_bit.no3
#define BUTTON_02    P6_bit.no2
#define BUTTON_03    P6_bit.no1
/*任务状态相关结构体*/
typedef struct 
{
    uint8_t    time;
    uint8_t    mode;
}SYS_Key_Task;
/*结构体变量声明*/
extern SYS_Key_Task    comm_Key;
/*任务函数*/
void Comm_Key_Task(void);

 

5.2.2 屏幕刷新任务

LCD驱动基础代码资料,可以从本屏幕的厂家 HS(汉昇)得到,此屏幕的立创商品编号C5329587。从网上其他论坛或者网购平台也容易下载到此屏幕用到的ST7789芯片资料。下面对应用层做一些说明:

刷新屏幕数据是很耗时的,本项目的芯片IO刷新一次整个屏幕数据(320*240),大概需要1.3秒,所以上电后先把固定显示的内容刷新出来,然后只刷新实时变动的参数,这也是普遍做法。为了保证任务被调用时持续阻塞时间不能超过10毫秒,刷新完其中一个参数就先退出本任务,执行完其他任务再刷新下一个参数,以此类推。

LCD固定显示内容如下:

左右滑动查看完整内容

 

void Init_Lcd_Interface_Chinese(void)
{
    int Show=0;
    LCD_Fill(0,0,LCD_W,LCD_H,LGRAYBLUE);                                                                                          /*刷新整个屏幕填充色*/
    LCD_ShowChinese(0,   0,  " 本从机回复参数    主机下发指令  ",RED,WHITE,16,0);
    LCD_ShowChinese(0,  35,  "雨刷开关参数:             ",RED,WHITE,16,0);
    LCD_ShowChinese(0,  70,  "雨量大小参数: 雨           ",RED,WHITE,16,0);
    LCD_ShowChinese(0, 105,  "           雨刷运动指令:  ",RED,WHITE,16,0);
    LCD_ShowString( 0, 195,  "__________________________",      RED,LGRAYBLUE,12,0);      /*电池电压值不属于通讯参数,通过横线区分开*/
    LCD_ShowChinese(0, 210,  "本机电池电压:   ",RED,WHITE,16,0);
    LCD_ShowString(144,210,  "V",RED,WHITE,16,0);
    for(Show=0;Show<20;Show++)
    {
        LCD_ShowString(152,  Show*12,"|",RED,LGRAYBLUE,16,0);                                                          /*主机和从机参数通过中线分开*/
    }
    LCD_WR_REG(0x29);
}


LCD刷新任务


void Comm_Updata_Lcd_Task(void)
{
switch( comm_Lcd_Task.mode )
{
case  0x00:                                  /**/
if( LIN_config.Num_00 == 0 )
{
LCD_ShowChinese(112, 35,  "关",RED,WHITE,16,0);
}
else if( LIN_config.Num_00 == 1 )
{
LCD_ShowChinese(112, 35,  "开",RED,WHITE,16,0);
}
comm_Lcd_Task.mode = comm_Lcd_Task.mode + 1;
break;
case  0x01:                                  /**/
if( LIN_config.Num_01 == 0 )
{
LCD_ShowChinese(112, 70,  "无",RED,WHITE,16,0);
}
else if( LIN_config.Num_01 == 1 )
{
LCD_ShowChinese(112, 70,  "小",RED,WHITE,16,0);
}
else if( LIN_config.Num_01 == 2 )
{
LCD_ShowChinese(112, 70,  "中",RED,WHITE,16,0);
}
else if( LIN_config.Num_01 == 3 )
{
LCD_ShowChinese(112, 70,  "大",RED,WHITE,16,0);
}
comm_Lcd_Task.mode = comm_Lcd_Task.mode + 1;
break;
case  0x02:                                 /**/
if( LIN_config.Num_02 == 0 )
{
LCD_ShowChinese(288, 105,  "静止",RED,WHITE,16,0);
}
else if( LIN_config.Num_02 == 1 )
{
LCD_ShowChinese(288, 105,  "慢速",RED,WHITE,16,0);
}
else if( LIN_config.Num_02 == 2 )
{
LCD_ShowChinese(288, 105,  "中速",RED,WHITE,16,0);
}
else if( LIN_config.Num_02 == 3 )
{
LCD_ShowChinese(288, 105,  "快速",RED,WHITE,16,0);
}
comm_Lcd_Task.mode = comm_Lcd_Task.mode + 1;
break;
case  0x03:                                  /**/
comm_Lcd_Task.mode = 0;                /*所有参数刷新一轮,返回第一个参数重新开始*/
break;
case  0x10:                                  /*英文界面*/
Init_Lcd_Interface_English();
comm_Lcd_Task.mode = 0;
break;
case  0x11:                                  /*中文界面*/
Init_Lcd_Interface_Chinese();
comm_Lcd_Task.mode = 0;
break;
default:
comm_Lcd_Task.mode = 0;
break;
}
}

 

5.2.3 ADC检测任务

左右滑动查看完整内容

 

uint16_t buffer_abc;
void Comm_Adc_Task(void)
{
switch( comm_Adc.mode )
{
case  0x00:                                  /**/
R_ADC_Get_Result(&buffer_abc);         /*VOLTAG引脚的ADC值放入变量buffer_abc*/
comm_Adc.voltag = ((float)buffer_abc*3300/1024)*15;      /*分压电阻15倍*/
comm_Adc.voltag = comm_Adc.voltag/1000;                     /*mV转换成V*/
comm_Adc.mode = comm_Adc.mode + 1;
break;
case  0x01:                                  /**/
if( comm_Adc.time >= 2000 )                           /*每隔2秒更新一次电压值*/
{
comm_Adc.time = 0;
LCD_ShowFloatNum1(112, 210,comm_Adc.voltag,3,RED,YELLOW,16);
comm_Adc.mode = 0;
}
break;
case0x02:                                 /**/
break;
default:
break;
}
}

 

上述从机的几个软件任务设计思路,在主机软件任务设计里也类似,不再赘述。下面给出其他两段代码来说明:LIN初始化代码,其实这部分代码从瑞萨官网和技术支持那里也可以得到,需要一些LIN通讯基础知识。

左右滑动查看完整内容

 

void RLIN_Master_Init(void)
{
LCHSEL = 0x00;         /*  Selects RLIN0 */
PER2  |= 0x04;         /*  Enable input clock supply RLIN0*/
LINCKSEL=0x00;         /*  selects the  fclk=32MHz  clock to RLIN0.*/
LWBR0  = 0x01;         /*  b0=1, LIN2.0 or 2.1;  Prescaler Clock Selcet 1/1;  bit sampling count select 0000 : 16 sampling. */ 
LBRP00 = 0x67;         /*   fa:0X67=103D, Baud rate= 32M/ (103+1)*16= 19230 bps  fb:9615bps    fc:2403bps */
LBRP01 = 0x5F;         /*   fd:10416bps*/
LIN0RVCIF = 0U;        /*  Clear Reception interrupt request signal */
LIN0TRMIF = 0U;        /*  Clear Transmission interrupt request signal */
LIN0WUPIF = 0U;        /*  Clear Wake up interrupt request signal */
LIN0IF    = 0U;        /*  Clear LIN or LIN Status interrupt */
LIN0RVCMK = 0U;        /*  interrupt reception servicing enable */
LIN0TRMMK = 0U;        /*  interrupt transmission servicing enable */
LIN0WUPMK = 0U;        /*  interrupt wake up servicing enable */
LIN0MK    = 0U;        /*  interrupt Status servicing enable */
LIE0  |=  0x0F;        /*  Enable successful response/wake-up reception interrupt, enable all interrupt*/
LEDE0 |=  0x8F;        /*  Enable error detection */
/*  Header format setting*/
LMD0  =   0x10;         /*   b0b1=00: LIN master mode ; b3b2=00: fa=LIN sysclock;  b4=1:transmission interrupt,sucessful reception interrupt...; b5=0: The noise filter is enable.*/
LBFC0 =   0x15;         /*  b3-b0=0101: transmission break width 18Tbits; b5b4=01: break delimiter 2Tbit*/
LSC0  =   0x11;         /*  b2-b0=001:inter-byte space 1bit or Response space 4bit; b5b4=01: inter-byte space 1Tbit;*/
LWUP0 =   0x30;         /*  b7-b4=0100: Wake-up Transmission low width 4 bits.*/
LIDB0 &=  0x00;         /*  Clear the ID buffer */
ISC   =   0x00;         /*  INTP11 pin input signal is set as external interrupt input*/
LINCKSEL|=0x10;         /*  Enable RLIN0 engine clock supply,*/
}

 

主机处理从机参数逻辑:

左右滑动查看完整内容

 

void LIN_received(void)
{
/* Processing received LIN data */
LIN_config.Num_09 = Master_RxData1[0];   /*接收的雨刷开关参数*/
LIN_config.Num_10 = Master_RxData1[1];   /*接收的雨量大小参数*/
if(LIN_config.Num_09 == 1 )                        /*开关参数:开*/
{
if( LIN_config.Num_10 == 0 )              /*雨量参数:无雨*/
{
LIN_config.Num_02 = 0;            /*雨刷运动指令:静止*/
}
else if( LIN_config.Num_10 == 1 )      /*雨量参数:小雨*/
{
LIN_config.Num_02 = 1;            /*雨刷运动指令:慢速*/
}
else if( LIN_config.Num_10 == 2 )      /*雨量参数:中雨*/
{
LIN_config.Num_02 = 2;            /*雨刷运动指令:中速*/
}
else if( LIN_config.Num_10 == 3 )      /*雨量参数:大雨*/
{
LIN_config.Num_02 = 3;            /*雨刷运动指令:快速*/
}
}
else
{
LIN_config.Num_02 = 0;                     /*雨刷运动指令:静止*/
}
}

 

六、大赛LOGO验证

LIN

手工焊接前PCB正面(打样时选择了基础库SMT)

LIN

PCB背面

LIN

全部焊接后正面

LIN

背面

LIN

3D外壳+面板组装后

LIN

立创EDA设计界面截图

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分