打印

基于华芯微特SWM32SRE的IO模拟LIN总线-缥缈九哥

[复制链接]
6690|4
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
缥缈九哥|  楼主 | 2021-12-15 15:28 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 缥缈九哥 于 2021-12-15 17:15 编辑

基于华芯微特SWM32SRE的IO模拟LIN总线-缥缈九哥原创
不说其它的,上源代码吧:
#include "main.h"

/******************************************************************
Lin总线帧格式:帧头+应答

帧头:同步间隔段(至少13个显性电平)+同步间隔段间隔符(至少1位隐形电平)+同步段(0x55)+字节间间隔+PID(ID+校验位)
注:PID=ID(6位)+校验(2位)
ID 取值范围为: 0x00~0x3f
ID的取值分类:  
                信号携带帧 :                           0x00~0x3b
                诊断帧(主机请求):        0x3c
                诊断帧(从机应答):        0x3d
                保留帧 :                          0x3e,0x3f
P0 = ID0⊕ID1⊕ID2⊕ID4                异或运算
P1 = ┐(ID3⊕ID4⊕ID5⊕ID1)                异或后取非

应答:应答间隔+数据段+校验和段
注:数据段           低字节的低位先发
标准型校验和:        只校验数据段
增强型校验和:        校验数据段以及PID
诊断帧只能用标准型校验和

******************************************************************/

/******************************************************************
lin中断接收函数功能:
1、回环效果:即主机发送帧头或者主机发送帧头+应答,主机的中断服务程序都会接收数据。
        可以检测出:主机串口Tx、Rx、Lin脚,三个引脚上的信号是相同的(除了电平不同)。
2、当串口检测到连续至少11位显性电平即进入中断开始接收。
3、中断服务函数接收数据时按进程推进
        ①接收同步段是否OK?
        ②接收ID校验后解析是数据执行还是反馈
        若是执行:                                        若是反馈:
        ③分步接收数据                                ③准备数据在帧头结束后发送数据
        ④匹配校验数据是否正确
        ⑤解析数据并执行

******************************************************************/
uint8_t TxData = 0;
volatile uint32_t TBtIdx = 0;
volatile uint32_t TxBusy = 0;
#define UART_TX_PIN                        PIN18
#define UART_TX_LOW()                GPIO_ClrBit(GPION, UART_TX_PIN)
#define UART_TX_HIGH()                GPIO_SetBit(GPION, UART_TX_PIN)
#define UART_RX_PIN               PIN19
#define UART_RX_Value()                GPIO_GetBit(GPION, UART_RX_PIN)
//#define UART_RX_POINT                GPIOB, PIN7
#define UART_RX_POINT                GPION, PIN5
uint8_t RxData = 0;
volatile uint32_t RBtIdx = 0;
volatile uint32_t RxBusy = 0;

void uart1_putchar(uint8_t ch)
{
        while(TxBusy){;}                                                                //等待发送空闲
        TxData = ch;TBtIdx = 0;TxBusy = 1;
        TIMR_Start(TIMR1);
        while(TxBusy){;}                                                                //等待发送完成
}
void TIMR1_Handler(void)
{
        TIMR_INTClr(TIMR1);
        if(TBtIdx == 0){UART_TX_LOW(); }else                        //起始位
        if(TBtIdx <  9){if(TxData&0x1){UART_TX_HIGH();}else{UART_TX_LOW();}        TxData>>=1;}else
        if(TBtIdx == 9){UART_TX_HIGH();TIMR_Stop(TIMR1);TxBusy = 0;TBtIdx = 0;return;} //停止位
        TBtIdx++;
}
void uart1_putbuf(uint8_t *buf,uint32_t len){while(len--){uart1_putchar(*buf++);}}
void uart1_puts(char *str){while(*str){uart1_putchar((int)*str++);}}        

void UART1Init(void)
{
        GPIO_Init(GPION, PIN19, 0, 1, 0);                                //GPION.19 配置为输入引脚,开启上拉
        GPIO_Init(GPION, PIN18, 1, 0, 0);                                //GPION.18 配置为输出引脚,推挽输出
        GPIO_Init(GPION, PIN17, 1, 0, 0);                                //GPION.17 配置为输出引脚,推挽输出
        GPIO_Init(UART_RX_POINT , 1, 0, 0);                                //GPIOB.7  配置为输出引脚,推挽输出        用于调试接收采样点
        GPIO_SetBit(GPION, PIN18);
        GPIO_SetBit(GPION, PIN17);
        TIMR_Init(TIMR1, TIMR_MODE_TIMER, (SystemCoreClock/LIN_BPS)*1.00, 1);        //每1/LIN_BPS秒钟触发一次中断,用于发送
        TIMR_Init(TIMR2, TIMR_MODE_TIMER, (SystemCoreClock/LIN_BPS)*0.99, 1);        //每1/LIN_BPS秒钟触发一次中断,用于接收
}

// 主机帧头部分
// 起先是同步间隔段,因为作为主机要连续发送至少13位显性电平,这里用的是STM32自带的库函数,直接调用就行。
void Lin_SendBreak(void)
{
        UART_TX_LOW();                                                                        //显性电平
        DelayUs((1000000*13)/LIN_BPS);                                        //至少13位显性电平
        UART_TX_HIGH();                                                                        //隐形电平
        DelayUs((1000000*1 )/LIN_BPS);                                        //至少01位隐形电平
}

