头文件can.h:1.定义测试ID;2.声明相关函数;
#ifndef __CAN_H
#define __CAN_H
#include "stm32f10x.h"
#include "stm32f10x_can.h"
#define CAN_RX0_INT_ENABLE ENABLE
#define TESTID (0x0CF11AD0 & 0xFFFFFFFF)
#define HAN_TESTID ((TESTID<<3) | (0x01<<2) | (0x00<<1))
uint8_t Can_Mode_Init(u8 tsjw, u8 tbs2, u8 tbs1, u16 brp, u8 mode);
uint8_t Can_Mode_Init1(u8 tsjw, u8 tbs2, u8 tbs1, u16 brp, u8 mode);
uint8_t Can_Send_Msg(uint8_t* msg,uint8_t len);
uint8_t Can_Send_Msg1(uint8_t* msg,uint8_t len,uint32_t ExId);
uint8_t Can_Recive_Msg(uint8_t* buf);
#endif
源文件can.c:1.CAN1相关初始化(使用库函数及自己写寄存器);2.CAN发送报文函数(使用库函数及自己写寄存器);3.CAN接收函数;
#include "can.h"
/*
* CAN初始化 成功返回0
*/
uint8_t Can_Mode_Init(u8 tsjw, u8 tbs2, u8 tbs1, u16 brp, u8 mode)
{
GPIO_InitTypeDef GPIO_InitStruct;
CAN_InitTypeDef CAN_InitStruct;
CAN_FilterInitTypeDef CAN_FilterInitStruct;
#if CAN_RX0_INT_ENABLE
NVIC_InitTypeDef NVIC_InitStruct;
#endif
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1,ENABLE); // 使能CAN1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); // 使能 GPIOA时钟
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12; // 引脚12
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11; // 引脚11
GPIO_Init(GPIOA,&GPIO_InitStruct);
CAN_InitStruct.CAN_TTCM = DISABLE; // 非时间触发通信模式
CAN_InitStruct.CAN_ABOM = DISABLE; // 软件自动离线管理
CAN_InitStruct.CAN_AWUM = DISABLE; // 睡眠模式通过软件唤醒
CAN_InitStruct.CAN_NART = ENABLE; // 禁止报文自动传送
CAN_InitStruct.CAN_RFLM = DISABLE; //报文不锁定,新覆盖旧
CAN_InitStruct.CAN_TXFP = DISABLE; // 优先级由报文标识符决定
CAN_InitStruct.CAN_Mode = mode; // 模式设置
CAN_InitStruct.CAN_SJW = tsjw;
CAN_InitStruct.CAN_BS1 = tbs1;
CAN_InitStruct.CAN_BS2 = tbs2;
CAN_InitStruct.CAN_Prescaler = brp;
CAN_Init(CAN1,&CAN_InitStruct);
CAN_FilterInitStruct.CAN_FilterNumber = 0; // 过滤器0
CAN_FilterInitStruct.CAN_FilterMode = CAN_FilterMode_IdMask; // 屏蔽位模式
CAN_FilterInitStruct.CAN_FilterScale = CAN_FilterScale_32bit; // 32位
CAN_FilterInitStruct.CAN_FilterIdHigh = (HAN_TESTID&0xFFFF0000)>>16; // FxR1 高16位
CAN_FilterInitStruct.CAN_FilterIdLow = (HAN_TESTID&0x0000FFFF); // // FxR1 低16位
CAN_FilterInitStruct.CAN_FilterMaskIdHigh = 0xFFFF;// 32位MSK 全部关心
CAN_FilterInitStruct.CAN_FilterMaskIdLow = 0xFFFF; //
CAN_FilterInitStruct.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; // 关联到FIFO 0
CAN_FilterInitStruct.CAN_FilterActivation = ENABLE; // 使能过滤器
CAN_FilterInit(&CAN_FilterInitStruct);
#if CAN_RX0_INT_ENABLE
CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE); // FIFO0挂号接收中断
NVIC_InitStruct.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStruct);
#endif
return 0;
}
/*
* CAN发送数据 官方库函数
*/
uint8_t Can_Send_Msg(uint8_t* msg,uint8_t 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[i] = msg[i];
}
mbox = CAN_Transmit(CAN1,&TxMessage);
i = 0;
while((CAN_TransmitStatus(CAN1,mbox) != CAN_TxStatus_Ok) && (i<0xfff)) i++; // 等待结束
if(i >= 0xfff) return 1; // 如果超时
return 0;
}
/*
* CAN接收数据 官方库函数
*/
uint8_t Can_Recive_Msg(uint8_t* buf)
{
u32 i;
uint8_t stat = 0;
CanRxMsg RxMessage;
if(CAN_MessagePending(CAN1,CAN_FIFO0) == 0) return 0; // 没有接收到数据,直接退出
CAN_Receive(CAN1,CAN_FIFO0,&RxMessage); // 读取数据
if(RxMessage.FMI == 0) // 过滤器匹配编号
{
for(i=0; i<8; i++) // 复制数据
{
buf[i] = RxMessage.Data[i];
}
stat = RxMessage.DLC; // 赋值数据长度
}
return stat ; // 返回数据长度
}
/*
* 初始化CAN1 使用官方基地址
*/
uint8_t Can_Mode_Init1(u8 tsjw, u8 tbs2, u8 tbs1, u16 brp, u8 mode)
{
uint32_t wait_ack = 0;
uint8_t InitStatus = 0;
CAN_FilterInitTypeDef CAN_FilterInitStruct;
RCC->APB1ENR |= 0x01<<25; // 使能CAN时钟
RCC->APB2ENR |= 0x01<<2; // 使能GPIOA时钟
GPIOA->CRH &= ~(0x0F<<12); // 复位12-15位
GPIOA->CRH |= 0x08<<12; // 1000 PA11 上拉/下拉输入
GPIOA->BSRR = 0x01<<11; // 上拉PA11
GPIOA->CRH &= ~(0x0F<<16); // 复位16-19位
GPIOA->CRH |= 0x0B<<16; // 1011 PA12 复用推挽输出 50MHz
CAN1->MCR &= ~(0x01<<1); // 退出睡眠模式
CAN1->MCR |= 0x01<<0; //请求初始化
while (((CAN1->MSR & 0x01) != 0x01) && (wait_ack != 0x0000FFFF)) // 初始化确认 判断是否超时
{
wait_ack++;
}
if(((CAN1->MSR & 0x01) != 0x01)) // 是否未进入初始化模式
{
InitStatus = 0; //初始化失败
}
CAN1->MCR &= ~(0x01<<7); // 静止时间触发通信
CAN1->MCR &= ~(0x01<<6); // 离线状态的退出是在,软件对CAN_MCR寄存器的INRQ位进行置1随后清0后,一旦硬件检测到128次11位连续的隐性位,就退出离线状态;
CAN1->MCR &= ~(0x01<<5); // 不开启自动唤醒
CAN1->MCR &= ~(0x01<<4); // 允许报文自动发送
CAN1->MCR &= ~(0x01<<3); // 新报文覆盖旧报文
CAN1->MCR &= ~(0x01<<2); // 优先级由报文的标识符来决定
CAN1->BTR = (uint32_t)((uint32_t)mode << 30) | \
((uint32_t)tsjw << 24) | \
((uint32_t)tbs1 << 16) | \
((uint32_t)tbs2 << 20) | \
((uint32_t)brp - 1);
CAN1->MCR &= ~(0x01<<0); // 从初始化模式进入正常工作模式
wait_ack = 0;
while (((CAN1->MSR & 0x01) == 0x01) && (wait_ack != 0x0000FFFF)) //等待退出初始化模式
{
wait_ack++;
}
if((CAN1->MSR & 0x01) == 0x01) // 是否未进入正常模式
{
InitStatus = 0; //初始化失败
}
CAN_FilterInitStruct.CAN_FilterNumber = 0; // 过滤器0
CAN_FilterInitStruct.CAN_FilterMode = CAN_FilterMode_IdMask; //
CAN_FilterInitStruct.CAN_FilterScale = CAN_FilterScale_32bit; // 32位
CAN_FilterInitStruct.CAN_FilterIdHigh = (HAN_TESTID&0xFFFF0000)>>16; // FxR1 高16位
CAN_FilterInitStruct.CAN_FilterIdLow = (HAN_TESTID&0x0000FFFF); // // FxR1 低16位
CAN_FilterInitStruct.CAN_FilterMaskIdHigh = 0xFFFF;// 32位MSK 全部关心
CAN_FilterInitStruct.CAN_FilterMaskIdLow = 0xFFFF; //
CAN_FilterInitStruct.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; // 关联到FIFO 0
CAN_FilterInitStruct.CAN_FilterActivation = ENABLE; // 使能过滤器
CAN_FilterInit(&CAN_FilterInitStruct);
return InitStatus;
}
/*
* CAN发送数据 使用官方基地址
*/
uint8_t Can_Send_Msg1(uint8_t* msg,uint8_t len,uint32_t ExId)
{
u8 transmit_mailbox;
u16 i = 0;
/* 选择发送邮箱 */
if((CAN1->TSR & (0x01<<26)) == (0x01<<26)) // 邮箱0空
{
transmit_mailbox = 0;
}
else if((CAN1->TSR & (0x01<<27)) == (0x01<<27)) // 邮箱1空
{
transmit_mailbox = 1;
}
else if((CAN1->TSR & (0x01<<28)) == (0x01<<28)) // 邮箱2空
{
transmit_mailbox = 2;
}
else
{
transmit_mailbox = 4; // 无邮箱空
}
if(transmit_mailbox != 0x04) // 寻找到空闲邮箱
{
CAN1->sTxMailBox[transmit_mailbox].TIR &= 0x01; // 复位除了TXRQ以外的位
CAN1->sTxMailBox[transmit_mailbox].TIR |= (0x00<<1) | (0x01<<2) | (ExId&0xffffffff)<<3 ; // 设置扩展标识符
CAN1->sTxMailBox[transmit_mailbox].TDTR &= (uint32_t)0xFFFFFFF0; // 复位数据长度位
CAN1->sTxMailBox[transmit_mailbox].TDTR |= len&0x0f; // 设置数据长度
CAN1->sTxMailBox[transmit_mailbox].TDLR = ((u32)msg[0]) | ((u32)msg[1]<<8) | ((u32)msg[2]<<16) |((u32)msg[3]<<24); // 数据填充
CAN1->sTxMailBox[transmit_mailbox].TDHR = ((u32)msg[4]) | ((u32)msg[5]<<8) | ((u32)msg[6]<<16) |((u32)msg[7]<<24);
CAN1->sTxMailBox[transmit_mailbox].TIR |=0x01; // 请求发送
}
i = 0;
while((CAN_TransmitStatus(CAN1,transmit_mailbox) != CAN_TxStatus_Ok) && (i<0xfff)) i++; // 等待结束
if(i >= 0xfff) return 1; // 如果超时
return 0;
}
中断服务函数,
/*
* CAN1中断服务函数
* 可用
*/
uint8_t buf[8] = {0,1,2,3,4,5,6,7};
uint8_t stat = 0;
void USB_LP_CAN1_RX0_IRQHandler(void)
{
uint8_t i;
CanRxMsg RxMessage;
stat = 0;
CAN_Receive(CAN1,CAN_FIFO0,&RxMessage); // 读取数据
if(RxMessage.FMI == 0) // 关联滤波器号 == 0?
{
for(i=0; i<8; i++) // 复制数据
{
buf[i] = RxMessage.Data[i];
}
stat = RxMessage.DLC; // 状态赋值
}
ToggleLed(GPIOB,GPIO_Pin_15); // 翻转PB15
}
/*
* CAN1中断方式读取报文信息
* 读取成功返回:1 失败返回:0
*/
uint8_t ReadRxDat(uint8_t* read_buff)
{
uint8_t i;
uint8_t rx_ok =0;
if(stat != 0)
{
for(i = 0; i<8; i++) // 复制数据
{
read_buff[i] = buf[i];
}
stat = 0; // 给状态标志位清零
rx_ok = 1; // 读取状态标志位
}
return rx_ok; // 返回标志位 成功:1 失败:0
}
主函数main.c:1.调用初始化函数;2.若收到报文则向上位机返回原报文;
#include "stm32f10x.h"
#include "stm32f10x_it.h"
#include "led.h"
#include "timer.h"
#include "systick.h"
#include "can.h"
/*
* 主函数
*/
int main(void)
{
uint8_t can_buf[8] = {0,1,2,3,4,5,6,7};
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断分组 2位优先级 2位响应优先级
LedInit1(); // LED初始化
SysTick_Init(); // 滴答定时器初始化
Can_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq ,CAN_BS1_9tq ,8,CAN_Mode_Normal);
while(1)
{
if(ReadRxDat(can_buf) != 0) // if 中断接收到指定ID报文
{
Can_Send_Msg1(can_buf,8,TESTID); // 发送收到的报文
ToggleLed(GPIOC,GPIO_Pin_6); // 翻转LED
}
Delay_ms(500); // 延时500ms
}
}
亲测可用,无论是库函数还是自己写的寄存器都能使用!!!
参考资料:正点原子代码及STM32中文参考手册!!!