
本帖最后由 浪迹天涯123 于 2017-12-26 20:21 编辑 简介:
总线电平=CAN_H的电压-CAN_L的电压 显性电平对应逻辑0=总线电平为2V左右 隐性电平对应逻辑0=总线电平为0V 显性电平具有优先权,只要有一个单元输出显性电平,总线上即为显性电平。而隐形电平则具有包容的意味,只有所有的单元都输出隐性电平,总线上才为隐性电平(显性电平比隐性电平更强)。另外,在CAN总线的起止端都有一个120Ω的终端电阻,来做阻抗匹配,以减少回波反射。 帧种类介绍:[td]
注:其中,数据帧和遥控帧有标准格式和扩展格式两种格式。 STM32 CAN控制器简介:标准格式有11 个位的标识符(ID),扩展格式有29 个位的ID 。
3种模式: 工作模式:
测试模式:
调试模式(不常用) STM32 CAN筛选器位宽和模式配置:CAN的标识符不表示目的地址而是表示发送优先级,接收节点根据标识符的值,来决定是否接收对应消息。 标识符屏蔽模式:过滤一组标识符 标识符列表模式:过滤一个标识符 例:设置筛选器组0工作在:1个32位筛选器-标识符屏蔽模式,然后设置CAN_F0R1=0XFFFF0000,CAN_F0R2=0XFF00FF00。其中存放到CAN_F0R1的值就是期望收到的ID,即(STID+EXTID+IDE+RTR)最好是:0XFFFF0000。而0XFF00FF00就是设置我们需要必须关心的ID,表示收到的映像,其位[31:24]和位[15:8]这16个位的必须和CAN_F0R1中对应的位一模一样,而另外的16个位则不关心,可以一样,也可以不一样,都认为是正确的ID,即收到的映像必须是0XFFxx00xx,才算是正确的(x表示不关心)。 波特率设置:寄存器: CAN主控制寄存器(CAN_MCR) 该寄存器的我们仅介绍下INRQ位,该位用来控制初始化请求。 设置INRQ=0,可使CAN从初始化模式进入正常工作模式。 设置INRQ=1,可使CAN从正常工作模式进入初始化模式。 CAN初始化时,先设置INRQ=1,进入初始化模式,进行初始化(尤其是CAN_BTR的设置,该寄存器,必须在CAN正常工作之前设置),之后再设置INRQ=0,进入正常工作模式。 CAN位时序寄存器(CAN_BTR)设置波特率 配置步骤:CAN接收FIFO寄存器(CAN_RF0R/CAN_RF1R) CAN发送邮箱标识符寄存器(CAN_TIxR)(x=0~2) CAN发送邮箱数据长度和时间戳寄存器 (CAN_TDTxR) (x=0~2) 低4位用于设置发送多少个字节,最多为8个字节 CAN发送邮箱数据寄存器(CAN_TDLxR/CAN_TDHxR) (x=0~2) CAN接收FIFO邮箱标识符寄存器(CAN_RIxR)(x=0/1) CAN接收FIFO邮箱数据长度和时间戳寄存器(CAN_RDTxR) (x=0/1) CAN接收FIFO邮箱邮箱数据寄存器(CAN_RDLxR/CAN_RDHxR) (x=0/1) CAN筛选器模式寄存器(CAN_FM1R)(0标识符屏蔽,1标识符列表) CAN筛选器尺度寄存器(CAN_FS1R)(0双16位,1单32位) CAN筛选器FIFO关联寄存器(CAN_FFA1R)(0筛选器分到FIFO0,1筛选器分到FIFO1) CAN筛选器激活寄存器(CAN_FA1R)(0未激活,1激活) CAN筛选器组i寄存器x(CAN_FiRx)(i=0~27,x=1/2)(F103筛选器只有14个) ①配置相关引脚的 复用功能,使能CAN时钟。
②设置CAN工作模式及波特率等。
③设置滤波器。 CODE://can.c #include "can.h" #include "led.h" #include "delay.h" #include "usart.h" //CAN初始化 //tsjw:重新同步跳跃时间单元.范围:CAN_SJW_1tq~ CAN_SJW_4tq //tbs2:时间段2的时间单元. 范围:CAN_BS2_1tq~CAN_BS2_8tq; //tbs1:时间段1的时间单元. 范围:CAN_BS1_1tq ~CAN_BS1_16tq //brp :波特率分频器.范围:1~1024; tq=(brp)*tpclk1 //波特率=Fpclk1/((tbs1+1+tbs2+1+1)*brp); //mode:CAN_Mode_Normal,普通模式;CAN_Mode_LoopBack,回环模式; //Fpclk1的时钟在初始化的时候设置为36M,如果设置CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_LoopBack); //则波特率为:36M/((8+9+1)*4)=500Kbps //返回值:0,初始化OK; // 其他,初始化失败; u8 CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode) { GPIO_InitTypeDef GPIO_InitStructure; CAN_InitTypeDef CAN_InitStructure; CAN_FilterInitTypeDef CAN_FilterInitStructure; #if CAN_RX0_INT_ENABLE NVIC_InitTypeDef NVIC_InitStructure; #endif RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PORTA时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); //使能CAN1时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽 GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化IO GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入 GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化IO //CAN单元设置 CAN_InitStructure.CAN_TTCM=DISABLE; //非时间触发通信模式 CAN_InitStructure.CAN_ABOM=DISABLE; //软件自动离线管理 CAN_InitStructure.CAN_AWUM=DISABLE; //睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位) CAN_InitStructure.CAN_NART=ENABLE; //禁止报文自动传送 CAN_InitStructure.CAN_RFLM=DISABLE; //报文不锁定,新的覆盖旧的 CAN_InitStructure.CAN_TXFP=DISABLE; //优先级由报文标识符决定 CAN_InitStructure.CAN_Mode= mode; //模式设置: mode:0,普通模式;1,回环模式; //设置波特率 CAN_InitStructure.CAN_SJW=tsjw; //重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1tq CAN_SJW_2tq CAN_SJW_3tq CAN_SJW_4tq CAN_InitStructure.CAN_BS1=tbs1; //Tbs1=tbs1+1个时间单位CAN_BS1_1tq ~CAN_BS1_16tq CAN_InitStructure.CAN_BS2=tbs2; //Tbs2=tbs2+1个时间单位CAN_BS2_1tq ~ CAN_BS2_8tq CAN_InitStructure.CAN_Prescaler=brp; //分频系数(Fdiv)为brp+1 CAN_Init(CAN1, &CAN_InitStructure); //初始化CAN1 CAN_FilterInitStructure.CAN_FilterNumber=0; //过滤器0 CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; //屏蔽位模式 CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //32位宽 CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000; //32位ID CAN_FilterInitStructure.CAN_FilterIdLow=0x0000; CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;//32位MASK CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000; CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;//过滤器0关联到FIFO0 CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;//激活过滤器0 CAN_FilterInit(&CAN_FilterInitStructure); //滤波器初始化 #if CAN_RX0_INT_ENABLE CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE); //FIFO0消息挂号中断允许. NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 主优先级为1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 次优先级为0 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); #endif return 0; } #if CAN_RX0_INT_ENABLE //使能RX0中断 //中断服务函数 void USB_LP_CAN1_RX0_IRQHandler(void) { CanRxMsg RxMessage; int i=0; CAN_Receive(CAN1, 0, &RxMessage); for(i=0;i<8;i++) printf("rxbuf[%d]:%d\r\n",i,RxMessage.Data); } #endif //can发送一组数据(固定格式:ID为0X12,标准帧,数据帧) //len:数据长度(最大为8) //msg:数据指针,最大为8个字节. //返回值:0,成功; // 其他,失败; u8 Can_Send_Msg(u8* msg,u8 len) { u8 mbox; u16 i=0; CanTxMsg TxMessage; TxMessage.StdId=0x12; // 标准标识符 TxMessage.ExtId=0x12; // 设置扩展标示符 TxMessage.IDE=CAN_Id_Standard; // 标准帧 TxMessage.RTR=CAN_RTR_Data; // 数据帧 TxMessage.DLC=len; // 要发送的数据长度 for(i=0;i<len;i++) TxMessage.Data=msg; mbox= CAN_Transmit(CAN1, &TxMessage); i=0; while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++; //等待发送结束 if(i>=0XFFF)return 1; return 0; } //can口接收数据查询 //buf:数据缓存区; //返回值:0,无数据被收到; // 其他,接收的数据长度; u8 Can_Receive_Msg(u8 *buf) { u32 i; CanRxMsg RxMessage; if( CAN_MessagePending(CAN1,CAN_FIFO0)==0)return 0; //没有接收到数据,直接退出 CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);//读取数据 for(i=0;i<8;i++) buf=RxMessage.Data; return RxMessage.DLC; } main.c #include "led.h" #include "delay.h" #include "key.h" #include "sys.h" #include "lcd.h" #include "usart.h" #include "can.h" int main(void) { u8 key; u8 i=0,t=0; u8 cnt=0; u8 canbuf[8]; u8 res; u8 mode=CAN_Mode_LoopBack;//CAN工作模式;CAN_Mode_Normal(0):普通模式,CAN_Mode_LoopBack(1):环回模式 delay_init(); //延时函数初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级 uart_init(115200); //串口初始化为115200 LED_Init(); //初始化与LED连接的硬件接口 LCD_Init(); //初始化LCD KEY_Init(); //按键初始化 CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_LoopBack);//CAN初始化环回模式,波特率500Kbps POINT_COLOR=RED;//设置字体为红色 LCD_ShowString(60,50,200,16,16,"WarShip STM32"); LCD_ShowString(60,70,200,16,16,"CAN TEST"); LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK"); LCD_ShowString(60,110,200,16,16,"2015/1/15"); LCD_ShowString(60,130,200,16,16,"LoopBack Mode"); LCD_ShowString(60,150,200,16,16,"KEY0:Send WK_UP:Mode");//显示提示信息 POINT_COLOR=BLUE;//设置字体为蓝色 LCD_ShowString(60,170,200,16,16,"Count:"); //显示当前计数值 LCD_ShowString(60,190,200,16,16,"Send Data:"); //提示发送的数据 LCD_ShowString(60,250,200,16,16,"Receive Data:"); //提示接收到的数据 while(1) { key=KEY_Scan(0); if(key==KEY0_PRES)//KEY0按下,发送一次数据 { for(i=0;i<8;i++) { canbuf=cnt+i;//填充发送缓冲区 if(i<4)LCD_ShowxNum(60+i*32,210,canbuf,3,16,0X80); //显示数据 else LCD_ShowxNum(60+(i-4)*32,230,canbuf,3,16,0X80); //显示数据 } res=Can_Send_Msg(canbuf,8);//发送8个字节 if(res)LCD_ShowString(60+80,190,200,16,16,"Failed"); //提示发送失败 else LCD_ShowString(60+80,190,200,16,16,"OK "); //提示发送成功 }else if(key==WKUP_PRES)//WK_UP按下,改变CAN的工作模式 { mode=!mode; CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,mode);//CAN普通模式初始化, 波特率500Kbps POINT_COLOR=RED;//设置字体为红色 if(mode==0)//普通模式,需要2个开发板 { LCD_ShowString(60,130,200,16,16,"Nnormal Mode "); }else //回环模式,一个开发板就可以测试了. { LCD_ShowString(60,130,200,16,16,"LoopBack Mode"); } POINT_COLOR=BLUE;//设置字体为蓝色 } key=Can_Receive_Msg(canbuf); if(key)//接收到有数据 { LCD_Fill(60,270,130,310,WHITE);//清除之前的显示 for(i=0;i<key;i++) { if(i<4)LCD_ShowxNum(60+i*32,270,canbuf,3,16,0X80); //显示数据 else LCD_ShowxNum(60+(i-4)*32,290,canbuf,3,16,0X80); //显示数据 } } t++; delay_ms(10); if(t==20) { LED0=!LED0;//提示系统正在运行 t=0; cnt++; LCD_ShowxNum(60+48,170,cnt,3,16,0X80); //显示数据 } } } |
![]() |