#include "main.h" #if 1 /****************************************************************** 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校验后解析是数据执行还是反馈 若是执行: 若是反馈: ③分步接收数据 ③准备数据在帧头结束后发送数据 ④匹配校验数据是否正确 ⑤解析数据并执行 ******************************************************************/ #define LIN_TX_PIN PIN3 #define LIN_TX_LOW() GPIO_ClrBit(GPIOD, LIN_TX_PIN) #define LIN_TX_HIGH() GPIO_SetBit(GPIOD, LIN_TX_PIN) #define LIN_RX_PIN PIN4 #define LIN_RX_Value() GPIO_GetBit(GPIOD, LIN_RX_PIN) #define MAX_LIN_BUF 16 __IO uint8_t lin_active=0; __IO uint8_t lin_index=0; uint8_t lin_buf[MAX_LIN_BUF]; //void lin_putchar(uint8_t ch){while(UART_IsTXFIFOFull(UART1)){;}UART1->DATA=ch;} void lin_putchar(uint8_t ch) { UART_WriteByte(UART1, ch); while(UART_IsTXBusy(UART1)) {;} } void lin_putbuf(uint8_t *buf,uint32_t len) { while(len--) { lin_putchar(*buf++); } } void lin_puts(char *str) { while(*str) { lin_putchar((int)*str++); } } uint8_t lin_getchar(void) { int i=0; while(!(UART1->CTRL & 0x8)) { DelayUs(1); //超过2MS没有数据就退出 。 if(i++>2000) { return 0xff; } } return (UART1->DATA&0xff); } void LIN_Init(void) { UART_InitStructure UART_initStruct; GPIO_Init(GPIOD, PIN3, 0, 1, 0, 0); //GPIOD.3 配置为输入引脚,开启上拉 GPIO_Init(GPIOD, PIN4, 0, 1, 0, 0); //GPIOD.4 配置为输入引脚,开启上拉 PORT_Init(PORTD, LIN_RX_PIN, PORTD_PIN4_UART1_RX, 1); //GPIOD.4 配置为UART1输入引脚 PORT_Init(PORTD, LIN_TX_PIN, PORTD_PIN3_UART1_TX, 0); //GPIOD.3 配置为UART1输出引脚 GPIO_Init(GPIOC, PIN4, 1, 0, 0, 0); //GPIOC.4 配置为输出引脚,推挽输出 GPIO_SetBit(GPIOC, PIN4); UART_initStruct.Baudrate = LIN_BPS; UART_initStruct.DataBits = UART_DATA_8BIT; //数据位位数,可取值UART_DATA_8BIT、UART_DATA_9BIT UART_initStruct.Parity = UART_PARITY_NONE; //奇偶校验位,可取值UART_PARITY_NONE、UART_PARITY_ODD、UART_PARITY_EVEN、UART_PARITY_ONE、UART_PARITY_ZERO UART_initStruct.StopBits = UART_STOP_1BIT; //停止位位数,可取值UART_STOP_1BIT、UART_STOP_2BIT UART_initStruct.TXThreshold = 0; //取值0--7 UART_initStruct.TXThresholdIEn = 0; //当TX FIFO中数据个数 <= TXThreshold时触发中断 UART_initStruct.RXThreshold = 7; //取值0--7 UART_initStruct.RXThresholdIEn = 0; //当RX FIFO中数据个数 >= RXThreshold时触发中断 UART_initStruct.TimeoutTime = 4; //超时时长 = TimeoutTime/(Baudrate/10) 秒 = 5/1920 = 2080 uS UART_initStruct.TimeoutIEn = 1; //超时中断,超过 TimeoutTime/(Baudrate/10) 秒没有在RX线上接收到数据时触发中断 UART_Init(UART1, &UART_initStruct); NVIC_SetPriority(UART1_IRQn, 6); UART_Open(UART1); } // 主机帧头部分 // 起先是同步间隔段,因为作为主机要连续发送至少13位显性电平,这里用的是STM32自带的库函数,直接调用就行。 void Lin_SendBreak(void) { GPIO_Init(GPIOD, LIN_TX_PIN, 1, 0, 0, 0); //GPIOD.3 配置为输出引脚,推挽输出 LIN_TX_LOW(); //显性电平 DelayUs((1000000*13)/LIN_BPS); //至少13位显性电平 LIN_TX_HIGH(); //隐形电平 DelayUs((1000000*1 )/LIN_BPS); //至少01位隐形电平 PORT_Init(PORTD, LIN_TX_PIN, PORTD_PIN3_UART1_TX, 0); //GPIOD.3 配置为UART1输出引脚 } // 接着就是同步段,发送0x55 void Lin_SendSyncSegment(void) { lin_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(); lin_putchar(Lin_CheckPID(id)); } // 此段函数功能:输入ID+数据,返回校验和段,里面有调用返回PID函数。诊断帧只能用标准校验这里还没有验证过,因为校验还没有测试。 // 是经典校验还是增强校验,另:诊断帧只能经典校验 uint8_t Lin_Checksum(uint8_t id, uint8_t data[]) { uint16_t sum ; sum = data[0]; if(id == 0x3c) // 如果是诊断帧,用经典校验 { for(int t=1; t<8; t++) { sum += data[t]; if(sum&0xff00) { sum&=0x00ff; sum+=1; } } sum = ~sum; return (uint8_t)sum ; } for(int 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[]) { lin_putbuf(data,8); lin_putchar(Lin_Checksum(id,data)); } void Lin_MainPutData(uint8_t id,uint8_t *data) { Lin_Hearder(id); Lin_Response(id,data); DelayUs((1000000*10)/LIN_BPS); //至少01位隐形电平 } #if 0 uint8_t Lin_MainGetData(uint8_t id,uint8_t *data) { uint8_t buf[12]; while((UART1->CTRL&0x8)) { uint8_t tmp=UART1->DATA; //清接收缓冲 } Lin_Hearder(id); //发送命令头 //0x55 0xb1 0x12 0x34 0x56 0x78 0x90 0xab 0xcd 0xef 0x3f for(int i=0; i<11; i++) { buf[i]=lin_getchar(); //接收11字节数据 } // for(int i=0;i<11;i++){printf("0x%02x ",buf[i]);} memcpy(data,buf+2,8); //取出数据 uint8_t ReceiveCheckSum = buf[10]; //接收校验和 uint8_t SumCheck = Lin_Checksum(id,data); //计算校验和 // printf("ReceiveCheckSum & SumCheck: 0x%02x=0x%02x \n\r",ReceiveCheckSum,SumCheck); if(ReceiveCheckSum == SumCheck) { return 1; //校验正确 } return 0; } #else uint8_t Lin_MainGetData(uint8_t id,uint8_t *data) { int delay=0; while((UART1->CTRL&0x8)) { uint8_t tmp=UART1->DATA; //清接收缓冲 } Lin_Hearder(id); //发送命令头 lin_active=0; lin_index=0; UART1->CTRL |=0x00000010; //开中断 while(!lin_active) { if(delay++>LIN_DELAY) { return 0; } DelayUs(100); } // for(int i=0;iCTRL &=0xffffffef; //关中断 uint8_t ReceiveCheckSum = lin_buf[10]; //接收校验和 uint8_t SumCheck = Lin_Checksum(id,data); //计算校验和 // printf("ReceiveCheckSum & SumCheck: 0x%02x=0x%02x \n\r",ReceiveCheckSum,SumCheck); if(ReceiveCheckSum == SumCheck) { return 1; //校验正确 } return 0; } #endif #if UART1_IS_LIN void UART1_Handler(void) { uint32_t tmp; if(UART_INTStat(UART1, UART_IT_RX_THR)) //阈值中断 { UART_INTClr(UART1, UART_IT_RX_THR); //uart0_putchar('*');//测试阈值中断 for(uint16_t i=0; i<6; i++) //读取6个字节,留1个给超时中断再读,否则缓冲空了产生不了超时中断 { if(UART_ReadByte(UART1, &tmp) == 0) { if(lin_index>=MAX_LIN_BUF) { lin_index=0; //防止缓冲溢出重新把接收指针指向缓冲开始 } lin_buf[lin_index++] = tmp; //读取字符 } else { lin_index=0; //字符出错丢弃后重新把接收指针指向缓冲开始 } } } if(UART_INTStat(UART1, UART_IT_RX_TOUT))//超时中断 { UART_INTClr(UART1, UART_IT_RX_TOUT); //uart0_putchar('#');//测试超时中断 while(UART_IsRXFIFOEmpty(UART1) == 0) { if(UART_ReadByte(UART1, &tmp) == 0) { if(lin_index>=MAX_LIN_BUF) { lin_index=0; //防止缓冲溢出重新把接收指针指向缓冲开始 } lin_buf[lin_index++] = tmp; //读取字符 } else { lin_index=0; //字符出错丢弃后重新把接收指针指向缓冲开始 } } lin_index=0; lin_active=1; //取走接收数据长度并重新把接收指针指向缓冲开始,表明数据有效 } } #endif void lin_test(int i) { uint8_t data[10]= {0xFE,0xDC,0xBA,0x98,0x76,0x54,0x32,0x10,0x99,0xaa}; if((i%500)==0) { int ret=Lin_MainGetData(0x32,data); if(ret) { for(int i=0; i<8; i++) { printf("0x%02x ",data[i]); } } // data[0]+=1;data[1]+=2;data[2]+=3; Lin_MainPutData(0x31,data); } } #else #define UART_RX_LEN 128 char UART_RXBuffer[UART_RX_LEN] = {0}; volatile uint32_t UART_RXIndex = 0; char TX_Str1[] = "Hi from Synwit\r\n"; char TX_Str2[] = "SWM320 LIN Demo\r\n"; void LIN_Init(void); void LIN_FrameSend(char data[], uint32_t count); void lin_test(int i) { if((i%1000)==0) { LIN_FrameSend(TX_Str1, sizeof(TX_Str1)); } if((i%1000)==499) { LIN_FrameSend(TX_Str2, sizeof(TX_Str2)); } } void LIN_Init(void) { UART_InitStructure UART_initStruct; GPIO_Init(GPIOD, PIN9, 1, 0, 0, 0); PORT_Init(PORTD, PIN4, PORTD_PIN4_UART1_RX, 1); //GPIOD.4 配置为 UART1 输入引脚 PORT_Init(PORTD, PIN3, PORTD_PIN3_UART1_TX, 0); //GPIOD.3 配置为 UART1 输出引脚 GPIO_Init(GPIOC, PIN4, 1, 0, 0, 0); //GPIOC.4 配置为输出引脚,推挽输出 GPIO_SetBit(GPIOD, PIN4); UART_initStruct.Baudrate = 9600; UART_initStruct.DataBits = UART_DATA_8BIT; UART_initStruct.Parity = UART_PARITY_NONE; UART_initStruct.StopBits = UART_STOP_1BIT; UART_initStruct.RXThreshold = 7; UART_initStruct.RXThresholdIEn = 1; UART_initStruct.TXThreshold = 7; UART_initStruct.TXThresholdIEn = 0; UART_initStruct.TimeoutTime = 20; UART_initStruct.TimeoutIEn = 1; UART_Init(UART1, &UART_initStruct); UART_LINConfig(UART1, 13, 0, 14, 0);// 发送 // UART_LINConfig(UART0, 13, 1, 14, 0);// 接收 UART_Open(UART1); } void LIN_FrameSend(char data[], uint32_t count) { uint32_t i; UART_LINGenerate(UART1); while(UART1->LINCR & UART_LINCR_GENBRK_Msk) __NOP(); //完成自动清零 for(i = 0; i < count; i++) { while(UART_IsTXFIFOFull(UART1)) __NOP(); UART_WriteByte(UART1, data[i]); } } void UART1_Handler(void) { uint32_t chr; if(UART_INTStat(UART1, UART_IT_RX_THR | UART_IT_RX_TOUT)) { while(UART_IsRXFIFOEmpty(UART1) == 0) { if(UART_ReadByte(UART1, &chr) == 0) { if(UART_RXIndex < UART_RX_LEN) { UART_RXBuffer[UART_RXIndex] = chr; UART_RXIndex++; } } } if(UART_INTStat(UART1, UART_IT_RX_TOUT)) { UART_INTClr(UART1, UART_IT_RX_TOUT); } } else if(UART_LINIsDetected(UART1)) { UART1->LINCR |= UART_LINCR_BRKDETIF_Msk; //清除中断标志 GPIO_InvBit(GPIOD, PIN9); if(UART_RXIndex) { printf("LIN frame data:\r\n %s\r\n", UART_RXBuffer); memset(UART_RXBuffer, 0, sizeof(UART_RXBuffer)); UART_RXIndex = 0; } } } #endif /* */