一.MCU
此项目MCU使用的是芯圣的一款32位单片机,内部自带CAN外设。
项目需要实现的功能是:通过诸多外部按键,LCD屏会显示测试内容,例如速度 、档位、故障码,同时通过数据发送端口(485或CAN)发送相应数据到电动车仪表,实现测试功能。
LCD段码屏的驱动我之前有过说明,具体参考LCD段码式液晶屏驱动-CSDN博客
二.485通讯
485通讯说白了就是串口通信,区别在于串口极其容易受干扰且传输距离近,MCU出来的TX,RX经过485芯片强化升级后变为抗干扰能力强的差分信号。
区别就在于串口是全双工的,可以同时收发,而485是半双工的,同一时间只能收或者发。当然氪金大佬可以选择全双工的485芯片,也可以通过增加个自动收发电路来实现。但是那个电路我没有实际使用过,我的老大说可以用,那就应该没问题。可以参考这位大佬的博客:终于讲透了,史上最详细的RS485自动收发电路你一定要掌握-CSDN博客
接下来我们看个常用的:
硬件连接,485RX、TX接MCU串口TX、RX 485EN接任意IO口,用来切换485收发模式
软件部分 CAN收发:
void RS485_Config() //注意手动切换485收发模式
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
//USART2 TX as PA2
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//USART2 Rx as PA3
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//485控制管脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 9600;//波特率9600
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx |USART_Mode_Rx;
USART_Init(USART2, &USART_InitStructure);
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启串口接受中断
// USART_ITConfig(USART2, USART_IT_TXE, ENABLE); //开启串口发送中断
USART_Cmd(USART2, ENABLE);
//控制485芯片进入接收模式
GPIO_ResetBits(GPIOA,GPIO_Pin_4);
}
void RS485_Send_Data(u8 *buf,u8 len) //发送函数,注意发送完改为接收
{
u8 t;
GPIO_SetBits(GPIOA,GPIO_Pin_4);//设置为发送模式
for(t=0;t<len;t++)
{
while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);//等待发送完成
USART_SendData(USART2,buf[t]);
}
while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET)
;
USART2_RX_Data_Len=0;
GPIO_ResetBits(GPIOA,GPIO_Pin_4);//设置为接收模式
}
void USART2_IRQHandler(void)//中断接收
{
u8 res;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收到数据
{
res =USART_ReceiveData(USART2); //读取接收到的数据
if(USART2_RX_Data_Len<USART2_RX_LEN)
{
USART2_RX_Buf[USART2_RX_Data_Len]=res; //记录接收到的值
USART2_RX_Data_Len++; //接收数据增加 1
}
USART2_RX_Flag=1;
}
}
三.CAN通讯
CAN通讯我只在车子的仪表上用过,当时速率是500K或者是250K。
硬件连接:
R32更具实际情况接,我是用了120欧,目的为了让总线寄生电容快速放电,确保总线快速进入隐性状态。
软件代码:
void CAN_Configuration(void)
{
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
/* CAN register init */
CAN_DeInit(CAN1);
CAN_StructInit(&CAN_InitStructure);
CAN_InitStructure.CAN_Mode =CAN_Mode_Normal; //工作模式(正常、静默、环回和环回静默)
CAN_InitStructure.CAN_Prescaler =9; //500K
CAN_InitStructure.CAN_BS1=CAN_BS1_5tq; //BTR-TS1 时间段1 占用了5个时间单元
CAN_InitStructure.CAN_BS2=CAN_BS2_2tq; //BTR-TS2 时间段2 占用了2个时间单元
CAN_InitStructure.CAN_SJW=CAN_SJW_1tq; //BTR-SJW 重新同步跳跃宽度 2个时间单元
CAN_InitStructure.CAN_NART=DISABLE; //自动重传 DISABLE-自动重传 ENABLE-不自动重传
CAN_InitStructure.CAN_TXFP=ENABLE; //发送邮箱优先级 ENABLE-先请求先发送 DISABLE-ID号小的先发送
CAN_InitStructure.CAN_RFLM=DISABLE; //报文锁定 ENABLE-FIFO溢出,新报文丢弃 DISABLE-新报文覆盖老报文
CAN_InitStructure.CAN_AWUM=ENABLE; //自动唤醒 ENABLE-自动唤醒 DISABLE-手动唤醒
CAN_InitStructure.CAN_TTCM=DISABLE; //时间触发通信模式 ENABLE-开启 DISABLE-关闭
CAN_InitStructure.CAN_ABOM=DISABLE; //离线自动恢复 ENABLE-自动恢复 DISABLE-手动恢复
CAN_Init(CAN1, &CAN_InitStructure);
//CAN过滤器初始化0
CAN_FilterInitStructure.CAN_FilterNumber=0; //过滤器0
CAN_FilterInitStructure.CAN_FilterIdHigh= 0x101<<5; //要筛选的ID高位
CAN_FilterInitStructure.CAN_FilterIdLow= 0; //要筛选的ID低位
CAN_FilterInitStructure.CAN_FilterMaskIdHigh= 0xFFFF; //筛选器高16位每位必须匹配
CAN_FilterInitStructure.CAN_FilterMaskIdLow= 0; //筛选器低16位每位必须匹配
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //选择过滤器位宽
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; //过滤器模式 屏蔽模式
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0; //配置过滤器关联 FIFO0和FIFO1
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //过滤器开关
CAN_FilterInit(&CAN_FilterInitStructure);
CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE); //FIF0消息挂号中断允许
}
void CAN_Send()
{
if(CANTX_100ms_F) CANTX_State=1;
if(CANTX_250ms_F) CANTX_State=2;
if(CANTX_500ms_F) CANTX_State=3;
if(CANTX_1000ms_F) CANTX_State=4;
if(CanTx_Idle==0)return; //处于发送状态,返回
if(CANTX_100ms_F||CANTX_250ms_F||CANTX_500ms_F||CANTX_1000ms_F)
{
switch(CANTX_State)
{
case 1:
{
TX_100ms_Cnt++;
if(TX_100ms_Cnt==1)
{
CAN_SetMsg(&TxMessage,0x18203120,Data_ID_18203120);
break;
}
if(TX_100ms_Cnt==2)
{
CAN_SetMsg(&TxMessage,0x18300A30,Data_ID_18300A30);
TX_100ms_Cnt=0;
CANTX_100ms_F=0;
break;
}
}
break;
case 2:
{
TX_250ms_Cnt++;
if(TX_250ms_Cnt==1)
{
CAN_SetMsg(&TxMessage,0x18200A20,Data_ID_18200A20);
break;
}
if(TX_250ms_Cnt==2)
{
CAN_SetMsg(&TxMessage,0x18202A20,Data_ID_18202A20);
break;
}
if(TX_250ms_Cnt==3)
{
CAN_SetMsg(&TxMessage,0x18202B20,Data_ID_18202B20);
break;
}
if(TX_250ms_Cnt==4)
{
CAN_SetMsg(&TxMessage,0x18203220,Data_ID_18203220);
break;
}
if(TX_250ms_Cnt==5)
{
CAN_SetMsg(&TxMessage,0x18300630,Data_ID_18300630);
break;
}
if(TX_250ms_Cnt==6)
{
CAN_SetMsg(&TxMessage,0x18300830,Data_ID_18300830);
break;
}
if(TX_250ms_Cnt==7)
{
CAN_SetMsg(&TxMessage,0x18300930,Data_ID_18300930);
break;
}
if(TX_250ms_Cnt==8)
{
CAN_SetMsg(&TxMessage,0x18300B30,Data_ID_18300B30);
break;
}
if(TX_250ms_Cnt==9)
{
CAN_SetMsg(&TxMessage,0x18300C30,Data_ID_18300C30);
break;
}
if(TX_250ms_Cnt==10)
{
CAN_SetMsg(&TxMessage,0x18200A21,Data_ID_18200A21);
break;
}
if(TX_250ms_Cnt==11)
{
CAN_SetMsg(&TxMessage,0x18202A21,Data_ID_18202A21);
break;
}
if(TX_250ms_Cnt==12)
{
CAN_SetMsg(&TxMessage,0x18202B21,Data_ID_18202B21);
break;
}
if(TX_250ms_Cnt==13)
{
CAN_SetMsg(&TxMessage,0x18100710,Data_ID_18100710);
break;
}
if(TX_250ms_Cnt==14)
{
CAN_SetMsg(&TxMessage,0x18202920,Data_ID_18202920);
break;
}
if(TX_250ms_Cnt==15)
{
CAN_SetMsg(&TxMessage,0x18202921,Data_ID_18202921);
break;
}
if(TX_250ms_Cnt==16)
{
CAN_SetMsg(&TxMessage,0x18203820,Data_ID_18203820);
TX_250ms_Cnt=0;
CANTX_250ms_F=0;
break;
}
}
break;
case 3:
{
TX_500ms_Cnt++;
if(TX_500ms_Cnt==1)
{
CAN_SetMsg(&TxMessage,0x18300E3F,Data_ID_18300E3F);
break;
}
if(TX_500ms_Cnt==2)
{
CAN_SetMsg(&TxMessage,0x18100C10,Data_ID_18100C10);
TX_500ms_Cnt=0;
CANTX_500ms_F=0;
break;
}
}
break;
case 4:
{
CAN_SetMsg(&TxMessage,0x18500550,Data_ID_18500550);
CANTX_1000ms_F=0;
}
break;
default:
break;
}
CAN_Transmit(CAN1, &TxMessage);
CAN_ITConfig(CAN1,CAN_IT_TME, ENABLE); //开传输邮箱空中断,传输邮箱空则代表数据传输完毕
CanTx_Idle=0;
}
void USB_HP_CAN1_TX_IRQHandler(void)//数据传输完毕进入到这个中断来
{
CAN_ClearITPendingBit(CAN1, CAN_IT_TME);//清除中断挂起标志位
CanTx_Idle=0xff;//发送处于空闲状态,表示可以再次发送
}
void CAN_SetMsg(CanTxMsg *TxMessage,uint32_t ExtId_Data,uint8_t *TxData)//扩展帧,数据
{
uint8_t ubCounter = 0;
TxMessage->ExtId =ExtId_Data; //需要发送的扩展ID号
TxMessage->IDE=CAN_ID_EXT; //扩展帧
TxMessage->RTR=CAN_RTR_DATA; //数据帧
TxMessage->DLC=8;
for (ubCounter = 0; ubCounter < 8; ubCounter++)
{
TxMessage->Data[ubCounter] = *(TxData+ubCounter);
}
}
void USB_LP_CAN1_RX0_IRQHandler(void) //CAN接收中断
{
// /*从邮箱中读出报文*/
// CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
// if((RxMessage.IDE==CAN_ID_EXT)&&(RxMessage.DLC==8))
// {
// canRxDataBuf.can_msg_id=RxMessage.ExtId;
// memcpy(canRxDataBuf.can_msg_data,RxMessage.Data,8);
// }
}
此项目的DC-DC 以及LDO电路这里不做描述,后续会单独起个电源电路的记录。