CAN是 Controller Area Network (控制器局域网总线)的缩写,是ISO国际标准化的串行通信协议。CAN总线是一种可以在无主机情况下实现微处理器或者设备之间相互通信的总线标准,使用双绞线来传输信号,降低了误差。现在,CAN的高性能和可靠性已被认同,并被广泛地应用于工业自动化、船舶、医疗设备、工业设备等方面。
CAN/RS-485为什么要用双绞线
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
只要有一个单元输出显性电平,总线上即为显性电平;而隐性电平的条件较为苛刻,只有所有的单元都输出隐性电平,总线上才为隐性电平。
通信是通过以下5种类型的帧进行的
其中,数据帧和遥控帧有标准格式和拓展格式两种格式。标准格式有11个位的标识符(ID),拓展格式有29个位的ID。
CAN使用位同步的方式来抗干扰,减小误差,实现对总线电平信号进行正确的采样,确保通讯正常。由发送单元在非同步的情况下发送的每秒钟的位数称为位速率,一个位可分为4段。
其中,SS段(同步段)固定长度为1Tq,而BS1及BS2段可以在位时序寄存器CAN_BTR设置它们的时间长度。
通过配置位时序寄存器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
初始化及配置:
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