Stm32之CAN通信
1.CAN*(Controller Area Network)*协议简介
1)协议种类及特点:
- 高速协议:ISO11898(125k~1Mbps);低速协议:ISO11519-2(125Kbps以下)。
- 特点:
- 多主控制(每个单元都可发送消息, 每个单元发送消息时,都会发送一个ID(不是地址,而是作为接收时的优先级顺序,0多且0比较靠高位的,优先级高)优先级高的继续,优先级的将被打断,等待高优先级发送完再发送 每个单元在接收时,可以通过对ID进行识别,判断自己是否要接收)
- 系统柔软性:每个设备没有绑定的地址,所以可以随时添加设备单元;
- 速度快,距离远:1Mbps(<40m),10km(<5Kbps)
- 错误检测/通知/恢复功能;
- 故障单元自动隔离功能;
- 可连接的节点多;
2)物理层特征:
3)协议层简介:(均为MSB)
-
每个数据帧都由7个段组成:帧起始,仲裁段(包含ID 优先级 ),控制段,数据段,CRC段,ACK段,帧结束。
-
位时序: 对于没有时钟线的can协议,其采用的是与uart相同的通信的双方事先约定好波特率的形式。 但can对应每一位的时间有较严格的要求,所以有位时序(即每一个位0/1中分出不同的时间段)的构成:(最小时间单位为1tq)(通常一个完整的位长8-25个tq)
-
SS (SYNC SEG) 段:同步段,要求总线的跳变沿发生在此段内,则表明该节点已与总线同步,大小为1tq; -
PTS (PROP SEG) 段:传播时间段,用于补偿网络的物理延时,大小为1-8tq; -
PBS1 (PHASE SEG1) 段:相位缓冲段1,通过在重新同步时加长该段时间,用来补偿边沿阶段的误差,大小为1-8tq; -
PBS2 (PHASE SEG2) 段:相位缓冲段2,通过在重新同步时缩短该段时间,用来补偿边沿阶段的误差,大小为2-8tq; -
SJW (reSynchornization Jump Width) : 再同步补偿宽度,用于重新同步时加长PSBS1和缩短PBS2所允许的限定宽度。不属于一个位的时段 ,一个位仅包含上面的四段。 -
实际通讯时,
- 硬同步:通过帧起始的跳变沿是否落在SS段内判断是否同步,以此调整节点与总线的同步;
- 软同步:通过加长PBS1或者缩短PBS2,来使位的跳变沿落在SS段内,以此同步。
在使用stm32的库函数时,会通过定义PBS1和PBS2段的长度及时钟预分频系数的大小来确定通讯的波特率。
2.Stm32的bxCAN (Basic eXtend CAN)
1)bxCAN简介:
-
普通stm32f103系列具有一个CAN(14个过滤器),互联型和f407有2个CAN(28个过滤器);(介绍普通型为主) -
拥有3个发送报文邮箱,两个拥有三级邮箱的FIFO (First Input First Output) 用于接收报文;(邮箱:存放要发送和收到的内容) -
14组位宽可变,可配置标识符的接受过滤器 (Acceptance Filters) 用于对传输来的ID 优先级 进行识别,判断是否要接收该 ID 优先级 的报文。
2)过滤器位宽及模式
3)bxCAN模式简介:
-
三种工作模式的转换: -
三种测试模式的区别:
-
静默模式:对总线一直保持1隐性电平,接收数据帧正常; -
环回模式:自发自收,将自己发送出去的报文自己接收,可以用于自测试,而忽略接收数据帧; -
环回静默模式:对总线一直保持1隐性电平,且忽略接收的数据帧,完全与总线断开的自发自收;
4)邮箱发送过程:
红色表示一次发送成功的过程:
- 当有超过1个发送邮箱在挂号时,发送顺序由邮箱中报文的标识符决定。 根据CAN协议,标识符 数值最低的报文具有最高的优先级。如果标识符的值相等,那么邮箱号小的报文先被发送 。
5)邮箱接收过程:
3.bxCAN寄存器介绍:
寄存器的数量较多分为三组:(较多,每个位没有全部列出,详情看手册)
1)CAN控制和状态寄存器
-
CAN_MCR (Main Controller) :主控制寄存器,[5]AWUM 自动唤醒模式选择位(0软件,1硬件);[1]SLEEP 睡眠请求位(1进入睡眠模式,0退出睡眠模式) [0] INRQ 初始化请求位(0 由初始化进入正常模式,1 由正常模式进入初始化模式 (退出CAN时) ) -
CAN_MSR :主状态寄存器,[1]SLAK 睡眠模式确认位,表明正处于睡眠模式;[0]INAK 表明正处于初始化状态; -
CAN_TSR:发送邮箱1/2/3的各种状态位; -
CAN_RF0R :FIFO0的对应状态及控制寄存器,[5]RFOM0 释放FIFO0 的输出邮箱,释放后,该位由软件清零; -
CAN_RF1R:与CAN_RF0R的各位功能相同,控制及状态是FIFO1. -
CAN_IER:CAN各中断的使能位。 -
CAN_ESR:CAN错误状态寄存器。 -
CAN_BTR:CAN位时序寄存器,
-
[25:24]SJWT 这两位定义了重新同步时可延长或缩短的时间单位上限,(JwT+1)个tq. -
[22:20]TS2 这两位定义了时间段2占几(TS2+1个)个时间单元tq. -
[19:16]TS1 这两位定义了时间段1占几(TS1+1个)个tq. -
[9:0]BRP ((Baud rate prescaler)): 波特率分频器,该位域定义了时间单元(tq)的时间长度.
f
B
a
u
d
=
1
正
常
位
时
间
=
1
(
t
q
+
t
B
S
1
+
t
B
S
2
)
=
1
t
q
[
(
T
S
1
+
1
)
+
(
T
S
2
+
1
)
+
1
]
=
1
1
f
P
L
C
k
B
R
P
+
1
[
(
T
S
1
+
1
)
+
(
T
S
2
+
1
)
+
1
]
=
f
P
L
C
K
(
B
R
P
+
1
)
(
T
S
1
+
1
+
T
S
2
+
1
+
1
)
f_{Baud}=\frac{1}{正常位时间}=\cfrac{1}{(t_q+t_{BS1}+t_{BS2})}=\cfrac{1}{t_q[(TS1+1)+(TS2+1)+1]}\\= \cfrac{1}{\cfrac{1}{\cfrac{f_{PLCk}}{BRP+1}}[(TS1+1)+(TS2+1)+1]}=\frac{f_{PLCK}}{(BRP+1)(TS1+1+TS2+1+1)}
fBaud?=正常位时间1?=(tq?+tBS1?+tBS2?)1?=tq?[(TS1+1)+(TS2+1)+1]1?=BRP+1fPLCk??1?[(TS1+1)+(TS2+1)+1]1?=(BRP+1)(TS1+1+TS2+1+1)fPLCK??
2)CAN邮箱寄存器
3)CAN过滤器寄存器
-
CAN_FMR:过滤器主控寄存器,非互联型stm32只有[0]FINT位有效,(1过滤器组初始化,0过滤器组工作在正常模式); 以下寄存器在配置时,CAN的过滤器需处于初始化状态,即,FINT=1 以下寄存器中,均是低28位有效,但非互联型产品则是低14位有效, -
CAN_FS1R: (Filter scale) 用于设置过滤器的位宽,对应位为1,该过滤器为单个32位;为0,该过滤器为2个16位 -
CAN_FFA1R:(Filter FIFO assignment) 过滤器FIFO关联寄存器,对应位为0是,报文在该过滤器过滤后进入FIFO0;若为1,则进入FIFO1; -
CAN_FA1R: ( Filter active) 过滤器激活寄存器,对应位置为1,则激活该过滤器; -
CAN_FiRx (x=1/2,i=0 -13):过滤器组i的寄存器x;一组过滤器组寄存器两个32位,在不同模式(屏蔽位模式/标识符列表模式)时充当的角色不同,用于存放我们要求的ID号 优先级 ,或掩码。 [详情见](#### 2)过滤器位宽及模式)
4.库函数使用
1)流程:
初始化时,需填入两个结构体到两个初始化函数中:
CAN_Init(CAN1, &CAN_InitStructure);
typedef struct
{
uint16_t CAN_Prescaler;
uint8_t CAN_SJW;
uint8_t CAN_BS1;
uint8_t CAN_BS2;
uint8_t CAN_Mode;
FunctionalState CAN_TTCM;
FunctionalState CAN_ABOM;
FunctionalState CAN_AWUM;
FunctionalState CAN_NART;
FunctionalState CAN_RFLM;
FunctionalState CAN_TXFP;
} CAN_InitTypeDef;
CAN_FilterInit(CAN_FilterInitTypeDef* CAN_FilterInitStruct);
typedef struct
{
uint8_t CAN_FilterNumber;
uint8_t CAN_FilterScale;
uint8_t CAN_FilterMode;
uint16_t CAN_FilterFIFOAssignment;
uint16_t CAN_FilterIdHigh;
uint16_t CAN_FilterIdLow;
uint16_t CAN_FilterMaskIdHigh;
uint16_t CAN_FilterMaskIdLow;
FunctionalState CAN_FilterActivation;
} CAN_FilterInitTypeDef
CAN与其他外设在库函数上最大的不同,可能是它发送及接收数据时得使用结构体将数据传入,因为CAN每次发送的数据帧较大 (f103中没有CAN1/2,CAN1就是CAN)
-
发送数据: 需要使用一个库函数定义好的结构体:CanTxMsg 类型 mbox=CAN_Transmit(CAN1, &TxMessage)
typedef struct
{
uint32_t StdId;
uint32_t ExtId;
uint8_t IDE;
uint8_t RTR;
uint8_t DLC;
uint8_t Data[8];
} CanTxMsg;
注意,每次发送数据后,需检查对应的标志位,保证数据发送完成,才能退出 可以像原子一样使用如下的检验函数(设置检查标志位的次数限制,防止卡在死循环中) u16 i=0;
while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++;
if(i>=0XFFF)return 1;
return 0;
-
接收数据: 与发送完数据要检查数据是否发送完成一样,接收数据时先检查是否有对应的接收标志位被挂起 if( CAN_MessagePending(CAN1,CAN_FIFO0)==0)return 0;
读取数据时同样需要使用一个库函数定义好的结构体:CanRxMsg 类型 CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
typedef struct
{
uint32_t StdId;
uint32_t ExtId;
uint8_t IDE;
uint8_t RTR;
uint8_t DLC;
uint8_t Data[8];
uint8_t FMI;
} CanRxMsg;
|