零、所需文件及环境:
1、第三章建立好的串口1当调试口程序
2、编译环境MDK5(KEIL5)
3、一个STM32F103C8T6硬件
4、一个下载器j-link 或 st-link等
5.代码编辑器 Notepad++ (可以不要 用记事本也能编译 都是习惯的问题)
6.USB转TTL设备 用于连接电脑串口助手
7.一个CAN分析仪 用来监测CAN总线数据
壹、复制第三章串口1当调试口程序
1.1 第0章为工程模版 但是真正应用时 灯、定时器、调试口 不管啥程序这仨都会用到 所以以后会把第三章程序当做我自己的基础工程。
1.2 复制第三章程序并修改名字
1.2 进入USER/BSP文件夹下 复制LED文件夹改为BSP_CAN
1.3 进入USER/BSP/BSP_CAN文件夹,修改文件名
贰、编写BSP_CAN.c与BSP_CAN.h代码
2.1 打开BSP_CAN.h修改为
#ifndef __BSP_CAN_H
#define __BSP_CAN_H
#include "stm32f10x.h"
extern void BSP_CAN_Init(void);
extern u8 Can_Send_Msg_TEXT(u8* msg,u8 len);
#endif
2.2 打开BSP_DEBUG.c修改为
#include "BSP_CAN.h"
#include "BSP_DEBUG.h"
#define CANx CAN1
#define CANx_RX_PIN GPIO_Pin_11
#define CANx_TX_PIN GPIO_Pin_12
#define CANx_GPIO_PORT GPIOA
#define CANx_RX_IRQn USB_LP_CAN1_RX0_IRQn
#define CANx_RX_Priority 6
#define CANx_RX_IRQHandler USB_LP_CAN1_RX0_IRQHandler
#define CANx_CLK RCC_APB1Periph_CAN1
#define CANx_GPIO_CLK RCC_APB2Periph_GPIOA
/* 功能: GPIO配置
参数: 无
返回值:无
*/
void BSP_CAN_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(CANx_GPIO_CLK, ENABLE);//使能PORTA时钟
RCC_APB1PeriphClockCmd(CANx_CLK, ENABLE);//使能CAN1时钟
//CAN_TX
GPIO_InitStructure.GPIO_Pin = CANx_TX_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽
GPIO_Init(CANx_GPIO_PORT, &GPIO_InitStructure); //初始化IO
//CAN_RX
GPIO_InitStructure.GPIO_Pin = CANx_RX_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_Init(CANx_GPIO_PORT, &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= CAN_Mode_Normal; //模式设置: mode:0,普通模式;1,回环模式;
/* 设置波特率:36MHz/9/(2+1+1)=1mbps */
// CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;
// CAN_InitStructure.CAN_BS1 = CAN_BS1_13tq;//250Kbps
// CAN_InitStructure.CAN_BS2 = CAN_BS2_2tq;
// CAN_InitStructure.CAN_Prescaler = 9;
CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;
CAN_InitStructure.CAN_BS1 = CAN_BS1_3tq;//500Kbps
CAN_InitStructure.CAN_BS2 = CAN_BS2_2tq;
CAN_InitStructure.CAN_Prescaler = 12;
// CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;
// CAN_InitStructure.CAN_BS1 = CAN_BS1_3tq;//1Mbps
// CAN_InitStructure.CAN_BS2 = CAN_BS2_2tq;
// CAN_InitStructure.CAN_Prescaler = 6;
CAN_Init(CANx, &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); //滤波器初始化
CAN_ITConfig(CANx,CAN_IT_FMP0,ENABLE); //FIFO0消息挂号中断允许.
NVIC_InitStructure.NVIC_IRQChannel = CANx_RX_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = CANx_RX_Priority; // 主优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 次优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
//中断服务函数
void CANx_RX_IRQHandler(void)
{
CanRxMsg RxMessage;
CAN_Receive(CANx, 0, &RxMessage);
DEBUGx_Send_ascii(&RxMessage.Data[0],8);
}
//can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)
//len:数据长度(最大为8)
//msg:数据指针,最大为8个字节.
//返回值:0,成功;
// 其他,失败;
u8 Can_Send_Msg_TEXT(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[i]=msg[i];
mbox= CAN_Transmit(CANx, &TxMessage);
i=0;
while((CAN_TransmitStatus(CANx, 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(CANx,CAN_FIFO0)==0)return 0; //没有接收到数据,直接退出
CAN_Receive(CANx, CAN_FIFO0, &RxMessage);//读取数据
for(i=0;i<8;i++)
buf[i]=RxMessage.Data[i];
return RxMessage.DLC;
}
叁、打开工程,将BSP_CAN.c添加入工程并添加头文件路径
肆、修改main函数
#include "stm32f10x.h"
#include "BSP_LED.h"
#include "BSP_DELAY.h"
#include "BSP_TIMER.h"
#include "BSP_DEBUG.h"
#include "BSP_CAN.h"
uint8_t CAN_TEXT[8] = {1,2,3,4,5,6,7,8};
int main(void)
{
BSP_LED_Init();
BSP_DELAY_Init();
// BSP_TIMER_Init();
BSP_DEBUG_Init(115200);
BSP_CAN_Init();
while(1)
{
Can_Send_Msg_TEXT(&CAN_TEXT[0],8);
printf("uart1 running...\r\n");
BSP_DELAY_ms(500);
}
}
伍、编译
5.1 一个警告 还是上一章的警告 不用管 抛掉出错的数据 编译器认为你那个变量没使用
陆、下载运行
6.1 调试口每隔500ms打印信息
6.2 can分析仪输出测试数据 can发送正常 每家分析仪使用都不一样 按人家的使用教程来就好 需要注意的是波特率和代码里面保持相同
6.3 can分析仪发送一条帧id为0x00000000 的数据帧 标准帧 数据为 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 串口助手收到八个相同的数据 CAN接收正常 因为我们写的can接收中断处理函数就是 将can口收到的数据原封不动从调试口送出去