硬件平台:STM32F10X内部CAN模块 + TJA1050 + JLink
软件平台:Keil 4
一、结果演示
二、接受程序例程
程序涉及的模块有:
USART:通用同步异步收发器,即串口,用于发送数据至上位机显示已接收到的数据;
RCC:复位及时钟控制模块,用于初始化STM32 外设时钟及设置CAN总线通信的波特率;
GPIO:通用输入输出口;
NVIC:中断服务程序;
CAN:STM32F10X自带的CAN通信模块,F1系列只有1个CAN,F4系列含有2个CAN,大容量的由2个,小容量的只有一个;
Delay:延时等待模块
Led:用led灯来指示是否正常发送,系统是否工作正常。
RCC
#include "Rcc.h"
void RCC_Init(void)
{
ErrorStatus HSEStartUpStatus; //定义枚举类型错误状态变量
RCC_DeInit();//复位系统时钟设置
RCC_HSEConfig(RCC_HSE_ON); //打开外部高速时钟晶振,使能HSE
/*RCC_HSE_ON 开
_off 关 _bypass hse晶振被外部时钟旁路*/
HSEStartUpStatus = RCC_WaitForHSEStartUp();
/*RCC_WaitForHSEStartUp()返回一个ErrorStatus枚举值,
success好,error未好*/
if(HSEStartUpStatus == SUCCESS)//HES就绪
{
RCC_HCLKConfig(RCC_SYSCLK_Div1);
//AHB时钟(HCLK)=系统时钟
RCC_PCLK1Config(RCC_HCLK_Div2);
//设置低速AHB时钟(APB1)为HCLK的2分频
RCC_PCLK2Config(RCC_HCLK_Div1);
//设置高速AHB时钟(APB2)=HCLK时钟
FLASH_SetLatency(FLASH_Latency_2);
//设置FLASH延时周期数为2
//使能领取指缓存
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
//设置PLL时钟源及倍频系数,为HSE的9倍频 8MHz * 9 = 72MHz
/*void RCC_PLLConfig(u32 RCC_PLLSource, u32 RCC_PLLMul)
RCC_PLLSource_HSI_Div2 pll输入时钟=hsi/2;
RCC_PLLSource_HSE_Div1 pll输入时钟 =hse
RCC_PLLSource_HSE_Div2 pll输入时钟=hse/2
RCC_PLLMul_2 ------_16 pll输入时钟*2---16
pll输出时钟不得超过72MHZ*/
RCC_PLLCmd(ENABLE);
//ENABLE / DISABLE
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);//等待PLL输出稳定
/*FlagStatus RCC_GetFlagStatus(u8 RCC_FLAG) 检查指定RCC标志位
返回SET OR RESET
RCC_FLAG_HSIRDY HSI晶振就绪
RCC_FLAG_HSERDY
RCC_FLAG_PLLRDY
RCC_FLAG_LSERDY
RCC_FLAG_LSIRDY.......*/
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
//设置PLL为系统时钟源
/*void RCC_SYSCLKConfig(u32 RCC_SYSCLKSource) 设置系统时钟
RCC_SYSCLKSource_HSI
RCC_SYSCLKSource_HSE
RCC_SYSCLKSource_PLLCLK 选HSI HSE PLL 作为系统时钟*/ while(RCC_GetSYSCLKSource() != 0x08);
//判断PLL是否是系统时钟
/*u8 RCC_GetSYSCLKSource(void) 返回用作系统时钟的时钟源
0x00:HSI 0x04:HSE 0x08:PLL */
}
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |
RCC_APB2Periph_AFIO , ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
//U2 U3 时钟在APB1
//打开GPIO时钟,复用功能,串口1的时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能CAN1时钟
/*void RCC_APB2PeriphClockCmd(u32 RCC_APB2Periph, FunctionalState NewState)
enable 或 disable apb2 外设时钟
RCC_APB2Periph_AFIO 功能复用IO 时钟
RCC_APB2Periph_GPIOA/B/C/D/E GPIOA/B/C/D/E 时钟
RCC_APB2Periph_ADC1/ADC2 ADC1/2 时钟
RCC_APB2Periph_TIM1
RCC_APB2Periph_SPI1
RCC_APB2Periph_USART1
RCC_APB2Periph_ALL 全部APB2外设时钟*/
}
GPIO
#include "GPIO.h"
void MYGPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//GPIO_InitStructure初始化结构体为GPIO_InitTypeDef结构
GPIO_DeInit(GPIOA);
GPIO_StructInit(&GPIO_InitStructure);
//函数:指向结构GPIO_InitTypeDef的指针,待初始化
//CAN TX : A12
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
//CAN TX : A11
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化IO
// USART TX :A9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
//GPIO_SPEED:GPIO_SPEED_10MHz/_2MHz/_50MHz 最高输出速率
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
/*Mode,工作状态:GPIO_MODE_AIN ----- 模拟输入
_IN_FLOATING ----- 浮空输入
_IPD ----- 上拉输出
_IPU ----- 上拉输入
_OUT_OD ----- 开漏输出
_OUT_PP ----- 推挽输出
_AF_OD ----- 复用开漏输出
_AF_PP ----- 复用推挽输出*/
GPIO_Init(GPIOA , &GPIO_InitStructure);
// USART RX :A10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
//IO浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
//初始化
}
CAN
#include "can.h"
#include "delay.h"
#include "usart.h"
//波特率=Fpclk1/((tbs1+1+tbs2+1+1)*brp);
//mode:CAN_Mode_Normal,普通模式;CAN_Mode_LoopBack,回环模式;
u8 CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
{
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
CAN_DeInit(CAN1);//重置can1为复位状态
//f10x的库函数文件里面居然定义了两个CAN!
CAN_StructInit(&CAN_InitStructure);//复位重置所有的成员变量
CAN_InitStructure.CAN_TTCM=DISABLE;
//enable or disable 时间触发通讯模式
//非时间触发通信模式
CAN_InitStructure.CAN_ABOM=DISABLE;
//enable or disable 自动离线管理
//软件自动离线管理
CAN_InitStructure.CAN_AWUM=DISABLE;
//enable or disable 自动唤醒模式
//睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)
CAN_InitStructure.CAN_NART=ENABLE;
//enable or disable 非自动重传
//禁止报文自动传送
CAN_InitStructure.CAN_RFLM=DISABLE;
//enable or disable 接收FIFO 锁定模式
//报文不锁定,新的覆盖旧的
CAN_InitStructure.CAN_TXFP=DISABLE;
//enable or disable 发送FIFO 优先级
//优先级由报文标识符决定
CAN_InitStructure.CAN_Mode= mode;
/*CAN_Mode_Normal CAN硬件工作在正常模式
CAN_Mode_silent CAN硬件工作在静默模式
CAN_Mode_LoopBack 环回模式
CAN_Mode_Silent_LoopBack 静默环回模式*/
//模式设置: mode:0,普通模式;1,回环模式;
//设置波特率
CAN_InitStructure.CAN_SJW=tsjw;
/*重新同步跳跃宽度(Tsjw),每位中可以延长或缩短多少个时间单位的上限
为tsjw+1个时间单位
CAN_SJW_1tq 重新同步跳跃宽度为 1 个时间单位
CAN_SJW_2tq
CAN_SJW_3tq
CAN_SJW_4tq*/
CAN_InitStructure.CAN_BS1=tbs1;
/*时间段 1 的时间单位数目 为1--16个时间单位*/
//Tbs1=tbs1+1个时间单位CAN_BS1_1tq ~CAN_BS1_16tq
CAN_InitStructure.CAN_BS2=tbs2;
/*时间段 2 的时间单位数目 为1--8个时间单位*/
//Tbs2=tbs2+1个时间单位CAN_BS2_1tq ~ CAN_BS2_8tq
CAN_InitStructure.CAN_Prescaler=brp;
/*设定一个时间单位的长度,范围是1---1024,实际会减一,这是什么鬼???*/
//分频系数(Fdiv)为 brp+1 因为实际会减一,所以这里有加一???
CAN_Init(CAN1, &CAN_InitStructure); //初始化CAN1
//can 过滤器设置
CAN_FilterInitStructure.CAN_FilterNumber=0;
//指定待初始化的过滤器,范围是1---13
//过滤器0
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;
/*CAN_FilterMode_IdMask 标识符屏蔽位模式
CAN_FilterMode_IdList 标识符列表模式*/
//屏蔽位模式
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;
/*过滤器位宽
CAN_FilterScale_Two16bit 2 个 16 位过滤器
CAN_FilterScale_One32bit 1 个 32 位过滤器
因为版本吧 CAN_FilterScale_One32bit 已变为 CAN_FilterScale_32bit*/
//32位宽
CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;
/*设定过滤器标识符,范围是0x0000---0xFFFF
32 位位宽时为其高段位,16 位位宽时为第一个*/
//32位ID
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
/*设定过滤器标识符,范围是0x0000---0xFFFF
32 位位宽时为其低段位,16 位位宽时为第二个*/
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;
/*设定过滤器屏蔽标识符或者过滤器标识符,范围是0x0000---0xFFFF
32 位位宽时为其高段位,16 位位宽时为第一个*/
//32位MASK
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
/*设定过滤器屏蔽标识符或者过滤器标识符,范围是0x0000---0xFFFF
32 位位宽时为其低段位,16 位位宽时为第二个*/
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;
//设定了过滤器指向的FIFO : 0或1
/*CAN_FilterFIFO0 过滤器 FIFO 指向 过滤器x
CAN_FilterFIFO1 过滤器 FIF1 指向 过滤器x */
//过滤器0关联到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;
//激活过滤器0 enable or disable
CAN_FilterInit(&CAN_FilterInitStructure); //滤波器初始化
CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE);
/*enable or disable can中断
CAN_IT_FMP0 FIFO0消息挂号中断允许. */
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
//USB 低优先级 or CAN 接收 0 中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
// 主优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
// 次优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
return 0; //配置成功返回 0
}
//如果是开启了,就有中断服务函数
void USB_LP_CAN1_RX0_IRQHandler(void)
{
CanRxMsg RxMessage;
int i=0;
can_ack = 1;
CAN_Receive(CAN1, 0, &RxMessage);
for(i=0;i<8;i++)
can_rx[i]=RxMessage.Data[i];
}
Led
#include "led.h"
//初始化PB12和13为输出口.并使能这两个口的时钟
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PB端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB
GPIO_SetBits(GPIOB,GPIO_Pin_12|GPIO_Pin_13);
}