CAN学习——基于GD32F470

CAN学习

  • CAN是什么?
  • CAN物理层
  • CAN协议
    • 帧的种类
    • 位时序
    • CAN通讯波特率计算
  • 基于GD32F470的代码

CAN是什么?

CAN是 Controller Area Network (控制器局域网总线)的缩写,是ISO国际标准化的串行通信协议。CAN总线是一种可以在无主机情况下实现微处理器或者设备之间相互通信的总线标准,使用双绞线来传输信号,降低了误差。现在,CAN的高性能和可靠性已被认同,并被广泛地应用于工业自动化、船舶、医疗设备、工业设备等方面。

CAN/RS-485为什么要用双绞线

CAN物理层

CAN控制器是根据两根线上的电位差来判断总线电平,分别是CAN_High和CAN_Low,这两条信号线共同构成一组差分信号线。

总线电平分为显性电平和隐性电平,显性电平在逻辑层面表现为“0”,隐性电平在逻辑层面表现为“1”,举个例子:

CAN_H和CAN_L的电平都为2.5V时,此时CAN总线为隐性 --> 逻辑1

CAN_H电平为3.5V,CAN_L为1.5V时,此时CAN总线为显性 --> 逻辑0
CAN学习——基于GD32F470_第1张图片

只要有一个单元输出显性电平,总线上即为显性电平;而隐性电平的条件较为苛刻,只有所有的单元都输出隐性电平,总线上才为隐性电平。

CAN学习——基于GD32F470_第2张图片

CAN协议

帧的种类

通信是通过以下5种类型的帧进行的

  1. 数据帧:用于发送单元向接收单元传送数据的帧
  2. 遥控帧:用于接收单元向具有相同ID的发送单元请求数据的帧
  3. 错误帧:用于当检测出错误时向其他单元通知错误的帧
  4. 过载帧:用于接收单元通知尚未做好接受准备的帧
  5. 帧间隔:用于将数据帧及遥控帧与前面的帧分离开来的帧

其中,数据帧和遥控帧有标准格式和拓展格式两种格式。标准格式有11个位的标识符(ID),拓展格式有29个位的ID。

位时序

CAN使用位同步的方式来抗干扰,减小误差,实现对总线电平信号进行正确的采样,确保通讯正常。由发送单元在非同步的情况下发送的每秒钟的位数称为位速率,一个位可分为4段。
CAN学习——基于GD32F470_第3张图片

CAN学习——基于GD32F470_第4张图片

其中,SS段(同步段)固定长度为1Tq,而BS1及BS2段可以在位时序寄存器CAN_BTR设置它们的时间长度。

CAN通讯波特率计算

通过配置位时序寄存器CAN_BTR的TS1[3:0]及TS2[2:0]寄存器位设定BS1及BS2的长度后,就可以确定每个CAN数据位的时间。

BS1段时间:

TS1=Tq x (TS1[3:0] + 1)

BS2段时间:

TS2= Tq x (TS2[2:0] + 1)

一个数据位的时间:

T1bit =1Tq+TS1+TS2 =1+ (TS1[3:0] + 1)+ (TS2[2:0] + 1)= N Tq

其中单个时间片的长度Tq与CAN外设的所挂载的时钟总线及分频器配置有关,CAN0和CAN1外设都是挂载在APB1总线上的, 而位时序寄存器CAN_BTR中的BRP[9:0]寄存器位可以设置CAN外设时钟的分频值 ,所以:

Tq = (BRP[9:0]+1) x TPCLK

其中的PLCK指APB1的时钟,最终可以算出CAN通讯的波特率:

BaudRate = 1/N Tq

基于GD32F470的代码

初始化及配置:

void CAN0_GPIO_Config(void)
{
    /* enable CAN clock */
    rcu_periph_clock_enable(RCU_CAN0);
    rcu_periph_clock_enable(CAN0_GPIO_CLK);  
    /* configure CAN0 GPIO */
    gpio_output_options_set(CAN0_TX_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, CAN0_TX_PIN);
    gpio_mode_set(CAN0_TX_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, CAN0_TX_PIN);
    gpio_af_set(CAN0_TX_GPIO_PORT, GPIO_AF_9, CAN0_TX_PIN);
    
    gpio_output_options_set(CAN0_RX_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, CAN0_RX_PIN);
    gpio_mode_set(CAN0_RX_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, CAN0_RX_PIN);
    gpio_af_set(CAN0_RX_GPIO_PORT, GPIO_AF_9, CAN0_RX_PIN);
}

void CAN1_GPIO_Config(void)
{
    //要想进入CAN1的中断必须初始化CAN0的时钟
    rcu_periph_clock_enable(RCU_CAN0);
    rcu_periph_clock_enable(CAN0_GPIO_CLK); 
    /* enable CAN clock */
    rcu_periph_clock_enable(RCU_CAN1);
    rcu_periph_clock_enable(CAN1_GPIO_CLK);  
    /* configure CAN1 GPIO */
    gpio_output_options_set(CAN1_TX_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, CAN1_TX_PIN);
    gpio_mode_set(CAN1_TX_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, CAN1_TX_PIN);
    gpio_af_set(CAN1_TX_GPIO_PORT, GPIO_AF_9, CAN1_TX_PIN);
    
    gpio_output_options_set(CAN1_RX_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, CAN1_RX_PIN);
    gpio_mode_set(CAN1_RX_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, CAN1_RX_PIN);
    gpio_af_set(CAN1_RX_GPIO_PORT, GPIO_AF_9, CAN1_RX_PIN);
}

