Canfestival运行前的准备工作
添加串口驱动,打印CANopen输出信息在drives分组下添加uart驱动,使用STM32的串口1来实现打印数据,代码如下:#include "bsp_uart.h"
void Uart_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能 PORTB 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //使能USART1时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No ;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE);
}
//重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
/* 发送一个字节数据到串口 */
USART_SendData(USART1, (uint8_t) ch);
/* 等待发送完毕 */
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
return (ch);
}
暂不需要开启接收功能,使用main.c测试代码如下,经测试一切正常,可进行下一步。int main(void)
{
Uart_Config();
printf("hello canopen!\r\n");
while(1)
{
}
}
串口助手已经输出字符串信息。
添加CAN驱动,实现CAN数据的收发
现在我们已经把串口的打印功能给调试完毕了,可以正常打印数据来进行简单的调试了,下面接着完成CAN驱动部分,CAN初始化、CAN发送数据和CAN接收数据代码
CAN初始化部分,波特率和模式可以根据参数设定
#include "bsp_can.h"
/************************************************************
* CAN波特率计算公式:
* Baud =32000000/BRP/(tSJW+tBS1+tBS2)
*
*
* 1M kbps
* tSJW = CAN_SJW_1tq
* tBS1 = CAN_BS1_3tq
* tBS2 = CAN_BS2_2tq
* BRP= 6
* 800 kbps
* tSJW = CAN_SJW_1tq
* tBS1 = CAN_BS1_5tq
* tBS2 = CAN_BS2_3tq
* BRP= 5
* 500 kbps
* tSJW = CAN_SJW_1tq
* tBS1 = CAN_BS1_8tq
* tBS2 = CAN_BS2_9tq
* BRP= 4
* 250 kbps
* tSJW = CAN_SJW_1tq
* tBS1 = CAN_BS1_3tq
* tBS2 = CAN_BS2_2tq
* BRP= 24
* 200 kbps
* tSJW = CAN_SJW_1tq
* tBS1 = CAN_BS1_3tq
* tBS2 = CAN_BS2_2tq
* BRP=30
*************************************************************/
void CAN_Config(unsigned char mode,unsigned char tSJW,unsigned char tBS1,unsigned char tBS2,unsigned char BRP)
{
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//使能 PORTB 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //使能AFIO时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); //使能 CAN1 时钟
GPIO_PinRemapConfig(GPIO_Remap1_CAN1,ENABLE);//CAN1部分映射到PB8和PB9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_Init(GPIOB, &GPIO_InitStructure);
//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= CAN_Mode_LoopBack; //模式设置: 0,普通模式;1,回环模式;
//设置波特率
CAN_InitStructure.CAN_SJW=CAN_SJW_1tq; //重新同步跳跃宽度(Tsjw)
CAN_InitStructure.CAN_BS1=CAN_BS1_3tq; //时间段 1 占用时间单位
CAN_InitStructure.CAN_BS2=CAN_BS2_2tq ; // 时间段 2 占用时间单位
CAN_InitStructure.CAN_Prescaler=6; //分频系数(Fdiv)为 brp+1
CAN_Init(CAN1, &CAN_InitStructure);// 初始化 CAN1
//CAN过滤器设置
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;// FIFO0
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //激活过滤器 0
CAN_FilterInit(&CAN_FilterInitStructure); //滤波器初始
CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE); //FIFO0 消息挂号中断允许.
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);
}
CAN发送数据函数,一次最多发送8个字节数据/**********************************************************************
*CAN发送数据函数(固定格式:ID 为 0X12,标准帧,数据帧)
*len:数据长度(最大为 8)
*msg:数据指针,最大为 8 个字节.
*返回值:0,成功;
* 其他,失败;
**********************************************************************/
unsigned char Can_Send_Msg(unsigned char* msg,unsigned char len)
{
unsigned char mbox;
unsigned int i = 0;
CanTxMsgTxMessage;
TxMessage.StdId = 0x12; // 标准标识符为 0x12
TxMessage.ExtId = 0x12; // 设置扩展标示符(29 位)
TxMessage.IDE = CAN_Id_Standard; // 标准帧
TxMessage.RTR = CAN_RTR_Data; // 数据帧
TxMessage.DLC = len; // 要发送的数据长度
for(i=0;i<len;i++)
TxMessage.Data = msg;
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接收函数,采用中断方式接收//CAN接收 中断服务函数
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);
}
main.c中添加测试代码,暂且使用回环模式进行测试,波特率设置为1M,代码和串口打印的数据如下,证明一切OK,又可以进行下一步啦!unsigned char canbuf;
unsigned char i,res;
void Delay(unsigned int t)
{
unsigned int x,y;
for(x=t;x>0;x--)
for(y=1000;y>0;y--);
}
int main(void)
{
Uart_Config();
CAN_Config(1,0,2,1,30);//200K 回环模式
printf("Test CAN_Mode_LoopBack!\r\n");
while(1)
{
for(i=0;i<8;i++)
{
canbuf=i; //填充发送缓冲区
}
res = Can_Send_Msg(canbuf,8); //发送 8 个字节
if(res==0 ) printf("Send OK!\r\n");
else printf("Send Error!\r\n");
Delay(8000);
}
}
测试结果:
添加定时器,为CANopen提供时钟节拍
CANopen的运行需要定时器为其提供时基,所以完成定时器的驱动也是必不可少的一步,暂且使用TIM3
TIM3定时器的初始化和中断函数代码如下:
#include "bsp_timer.h"
unsigned char timeflag;
/************************************************************
* 定时器计算公式:
* Tout= ((arr+1)*(psc+1))/Tclk;
*
*arr:设置自动重装载寄存器周期的值
*psc:设置时钟频率除数的预分频值
*Tclk: TIM3 的输入时钟频率(单位为 Mhz) 72M
*Tout: TIM3 溢出时间(单位为 us)
*举例:10Khz 的计数频率,定时500ms arr = 4999 psc = 7199
*************************************************************/
void Timer_Config(unsigned int arr,unsigned int psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // 使能TIM3时钟
//定时器 TIM3 初始化
TIM_TimeBaseStructure.TIM_Period = arr; //设置自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM 向上计数
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //初始化 TIM3
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //允许更新中断
//中断优先级 NVIC 设置
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级 0 级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级 3 级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能
NVIC_Init(&NVIC_InitStructure); //初始化NVIC
TIM_Cmd(TIM3, ENABLE); //使能TIM3
}
//定时器 3 中断服务程序
void TIM3_IRQHandler(void) //TIM3 中断
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查 TIM3 更新中断发生与否
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除 TIM3 更新中断标志
timeflag = 1;
}
}
上面发送CAN报文还是使用模拟的延时函数来发送数据,现在就可以使用定时器来大摇大摆的发送数据啦!主函数测试代码如下,效果依旧如此,这一步我们也算完成啦!extern unsigned char timeflag;
unsigned char canbuf;
unsigned char i,res;
int main(void)
{
Uart_Config();
Timer_Config(4999,7199); //500ms10K
CAN_Config(1,0,2,1,30);//200K 回环模式
printf("Test CAN_Mode_LoopBack!\r\n");
while(1)
{
if(timeflag == 1)
{
timeflag = 0;
for(i=0;i<8;i++)
{
canbuf=i; //填充发送缓冲区
}
res = Can_Send_Msg(canbuf,8); //发送 8 个字节
if(res==0 ) printf("Send OK!\r\n");
else printf("Send Error!\r\n");
}
}
}
页:
[1]