
本帖最后由 浪迹天涯123 于 2017-12-26 20:21 编辑 p7 K0 r O8 L( `7 \9 ` 简介:
总线电平=CAN_H的电压-CAN_L的电压 # k9 s* f+ o+ D" O 显性电平对应逻辑0=总线电平为2V左右 隐性电平对应逻辑0=总线电平为0V 显性电平具有优先权,只要有一个单元输出显性电平,总线上即为显性电平。而隐形电平则具有包容的意味,只有所有的单元都输出隐性电平,总线上才为隐性电平(显性电平比隐性电平更强)。另外,在CAN总线的起止端都有一个120Ω的终端电阻,来做阻抗匹配,以减少回波反射。 帧种类介绍:[td]
注:其中,数据帧和遥控帧有标准格式和扩展格式两种格式。 STM32 CAN控制器简介:标准格式有11 个位的标识符(ID),扩展格式有29 个位的ID 。
3种模式: 工作模式:
测试模式:
调试模式(不常用) STM32 CAN筛选器位宽和模式配置:CAN的标识符不表示目的地址而是表示发送优先级,接收节点根据标识符的值,来决定是否接收对应消息。 标识符屏蔽模式:过滤一组标识符 % w. G0 F3 D& |% q @+ ?5 t8 r 标识符列表模式:过滤一个标识符 例:设置筛选器组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从初始化模式进入正常工作模式。 ! ]. k" l, C7 z. T A 设置INRQ=1,可使CAN从正常工作模式进入初始化模式。 CAN初始化时,先设置INRQ=1,进入初始化模式,进行初始化(尤其是CAN_BTR的设置,该寄存器,必须在CAN正常工作之前设置),之后再设置INRQ=0,进入正常工作模式。 CAN位时序寄存器(CAN_BTR)设置波特率 配置步骤:CAN接收FIFO寄存器(CAN_RF0R/CAN_RF1R) 9 P; P; v8 X9 a' r1 z$ F1 O CAN发送邮箱标识符寄存器(CAN_TIxR)(x=0~2) ) [; v* p, {5 \- P' m# k4 i+ N: d CAN发送邮箱数据长度和时间戳寄存器 (CAN_TDTxR) (x=0~2) 低4位用于设置发送多少个字节,最多为8个字节 CAN发送邮箱数据寄存器(CAN_TDLxR/CAN_TDHxR) (x=0~2) CAN接收FIFO邮箱标识符寄存器(CAN_RIxR)(x=0/1) * t3 u# R3 |; v6 j1 s2 I* X CAN接收FIFO邮箱数据长度和时间戳寄存器(CAN_RDTxR) (x=0/1) 9 X; F t( e% [5 C, O( L/ u CAN接收FIFO邮箱邮箱数据寄存器(CAN_RDLxR/CAN_RDHxR) (x=0/1) CAN筛选器模式寄存器(CAN_FM1R)(0标识符屏蔽,1标识符列表) 3 [% b4 b+ z. E& w$ P/ Q3 L CAN筛选器尺度寄存器(CAN_FS1R)(0双16位,1单32位) CAN筛选器FIFO关联寄存器(CAN_FFA1R)(0筛选器分到FIFO0,1筛选器分到FIFO1) " x2 t2 ]5 u; L. J6 V CAN筛选器激活寄存器(CAN_FA1R)(0未激活,1激活) . W, X3 X) K3 K$ a% h8 Z CAN筛选器组i寄存器x(CAN_FiRx)(i=0~27,x=1/2)(F103筛选器只有14个) ①配置相关引脚的 复用功能,使能CAN时钟。
②设置CAN工作模式及波特率等。
③设置滤波器。 CODE://can.c. S$ E2 l! b5 u9 W3 f& V #include "can.h"" ?! G9 k$ ?: Z, f( @& w$ w #include "led.h" #include "delay.h"( V: B! A+ D6 E& E! Q #include "usart.h" ) u- O" C- y l/ _5 k/ o8 S - F& h0 L$ @, ` f4 T3 u" _1 G& ` //CAN初始化 //tsjw:重新同步跳跃时间单元.范围:CAN_SJW_1tq~ CAN_SJW_4tq6 W, j6 Q$ Y5 j8 a8 a //tbs2:时间段2的时间单元. 范围:CAN_BS2_1tq~CAN_BS2_8tq; //tbs1:时间段1的时间单元. 范围:CAN_BS1_1tq ~CAN_BS1_16tq% q) M5 ]0 M# B% Q9 m6 \- O) { //brp :波特率分频器.范围:1~1024; tq=(brp)*tpclk1 //波特率=Fpclk1/((tbs1+1+tbs2+1+1)*brp);, }3 d. s) [+ Z5 g' P //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);3 S0 W' d$ G% S- }7 e; x5 L5 M2 ~( ? //则波特率为:36M/((8+9+1)*4)=500Kbps //返回值:0,初始化OK; // 其他,初始化失败; u8 CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode) { ; z! K" \' y* w0 Z/ _ GPIO_InitTypeDef GPIO_InitStructure; CAN_InitTypeDef CAN_InitStructure;+ ?' U3 D9 X2 D2 r" ^. o d CAN_FilterInitTypeDef CAN_FilterInitStructure;! d4 D! ]. q% G# L8 K! m #if CAN_RX0_INT_ENABLE NVIC_InitTypeDef NVIC_InitStructure; #endif RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PORTA时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); //使能CAN1时钟 , W4 q& c7 s! h# f/ m7 T$ h( s GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;6 D* ?. Q. r# s J7 L GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽4 k! h% q' z2 y, i6 { GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化IO GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;) V6 _: c0 Y2 J d9 W" p GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入 GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化IO0 V4 j! m! p& V9 X //CAN单元设置 CAN_InitStructure.CAN_TTCM=DISABLE; //非时间触发通信模式 CAN_InitStructure.CAN_ABOM=DISABLE; //软件自动离线管理 0 T6 k( l# S- u Z& l( i: | CAN_InitStructure.CAN_AWUM=DISABLE; //睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)# \1 v( h* d z+ x- \/ T% T! O p6 s CAN_InitStructure.CAN_NART=ENABLE; //禁止报文自动传送 CAN_InitStructure.CAN_RFLM=DISABLE; //报文不锁定,新的覆盖旧的 3 R# m5 V; L! V CAN_InitStructure.CAN_TXFP=DISABLE; //优先级由报文标识符决定 CAN_InitStructure.CAN_Mode= mode; //模式设置: mode:0,普通模式;1,回环模式; * V: g2 N# {1 ?) F" I //设置波特率 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_16tq0 K7 U& k- T$ w& t' [- H" k CAN_InitStructure.CAN_BS2=tbs2; //Tbs2=tbs2+1个时间单位CAN_BS2_1tq ~ CAN_BS2_8tq q' J( J& @& [# x& F; _ CAN_InitStructure.CAN_Prescaler=brp; //分频系数(Fdiv)为brp+1 ! O2 C! ~" ~: }2 ~+ r) d" w CAN_Init(CAN1, &CAN_InitStructure); //初始化CAN1 5 u; f4 ]& r' K3 F CAN_FilterInitStructure.CAN_FilterNumber=0; //过滤器0" j8 t' M( i# c/ G+ x" H CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; //屏蔽位模式 CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //32位宽 6 O7 }" p* i ? CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000; //32位ID9 G) ~1 K# d/ N CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;/ a3 f* p, _% U( O CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;//32位MASK4 @1 @2 C5 U/ i n/ j CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000; CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;//过滤器0关联到FIFO0 CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;//激活过滤器03 u* F7 F2 c' ] s CAN_FilterInit(&CAN_FilterInitStructure); //滤波器初始化4 U: y, c# A6 I1 N5 ^& \0 n0 o; | #if CAN_RX0_INT_ENABLE CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE); //FIFO0消息挂号中断允许. ( |4 c) Q! ^: i6 w- M" h3 O, Q" Q* y 7 V) [0 O3 Q2 ? NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 主优先级为1! _5 E" i' L3 E @ NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 次优先级为08 _8 L0 y6 `2 [4 \5 j2 e/ @ NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); #endif return 0;- c7 X N( f8 z9 K: d$ U# [ } 2 K; h& d: ^ S# e. R8 r8 \ }# c #if CAN_RX0_INT_ENABLE //使能RX0中断 //中断服务函数 void USB_LP_CAN1_RX0_IRQHandler(void)" T- i: M( ] Y { CanRxMsg RxMessage; int i=0; CAN_Receive(CAN1, 0, &RxMessage);, q9 j9 k0 u7 W5 k for(i=0;i<8;i++) printf("rxbuf[%d]:%d\r\n",i,RxMessage.Data);. \' J" {7 b& q. C } #endif& `7 w: d: J' ^5 E1 u" h( r- \ //can发送一组数据(固定格式:ID为0X12,标准帧,数据帧) / y1 r( t: g. f q/ R //len:数据长度(最大为8) //msg:数据指针,最大为8个字节.& C( {+ R) H6 D: O* B& \9 b //返回值:0,成功;* {& s: J" D$ Q. W, l // 其他,失败; u8 Can_Send_Msg(u8* msg,u8 len) { u8 mbox;- B7 E+ F- K" S% a0 b) J u16 i=0; CanTxMsg TxMessage;9 Z: b) X# N( f8 k' F# a TxMessage.StdId=0x12; // 标准标识符 # L0 t) U+ _" y5 B1 E TxMessage.ExtId=0x12; // 设置扩展标示符 TxMessage.IDE=CAN_Id_Standard; // 标准帧4 M' x# f6 E) s, s* Q' p TxMessage.RTR=CAN_RTR_Data; // 数据帧; @2 @# x& D. ` TxMessage.DLC=len; // 要发送的数据长度1 p0 w) b% F$ o5 L% Z for(i=0;i<len;i++) TxMessage.Data=msg; mbox= CAN_Transmit(CAN1, &TxMessage); ^6 h" r1 P3 J) [: o7 ]8 c i=0; while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++; //等待发送结束, O& ]+ s1 {3 p: t! n7 I if(i>=0XFFF)return 1;3 E( [+ ?/ ^# Q j3 J, j return 0; ) ]2 p$ A2 I A! M } //can口接收数据查询, s* u; A) d2 I) X4 f //buf:数据缓存区; //返回值:0,无数据被收到; // 其他,接收的数据长度; u8 Can_Receive_Msg(u8 *buf)9 s0 D" H! o/ b! }5 p+ C- }2 C { 0 H. ]8 j, u1 ]6 p. g: L& @2 } u32 i;5 {+ P$ W/ y: n2 F( {: w3 G! _8 S CanRxMsg RxMessage; if( CAN_MessagePending(CAN1,CAN_FIFO0)==0)return 0; //没有接收到数据,直接退出 CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);//读取数据 / ^3 B* v' o, k7 K for(i=0;i<8;i++) buf=RxMessage.Data; return RxMessage.DLC; }. M, K8 H: m) y& v3 ` * q- b, ^9 t9 V. \% R c4 C; [9 j main.c % d" H# R3 i! j# g9 e1 K #include "led.h"! H* b/ }; _9 ^. @# R# e% B #include "delay.h" #include "key.h" #include "sys.h". k5 K0 g1 |) E( v0 u* q #include "lcd.h" #include "usart.h" # v2 T" t: H: B) x' k& o2 H #include "can.h" 2 g: J7 A4 Z- N! ~ int main(void)& N% e1 G4 b3 @# ]( g { u8 key;* w( e8 i; v+ j: P7 N8 N9 t3 h5 {1 i u8 i=0,t=0;9 C! o2 c; D5 q u8 cnt=0; u8 canbuf[8]; u8 res;- P) @3 L |+ s% f% f+ T u8 mode=CAN_Mode_LoopBack;//CAN工作模式;CAN_Mode_Normal(0):普通模式,CAN_Mode_LoopBack(1):环回模式 " ]% h4 d# d7 M1 U @, F; X. M delay_init(); //延时函数初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级9 v7 I4 p9 ~1 b& W; N: v+ L 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 - q8 z9 c; ~# ]6 _ Y, l# y POINT_COLOR=RED;//设置字体为红色 LCD_ShowString(60,50,200,16,16,"WarShip STM32"); 8 q1 s* `9 L/ C/ x Q LCD_ShowString(60,70,200,16,16,"CAN TEST"); : p: R9 _/ t3 l 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;//设置字体为蓝色 # w" h* Q2 Z3 b" f4 I, I LCD_ShowString(60,170,200,16,16,"Count:"); //显示当前计数值 % s- Q3 X/ w- V2 t+ T LCD_ShowString(60,190,200,16,16,"Send Data:"); //提示发送的数据 LCD_ShowString(60,250,200,16,16,"Receive Data:"); //提示接收到的数据 ( Q, o4 C' m6 ~: M( ^ y F* O7 }2 L while(1)# Z* }. ], F- [7 w {3 v! Z/ H0 A0 \! u key=KEY_Scan(0);4 i. }' `8 p2 }6 I if(key==KEY0_PRES)//KEY0按下,发送一次数据 {1 n( E( r& t& d& C# v: X for(i=0;i<8;i++) {/ e& ^- l! D- Z& N canbuf=cnt+i;//填充发送缓冲区1 z7 ^ W5 H+ C3 E7 D2 F# n if(i<4)LCD_ShowxNum(60+i*32,210,canbuf,3,16,0X80); //显示数据1 C/ e* a: W: J3 H, T else LCD_ShowxNum(60+(i-4)*32,230,canbuf,3,16,0X80); //显示数据, h. c- W' e) Q& v1 Q1 u; W5 Z9 A } 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的工作模式 { ' l$ m0 a; }0 W) i# ~ 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个开发板2 [' a% S+ |" n) I2 s- P {6 k" W& [9 u$ {, u- [4 h LCD_ShowString(60,130,200,16,16,"Nnormal Mode "); : I, b% K8 s9 r6 E }else //回环模式,一个开发板就可以测试了.# `, r }% E2 y! x7 u* \) |9 I {' ^6 r$ {8 n' i LCD_ShowString(60,130,200,16,16,"LoopBack Mode"); } POINT_COLOR=BLUE;//设置字体为蓝色 + k: \% q( l3 p9 Z } key=Can_Receive_Msg(canbuf);. k. {4 q" Z7 _8 C- Z if(key)//接收到有数据 { LCD_Fill(60,270,130,310,WHITE);//清除之前的显示 for(i=0;i<key;i++)" J* g8 b: [6 ~& P8 _- I { ( Q: X$ N7 V/ R. E2 O 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);# E$ V9 o7 e7 w; }3 x" h. A* g* R% ^ _ if(t==20) {) }% l1 @; B* q" t LED0=!LED0;//提示系统正在运行 4 X9 t9 F T# c! F4 {5 k4 c t=0; cnt++;3 _8 G# I$ h" A% {. e LCD_ShowxNum(60+48,170,cnt,3,16,0X80); //显示数据3 y" H- T# x+ ^* I } # _) H L1 {2 ?9 m: o9 Z: A: a } } ( Q; R" @1 c2 y* L8 g0 ` |
![]() |