// 接着就是同步段,发送0x55
void Lin_SendSyncSegment(void){uart1_putchar(0x55);}
//然后就是发送PID(protect ID),这里的前六位为ID,后两位为校验位,函数功能为:输入ID,返回PID。
uint8_t Lin_CheckPID(uint8_t id)
{
        uint8_t P0 = (((id)^(id>>1)^(id>>2)^(id>>4))&0x01)<<6 ;
        uint8_t P1 = ((~((id>>1)^(id>>3)^(id>>4)^(id>>5)))&0x01)<<7 ;
        return (id|P0|P1) ;
}
// 该函数体就是单片机作为主机发送的帧头,可以指定ID发送帧头,接收从机返回的数据;也可以发送帧头+数据,让从机接收。
void Lin_Hearder(uint8_t id)
{
        Lin_SendBreak();
        Lin_SendSyncSegment();
        uart1_putchar(Lin_CheckPID(id));
}
// 此段函数功能:输入ID+数据,返回校验和段,里面有调用返回PID函数。诊断帧只能用标准校验这里还没有验证过,因为校验还没有测试。
// 是经典校验还是增强校验,另:诊断帧只能经典校验
uint8_t Lin_Checksum(uint8_t id , uint8_t data[])
{
        uint8_t t ;
        uint16_t sum ;

        sum = data[0];
        if(id == 0x3c)                        // 如果是诊断帧,用经典校验
        {
                for(t=1;t<8;t++)
                {
                        sum += data[t];
                        if(sum&0xff00)
                        {
                                sum&=0x00ff;
                                sum+=1;
                        }
                }
                sum = ~sum;        
                return (uint8_t)sum ;
        }
        for(t=1;t<8;t++)
        {
                sum += data[t];
                if(sum&0xff00)
                {
                        sum&=0x00ff;
                        sum+=1;
                }
        }
        sum+=Lin_CheckPID(id);
        if(sum&0xff00)
        {
                sum&=0x00ff;
                sum+=1;
        }
        sum = ~sum;        
        return (uint8_t)sum ;
}
//上面三个函数是单片机无论作为主机还是从机都需要用到的部分,所以在后面进行预编译选择的时候,放到外面。
//这里是主机的响应函数调用。
void Lin_Response(uint8_t id ,uint8_t data[])
{
        uart1_putbuf(data,8);
        uart1_putchar(Lin_Checksum(id,data));
}
void Lin_MainPutData(uint8_t id,uint8_t *data)
{
        Lin_Hearder(id);
        Lin_Response(id,data);
}
uint8_t uart1_getchar(void)
{
        TIMR_Start(TIMR2);
        TBtIdx = 0;RxBusy = 1;
        while(RxBusy){;}                                                                //等待接收完成
        return RxData;
}
void TIMR2_Handler(void)
{
        TIMR_INTClr(TIMR2);
        if(RBtIdx == 0){}else                                                        //起始位
        if(RBtIdx <  9){RxData>>=1;GPIO_InvBit(UART_RX_POINT);if(UART_RX_Value()){RxData|=0x80;}GPIO_InvBit(UART_RX_POINT);}else
        if(RBtIdx == 9){TIMR_Stop(TIMR2);RxBusy = 0;RBtIdx = 0;return;} //停止位
        RBtIdx++;
}
uint8_t Lin_MainGetData(uint8_t id,uint8_t *data)
{
        Lin_Hearder(id);                                                                //发送命令头
        while(UART_RX_Value()){;}                                                //等待起始位
        DelayUs((1000000*0.425)/LIN_BPS);                                //延时半个采样点
        for(int i=0;i<8;i++){data=uart1_getchar();}        //接收8字节数据
        uint8_t ReceiveCheckSum = uart1_getchar();                //接收校验和
        uint8_t SumCheck = Lin_Checksum(id,data);                //计算校验和
//        printf("ReceiveCheckSum & SumCheck: 0x%02x=0x%02x ",ReceiveCheckSum,SumCheck);
        if(ReceiveCheckSum == SumCheck)        {return 1;}                //校验正确
        return 0;
}        

/*
代码完毕
部分内容参考了CSDN上的帖子
*/




使用特权

评论回复

相关帖子

沙发
缥缈九哥|  楼主 | 2021-12-15 15:29 | 只看该作者
不小心TXD和RXD交叉了,只好将错就错用IO模拟了。哈哈。

使用特权

评论回复
板凳
缥缈九哥|  楼主 | 2024-3-31 22:04 | 只看该作者
估计没有人用LIN总线。

使用特权

评论回复
地板
ysf| | 2024-4-15 20:28 | 只看该作者
收藏之,方便以后理解LIN总线

使用特权

评论回复
5
zenghlzenghl| | 2024-4-29 18:49 | 只看该作者
收藏之,方便以后理解LIN总线

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:童时不懂世事艰,笑谈学成锦衣还。岁月无声已先过,男儿有泪空自弹。    莫待沾霜愁上发,须嬴吐气喜开颜。拼搏半年誓如愿,不到长城心不甘。

67

主题

1868

帖子

271

粉丝