STM32F10X CAN+TJA1050发送程序例程,已测试

硬件平台:STM32F10X内部CAN模块 + TJA1050 + JLink

软件平台:Keil 4 

一、基础知识

1、CAN 是Controller Area Network ,控制局域网。CAN 控制器根据两根线上的电位差来判断总线电平。总线电平分为显性电平和隐性电平,显性为0,隐性为1,总线是:与的关系,所以显性具有优先性,显性强于隐性。发送方通过使总线电平发生变化,将消息发送给接收方。

2、什么是FIFO?

FIFO存储器,一种先进先出的双口数据缓存器,其中一个输入口,另一个口是输出口。First In First Out,相比于普通存储器优点是没有外部读写地址线,使用简单;缺点是只能顺序写入,顺序读出。其数据地址由内部读写指针自动加1完成,并非普通存储器那样由地址线决定读取或写入某个指定的地址。

主要作用:系统设计中,FIFO存储器可以增加数据传输率、处理大量数据流、匹配具有不同传输率。

  对于单片FIFO来说,主要有两种结构:触发导向结构和零导向传输结构。触发导向传输结构的FIFO是由寄存器阵列构成的,零导向传输结构的FIFO是由具有读和写地址指针的双口RAM构成。

3、相关硬件作用

TJA1050: 电平转换器,将STM32 CAN TX\RX 引脚的电平转换为符合CAN 通信协议的差分信号电平,同样功能的元器件可用SN65HVD230。

区别在于,TJA1050 需要 5V供电,范围是:4.75----5.25V,要加外部电源;而SN65HVD230只要3.3V供电,范围是3-----3.6V之间,与STM32基本一致,电路可以简化。

JLink:调试器,在线调试用。

CAN总线的协议、帧的结构、相关的时序即差分信号的仲裁等等,大家可以自行查找,多看几遍

二、发送程序例程

程序涉及的模块有:

USART:通用同步异步收发器,即串口,用于发送数据至上位机显示已发送的数据;

RCC:复位及时钟控制模块,用于初始化STM32 外设时钟及设置CAN总线通信的波特率;

GPIO:通用输入输出口;

CAN:STM32F10X自带的CAN通信模块,F10系列只有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)HCLK2分频

 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时钟源及倍频系数,HSE9倍频 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_SPEEDGPIO_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,回环模式;

//Fpclk1的时钟在初始化的时候设置为36M,如果设置CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,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;

//CAN单元设置

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->MCRSLEEP)

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  重新同步跳跃宽度为 个时间单位

CAN_SJW_2tq 

CAN_SJW_3tq 

CAN_SJW_4tq*/

CAN_InitStructure.CAN_BS1=tbs1; 

/*时间段 的时间单位数目  为1--16个时间单位*/

//Tbs1=tbs1+1个时间单位CAN_BS1_1tq ~CAN_BS1_16tq

CAN_InitStructure.CAN_BS2=tbs2;

/*时间段 的时间单位数目  为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 位位宽时为第一个*/

//32ID

CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;

/*设定过滤器标识符,范围是0x0000---0xFFFF

32 位位宽时为其低段位,16 位位宽时为第二个*/

CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;

/*设定过滤器屏蔽标识符或者过滤器标识符,范围是0x0000---0xFFFF

32 位位宽时为其高段位,16 位位宽时为第一个*/

//32MASK

CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;

/*设定过滤器屏蔽标识符或者过滤器标识符,范围是0x0000---0xFFFF

32 位位宽时为其低段位,16 位位宽时为第二个*/

CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;

//设定了过滤器指向的FIFO : 01

/*CAN_FilterFIFO0  过滤器 FIFO 指向 过滤器x

  CAN_FilterFIFO1 过滤器 FIF1 指向 过滤器x  */

//过滤器0关联到FIFO0

CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;

//激活过滤器0   enable or disable

CAN_FilterInit(&CAN_FilterInitStructure); //滤波器初始化

return 0; //配置成功返回 

}   

//can发送一组数据(固定格式:ID0X12,标准帧,数据帧)

//len:数据长度(最大为8)      

//msg:数据指针,最大为8个字节. 8*8bit = 64bit

//返回值:0,成功;

//  其他,失败;

u8 Can_Send_Msg(u8* msg,u8 len)

{

u8 TransmitMailbox = 0;//消息发送状态变量

u16 i=0;

CanTxMsg TxMessage;

//开始一个消息的传输 ,结构体名是 TxMessage

/*typedef struct 

u32 StdId; 

u32 ExtId; 

u8 IDE; 

u8 RTR; 

u8 DLC; 

u8 Data[8]; 

} CanTxMsg;*/

TxMessage.StdId=0x12; // 标准标识符  0x7FF

TxMessage.ExtId=0x12; // 设置扩展标示符 0x3FFFF

TxMessage.IDE=CAN_Id_Standard; 

/* IDE 消息标识符的类型

CAN_ID_STD  使用标准标识符

CAN_ID_EXT  标准标识符 扩展标识符*/

TxMessage.RTR=CAN_RTR_Data;

/* 待传输消息的帧类型

CAN_RTR_DATA  数据帧

CAN_RTR_REMOTE   远程帧*/

TxMessage.DLC=len;

// 要发送的数据长度 ,待传输消息的帧长度

for(i=0;i

TxMessage.Data[i]=msg[i];

TransmitMailbox= CAN_Transmit(CAN1, &TxMessage); 

  /*发送消息*/  

i=0; 

while((CAN_TransmitStatus(CAN1,TransmitMailbox) == CAN_TxStatus_Failed)&&(i<0XFFF))

i++;

//等待发送结束

printf("CAN has send data : 0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x\r\n",TxMessage.Data[0],TxMessage.Data[1],TxMessage.Data[2],TxMessage.Data[3],TxMessage.Data[4],TxMessage.Data[5],TxMessage.Data[6],TxMessage.Data[7]); 

if(i>=0XFFF)return 1;

return 0;

}

Led

#include "led.h"

//初始化PB1213为输出口.并使能这两个口的时钟     

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);

}

三、结果显示

STM32F10X CAN+TJA1050发送程序例程,已测试_第1张图片



你可能感兴趣的:(STM32)