打印
[程序源码]

Canfestival运行前的准备工作

[复制链接]
1328|13
手机看帖
扫描二维码
随时随地手机跟帖
沙发
一路向北lm|  楼主 | 2020-8-31 20:02 | 只看该作者
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);               
}


使用特权

评论回复
板凳
一路向北lm|  楼主 | 2020-8-31 20:03 | 只看该作者
//重定向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);
}


使用特权

评论回复
地板
一路向北lm|  楼主 | 2020-8-31 20:03 | 只看该作者
暂不需要开启接收功能,使用main.c测试代码如下,经测试一切正常,可进行下一步。
int main(void)
{       
Uart_Config();
printf("hello canopen!\r\n");
  while(1)
        {
        }
}


使用特权

评论回复
5
一路向北lm|  楼主 | 2020-8-31 20:04 | 只看该作者
串口助手已经输出字符串信息。

使用特权

评论回复
6
一路向北lm|  楼主 | 2020-8-31 20:04 | 只看该作者
添加CAN驱动,实现CAN数据的收发
现在我们已经把串口的打印功能给调试完毕了,可以正常打印数据来进行简单的调试了,下面接着完成CAN驱动部分,CAN初始化、CAN发送数据和CAN接收数据代码

使用特权

评论回复
7
一路向北lm|  楼主 | 2020-8-31 20:05 | 只看该作者
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);
}


使用特权

评论回复
8
一路向北lm|  楼主 | 2020-8-31 20:05 | 只看该作者
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;
  CanTxMsg  TxMessage;
  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[i] = msg[i];
        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;
}


使用特权

评论回复
9
一路向北lm|  楼主 | 2020-8-31 20:05 | 只看该作者
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[i]);
}


使用特权

评论回复
10
一路向北lm|  楼主 | 2020-8-31 20:06 | 只看该作者
main.c中添加测试代码,暂且使用回环模式进行测试,波特率设置为1M,代码和串口打印的数据如下,证明一切OK,又可以进行下一步啦!
unsigned char canbuf[8];
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]=i; //填充发送缓冲区
                }
     res = Can_Send_Msg(canbuf,8); //发送 8 个字节
                 if(res==0 ) printf("Send OK!\r\n");
                 else   printf("Send Error!\r\n");
                 Delay(8000);
        }
}


使用特权

评论回复
11
一路向北lm|  楼主 | 2020-8-31 20:06 | 只看该作者
测试结果:

使用特权

评论回复
12
一路向北lm|  楼主 | 2020-8-31 20:07 | 只看该作者
添加定时器,为CANopen提供时钟节拍
CANopen的运行需要定时器为其提供时基,所以完成定时器的驱动也是必不可少的一步,暂且使用TIM3

使用特权

评论回复
13
一路向北lm|  楼主 | 2020-8-31 20:07 | 只看该作者
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;
  }
}


使用特权

评论回复
14
一路向北lm|  楼主 | 2020-8-31 20:08 | 只看该作者
上面发送CAN报文还是使用模拟的延时函数来发送数据,现在就可以使用定时器来大摇大摆的发送数据啦!主函数测试代码如下,效果依旧如此,这一步我们也算完成啦!
extern unsigned char timeflag;
unsigned char canbuf[8];
unsigned char i,res;

int main(void)
{       
   Uart_Config();
         Timer_Config(4999,7199); //500ms  10K
         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]=i; //填充发送缓冲区
                                }
                         res = Can_Send_Msg(canbuf,8); //发送 8 个字节
                         if(res==0 ) printf("Send OK!\r\n");
                         else   printf("Send Error!\r\n");
                        }               
                }
}


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

274

主题

3760

帖子

74

粉丝