CAN初始化代码如下,这里没什么好讲的,可参考正点原子官方提供的例程。
#include "can.h"
#include <string.h>
#include "stm32f10x.h"
#include <stdarg.h>
#include <stdio.h>
u8 CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
{
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
#if CAN_RX0_INT_ENABLE
NVIC_InitTypeDef NVIC_InitStructure;
#endif
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PORTA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能CAN1时钟
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
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化IO
//CAN单元设置
CAN_InitStructure.CAN_TTCM=DISABLE;
CAN_InitStructure.CAN_ABOM=DISABLE;
CAN_InitStructure.CAN_AWUM=DISABLE;
CAN_InitStructure.CAN_NART=ENABLE;
CAN_InitStructure.CAN_RFLM=DISABLE;
CAN_InitStructure.CAN_TXFP=DISABLE;
CAN_InitStructure.CAN_Mode= mode;
//设置波特率
CAN_InitStructure.CAN_SJW=tsjw;
CAN_InitStructure.CAN_BS1=tbs1;
CAN_InitStructure.CAN_BS2=tbs2;
CAN_InitStructure.CAN_Prescaler=brp;
CAN_Init(CAN1, &CAN_InitStructure);
CAN_FilterInitStructure.CAN_FilterNumber=0;
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);//滤波器初始化
#if CAN_RX0_INT_ENABLE
CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 主优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 次优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
#endif
return 0;
}
#if CAN_RX0_INT_ENABLE //使能RX0中断
//中断服务函数
void USB_LP_CAN1_RX0_IRQHandler(void)
{
CanRxMsg RxMessage;
int i=0;
CAN_Receive(CAN1, 0, &RxMessage);
for(i=0;i<8;i++)
printf("rxbuf[%d]:%d\r\n",i,RxMessage.Data[i]);
}
#endif
//can发送一组数据(固定格式:ID为0X555,标准帧,数据帧)
//len:数据长度(最大为8)
//msg:数据指针,最大为8个字节.
//返回值:0,成功;
// 其他,失败;
u8 Can_Send_Msg(u8* msg,u8 len)
{
u8 mbox;
u16 i=0;
CanTxMsg TxMessage;
TxMessage.StdId=0x555; // 标准标识符
//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_Failed)&&(i<0XFFF))i++; //等待发送结束
if(i>=0XFFF)return 1;
return 0;
}
can_printf的代码如下,该部分代码主要由两部分构成,一是将格式化字符串及参数存储到调试消息缓冲区can_msg_buf中;二是将缓冲区中的调试信息发送到CAN总线,具体实现过程可以参考下面代码的注释。
#define CAN_MSG_BUF_SIZE 128 // 调试消息缓冲区大小
char can_msg_buf[CAN_MSG_BUF_SIZE]; // 调试消息缓冲区
void can_printf(const char *fmt, ...)
{
// 将格式化字符串及参数存储到调试消息缓冲区can_msg_buf中
va_list args;
va_start(args, fmt);
vsnprintf(can_msg_buf, CAN_MSG_BUF_SIZE, fmt, args);
va_end(args);
//将缓冲区中的调试信息发送到CAN总线
int strLength = strlen(can_msg_buf);
int split = strlen(can_msg_buf) / 8;
int remainder = strlen(can_msg_buf) % 8;
u8 asciiArray[strLength];
u8 canbuffer[8];
u8 fdbc;
for (int i = 0; i < strLength ; i++)//不管是否超过8个字节先发一次数据,后期可以用来查看mailbox是否被占用
{
asciiArray[i] = (int)can_msg_buf[i];
}
for (int i = 0; i < 8; i++)
{
canbuffer[i] = asciiArray[i];
}
fdbc = Can_Send_Msg(canbuffer, 8); //can发送数据
if (strLength > 8)//如果大于8个字节进行拆包,从弟9个数据开始发送
{
for (int i = 1; i < split ; i++)
{
for (int j = 0; j < 8; j++)
{
canbuffer[j] = asciiArray[i * 8 + j];
}
while (CAN_TransmitStatus(CAN1, 0) == CAN_TxStatus_Pending) //检查MailBox[0]是否空闲
{
//等待
}
fdbc = Can_Send_Msg(canbuffer, 8);
//如果不检测mailbox的占用状态,在这里也可以加一个延时函数,但是一般不建议这样写
//delay_ms(3);
}
if (remainder != 0)
{
for (int i = 0; i < 8; i++)
{
canbuffer[i] = 0;
}
for (int i = 0; i < remainder; i++)
{
canbuffer[i] = asciiArray[split * 8 + i];
}
while (CAN_TransmitStatus(CAN1, 0) == CAN_TxStatus_Pending) //检查MailBox[0]是否空闲
{
//等待
}
fdbc = Can_Send_Msg(canbuffer, 8);
}
}
}
主函数代码如下,就是每隔一段时间打印一句话,在这句话中有一个变量,实现类似于printf函数的功能。
#include "stm32f10x.h"
#include "can.h"
#include "delay.h"
int main(void)
{
delay_init();
CAN_Mode_Init(CAN_SJW_1tq, CAN_BS2_8tq, CAN_BS1_9tq, 4, CAN_Mode_Normal); //can初始化
while (1)
{
static int m = 0;
m++;
can_printf("Hello, Have a Good Day, %d Seconds Have Passed Now!!!", m);
delay_ms(1000);
}
}
最后可以看一下现象,本人用Qt编写了一个简单的上位机程序,这里不过多赘述,主要就是将ASCII码转换成字符串,实现该功能的代码主要如下:
//vco是一个结构体数组,代表接收的CAN报文,.Data就是访问了其中的数据帧,
//将每次接收的CAN报文数据帧依次放到QByteArray中,然后转换成字符串
void MainWindow::displayText(VCI_CAN_OBJ *vco,DWORD dwRel)
{
QByteArray data;
for(int i = 0;i<dwRel;i++)
{
for(int j = 0;j<8;j++)
data.append(vco[i].Data[j]);
}
QString str(data);
ui->textEdit->append(str);
}
最后就是观察现象了,效果如下:
最后,这是本人第一次发帖,如有什么不足之处请指出,还望大家多多包涵。