void CAN_Init(can_parameter_struct can_parameter, can_filter_parameter_struct can_filter)
{
    CAN0_GPIO_Config();
    CAN1_GPIO_Config();
    can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);
    can_struct_para_init(CAN_INIT_STRUCT, &can_filter);
    
    can_deinit(CAN0);
    can_deinit(CAN1);
    /*CAN0 CAN1挂载在APB1
      APB1 -> 50MHz*/
    can_parameter.working_mode = CAN_NORMAL_MODE;      //正常模式
    can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;  //重同步宽度
    can_parameter.time_segment_1 = CAN_BT_BS1_5TQ;
    can_parameter.time_segment_2 = CAN_BT_BS2_4TQ;
    can_parameter.time_triggered = DISABLE;
    can_parameter.auto_bus_off_recovery = DISABLE;
    can_parameter.auto_wake_up = DISABLE;
    can_parameter.no_auto_retrans = DISABLE;
    can_parameter.rec_fifo_overwrite = DISABLE;
    can_parameter.trans_fifo_order = DISABLE;
    
#if  CAN_BAUDRATE == 500
    can_parameter.prescaler = 10;
#endif
    
    can_init(CAN0, &can_parameter);
    can_init(CAN1, &can_parameter);
    
    /* initialize filter */ 
    can_filter.filter_number=0;
    can_filter.filter_mode = CAN_FILTERMODE_MASK;
    can_filter.filter_bits = CAN_FILTERBITS_32BIT;
    can_filter.filter_list_high = 0x3000;              
    can_filter.filter_list_low = 0x0000;
    can_filter.filter_mask_high = 0x3000;              
    can_filter.filter_mask_low = 0x0000;   
    can_filter.filter_fifo_number = CAN_FIFO0;
    can_filter.filter_enable = ENABLE;
    
    can_filter_init(&can_filter);
    
    /* CAN1 filter number */
    can_filter.filter_number = 15;
    can_filter_init(&can_filter);
    
}

主函数:

FlagStatus can0_receive_flag = RESET;
FlagStatus can1_receive_flag = RESET;
FlagStatus can0_error_flag = RESET;
FlagStatus can1_error_flag = RESET;
can_parameter_struct can_init_parameter;
can_filter_parameter_struct can_filter_parameter;
can_trasnmit_message_struct transmit_message;
can_receive_message_struct receive_message;

int main(void)
{
    systick_config();
    Buzzer_Init();
    CAN_Init(can_init_parameter,can_filter_parameter);
    nvic_irq_enable(CAN1_RX0_IRQn,0,0);      //FIFO0的接收中断         
    can_interrupt_enable(CAN1, CAN_INT_RFNE0);       //打开CAN1中断
    /* initialize transmit message */
    transmit_message.tx_sfid = 0x300>>1;
    transmit_message.tx_efid = 0x00;
    transmit_message.tx_ft = CAN_FT_DATA;
    transmit_message.tx_ff = CAN_FF_STANDARD;
    transmit_message.tx_dlen = 2;
    transmit_message.tx_data[0] = 0x12;
    transmit_message.tx_data[1] = 0x34;
    while(1)
    {
        can_message_transmit(CAN1, &transmit_message);
        printf("\r\n can1 transmit data:%x,%x", transmit_message.tx_data[0], transmit_message.tx_data[1]);
        delay_1ms(1000);
        if(can1_receive_flag == SET)  //CAN1接收到消息
        {
          printf("\r\n can1 receive data:%x,%x\r\n", receive_message.rx_data[0], receive_message.rx_data[1]);
          Buzzer_on();
          delay_1ms(1000);
          Buzzer_off();
          can1_receive_flag = RESET;
        }
        
        if(can1_error_flag == SET)
        {
          can1_error_flag = RESET;
          printf("\r\n can1 communication error");
        }
    }
}

中断函数:

void CAN1_RX0_IRQHandler(void)
{
      /* check the receive message */
    can_message_receive(CAN1, CAN_FIFO0, &receive_message);
    /*
        FF表示帧格式
        FF = 0 -> 标准帧
        FF = 1 -> 拓展帧
                         */
    if((0x180 == receive_message.rx_sfid)&&(CAN_FF_STANDARD == receive_message.rx_ff)&&(2 == receive_message.rx_dlen))
    {
        can1_receive_flag = SET; 
    }
    else
    {
        can1_error_flag = SET; 
    }
    printf("\r\ncan1 receive data!!\r\n");
}

注意!!!!要想进入CAN1的接收中断,必须要初始化CAN0的时钟!!!这是GD32的BUG

你可能感兴趣的:(学习)