当前位置: 首页 > news >正文

STM32基于can总线通信控制多个舵机/电机原理及代码

CAN(Controller Area Network)总线是一种广泛用于汽车和工业领域的串行通信协议,由德国博世公司在1986年首次提出。它最初设计用于汽车内部的各种电子控制单元(ECU)之间的通信,但由于其高可靠性、实时性和抗干扰能力,现在也被广泛应用于其他领域,如工业自动化、医疗设备、航空航天等。

此次实战演示效果为一个stm32通过按键给另一个stm32发送消息,另一个stm32接受到消息后显示到屏幕上并处理收到的消息控制舵机。演示视频在同名b站已发布,下面是视频链接:

【两个stm32的can通信及控制多个舵机-哔哩哔哩】 https://b23.tv/WHhrOWn

下图是几种主要通信方式的特点及对比:

1.主要特点

  1. 多主结构:CAN总线支持多主模式,任何节点都可以在总线空闲时开始传输消息,这通过非破坏性仲裁机制实现。

  2. 非破坏性仲裁:当多个节点同时发送消息时,CAN总线使用标识符(ID)进行仲裁。标识符数值越低,优先级越高。在仲裁过程中,优先级高的消息继续发送,而优先级低的消息自动退出发送,不会造成数据损坏。

  3. 高可靠性:CAN总线具有错误检测、错误通知和错误恢复机制。它包括循环冗余校验(CRC)、帧检查、应答错误和形式错误等。

  4. 通信速率:CAN总线的通信速率可达1 Mbps(在短距离内),距离较长时,速率会降低。例如,在40米内可达1 Mbps,而在1000米内可达50 kbps。

  5. 连接节点数:理论上,CAN总线可以连接多达110个节点,实际数量取决于总线驱动器和网络负载。

  6. 消息格式:CAN总线有两种消息格式:标准帧(11位标识符)和扩展帧(29位标识符)。

2.CAN消息帧类型:最常用的是数据帧。

3.数据帧结构:

标准数据帧(11位ID) 由以下字段组成:

  • 帧起始(Start of Frame, SOF):一个显性位(0),表示帧的开始。

  • 仲裁场(Arbitration Field):

    • 11位标识符(ID) + 远程传输请求位(RTR,0表示数据帧)。

  • 控制场(Control Field):包括标识符扩展位(IDE,0表示标准帧)、保留位和数据长度码(DLC,0-8字节)。

  • 数据场(Data Field):0-8字节的数据。

  • CRC场(CRC Field):15位CRC校验和 + 1位CRC界定符。

  • 应答场(ACK Field):包括应答间隙和应答界定符。

  • 帧结束(End of Frame, EOF):7个连续的隐性位(1)。

扩展数据帧(29位ID) 与标准帧类似,但仲裁场包括29位标识符。

4.错误处理:

5.stm32的can外设:

STM32的CAN外设称为bxCAN(Basic Extended CAN)其主要特点有:

  • 支持CAN协议:兼容CAN 2.0A和2.0B主动模式,支持标准帧(11位标识符)和扩展帧(29位标识符)。

  • 通信速率:最高可达1 Mbps。

  • 四种工作模式:正常模式、静默模式、环回模式和环回静默模式(用于自测试)。

  • 可配置的位时序:允许用户根据总线时钟和波特率要求设置位时序参数。

  • 发送和接收:具有3个发送邮箱和2个接收FIFO(每个FIFO可存储3个报文),以及可配置的过滤器组(最多28个,取决于型号)。

  • 高级错误管理:包括错误计数器和错误状态指示。

  • 时间触发通信:支持时间触发通信模式(TTCAN)级别1。

6.can收发器模块:TJA1050

理论部分介绍的差不多了接下来给大家介绍实战了,虽然理论看起来很复杂但实际上stm32的库函数已经帮大家都封装好了,我们只需进行简单的配置即可使用,所以说使用起来并不难,大家可以先入门学会基本使用,后面需要过滤,遥控等其它功能再着重的加强学习。

7.硬件介绍

为了方便就直接用了我之前项目的pcb板子,因为舵机和电机都是pwm控制的,这里就选用了舵机来演示,原理是一样的,能控制舵机自然就会电机了。(注:这里硬件连接有一个问题,我的两个TJA1050的电源和地线都接到了右边板子上,这样会导致只能右边32给左边的32发送消息,但在此次演示中不影响)

8.软件介绍

main.c

uint8_t KeyNum;uint8_t yxy=0;
uint8_t yzc=0;CanTxMsg TxMsgArray[] = {
/*   StdId     ExtId         IDE             RTR        DLC         Data[8]        */{0x555, 0x00000000, CAN_Id_Standard, CAN_RTR_Data,   4, {0x11, 0x22, 0x33, 0x44}},{0x000, 0x12345678, CAN_Id_Extended, CAN_RTR_Data,   4, {0xAA, 0xBB, 0xCC, 0xDD}},{0x666, 0x00000000, CAN_Id_Standard, CAN_RTR_Data,   4, {0x01, 0x02, 0x03, 0x04}},{0x000, 0x0789ABCD, CAN_Id_Extended, CAN_RTR_Data,   4, {0x05, 0x06, 0x07, 0x08}},
};//CAN_RTR_Remote,在普通模式下,遥控帧通信需要另一个节点响应数据
uint8_t pTxMsgArray = 0;void RCC_Configuration(void) {RCC_DeInit();SystemInit();
}int main(void)
{delay_init();	    	 		//延时函数初始化	  RCC_Configuration();            //系统时钟初始化KEY_Init();	                    //按键初始化EXTIX_Init();					//外部中断初始化	MyCAN_Init();                   //can初始化Servo_Init();                   //舵机初始化LED_Init();                     //LED初始化OLED_Init();                    //oled初始化OLED_Clear();                   //oled初始化后清屏OLED_ShowString(0, 0, " Rx :",16);OLED_ShowString(0, 2, "RxID:",16);OLED_ShowString(0, 4, "Leng:",16);OLED_ShowString(0, 6, "Data:",16);while (1){	  //按键发送can消息		KeyNum = Key_GetNum();//按键扫描if(KeyNum == 1)//B3{MyCAN_Transmit(&TxMsgArray[pTxMsgArray]);pTxMsgArray ++;if (pTxMsgArray >= sizeof(TxMsgArray) / sizeof(CanTxMsg)){pTxMsgArray = 0;}}else if(KeyNum == 2)//B4{MyCAN_Transmit(&TxMsgArray[pTxMsgArray]);pTxMsgArray ++;if (pTxMsgArray >= sizeof(TxMsgArray) / sizeof(CanTxMsg)){pTxMsgArray = 0;}}		//处理中断接收到的can消息if (MyCAN_RxFlag == 1)//中断标志位{MyCAN_RxFlag = 0;if (MyCAN_RxMsg.IDE == CAN_Id_Standard)//标准帧{OLED_ShowString(45, 0, "Std",16);OLED_ShowHexNum(45, 2, MyCAN_RxMsg.StdId, 8);}else if (MyCAN_RxMsg.IDE == CAN_Id_Extended)//拓展帧{OLED_ShowString(45, 0, "Ext",16);OLED_ShowHexNum(45, 2, MyCAN_RxMsg.ExtId, 8);}if (MyCAN_RxMsg.RTR == CAN_RTR_Data)//数据帧{OLED_ShowString(75, 0, "Data  ",16);OLED_ShowHexNum(45, 4, MyCAN_RxMsg.DLC, 1);OLED_ShowHexNum(45, 6, MyCAN_RxMsg.Data[0], 2);OLED_ShowHexNum(63, 6, MyCAN_RxMsg.Data[1], 2);OLED_ShowHexNum(81, 6, MyCAN_RxMsg.Data[2], 2);OLED_ShowHexNum(99, 6, MyCAN_RxMsg.Data[3], 2);//根据接受到的数据控制舵机(可自行diy)if(MyCAN_RxMsg.Data[0] == 0x11){GPIO_ResetBits(GPIOA, GPIO_Pin_11);Servo_SetAngle(0);}if(MyCAN_RxMsg.Data[0] == 0xAA){GPIO_ResetBits(GPIOA, GPIO_Pin_12);Servo_SetAngle1(0);}if(MyCAN_RxMsg.Data[0] == 0x01){GPIO_SetBits(GPIOA, GPIO_Pin_11);Servo_SetAngle(60);}if(MyCAN_RxMsg.Data[0] == 0x05){GPIO_SetBits(GPIOA, GPIO_Pin_12);Servo_SetAngle1(60);}}else if (MyCAN_RxMsg.RTR == CAN_RTR_Remote)//遥控帧{OLED_ShowString(75, 0, "Remote",16);OLED_ShowHexNum(45, 4, MyCAN_RxMsg.DLC, 1);OLED_ShowHexNum(45, 6, 0x00, 2);OLED_ShowHexNum(63, 6, 0x00, 2);OLED_ShowHexNum(81, 6, 0x00, 2);OLED_ShowHexNum(99, 6, 0x00, 2);}}		}
}

一般情况下都是一个帧id对应一个电机或舵机,然后通过帧数据来控制电机和舵机的运动,我这里就简单演示一下没搞那么复杂,有需要的小伙伴可自行diy代码。对于电机的代码,我之前也发布过步进电机和无刷电机的控制代码文章,大家可自行结合之前的电机代码自行diy。

can.c

#include "stm32f10x.h"                  // Device header
#include "MyCAN.h"
#include "delay.h"CanRxMsg MyCAN_RxMsg;
uint8_t MyCAN_RxFlag;void MyCAN_Init(void)
{CAN_DeInit(CAN1);// 先关闭CANdelay_ms(10);//延时一会RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);  // 必须开启AFIO时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // PB8, PB9 时钟GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;//can_txGPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;//can_rxGPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_PinRemapConfig(GPIO_Remap1_CAN1, ENABLE); //PB8, PB9 重映射can功能//开启中断CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStructure);delay_ms(5);//延时一会CAN_InitTypeDef CAN_InitStructure;CAN_StructInit(&CAN_InitStructure);CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;//LoopBack    NormalCAN_InitStructure.CAN_Prescaler = 48;		//波特率 = 36M / 48 / (1 + 2 + 3) = 125KCAN_InitStructure.CAN_BS1 = CAN_BS1_2tq;CAN_InitStructure.CAN_BS2 = CAN_BS2_3tq;CAN_InitStructure.CAN_SJW = CAN_SJW_2tq;CAN_InitStructure.CAN_NART = ENABLE;// 禁用自动重传,避免阻塞(不堵塞但第一次通信所需同步时间较长)CAN_InitStructure.CAN_TXFP = DISABLE;CAN_InitStructure.CAN_RFLM = DISABLE;CAN_InitStructure.CAN_AWUM = ENABLE;// 自动唤醒CAN_InitStructure.CAN_TTCM = DISABLE;CAN_InitStructure.CAN_ABOM = ENABLE;// 自动总线恢复CAN_Init(CAN1, &CAN_InitStructure);//过滤器配置CAN_FilterInitTypeDef CAN_FilterInitStructure;CAN_FilterInitStructure.CAN_FilterNumber = 0;CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;CAN_FilterInit(&CAN_FilterInitStructure);
}void MyCAN_Transmit(CanTxMsg *TxMessage)
{uint8_t TransmitMailbox = CAN_Transmit(CAN1, TxMessage);uint32_t Timeout = 0;while (CAN_TransmitStatus(CAN1, TransmitMailbox) != CAN_TxStatus_Ok){Timeout ++;if (Timeout > 100000){break;}}
}void USB_LP_CAN1_RX0_IRQHandler(void)
{if (CAN_GetITStatus(CAN1, CAN_IT_FMP0) == SET){CAN_Receive(CAN1, CAN_FIFO0, &MyCAN_RxMsg);MyCAN_RxFlag = 1;}
}

因为我使用的板子A11和A12引脚连接了LED会影响can总线通信,故重映射了B8和B9的can引脚,一样的不影响使用,此外我在环回模式下单设备测试时没任何问题,但普通模式下两设备通信时会出现通信堵塞的现象,故将can的NART配置成了ENABLE后不堵塞了但是两个设备间第一次通信的同步所需要的时间会有点长,但不影响使用,如果有大佬能够解决欢迎评论区留言。

can.h

#ifndef __MYCAN_H
#define __MYCAN_Hvoid MyCAN_Init(void);
void MyCAN_Transmit(CanTxMsg *TxMessage);
void MyCAN_Init(void);
extern CanRxMsg MyCAN_RxMsg;
extern uint8_t MyCAN_RxFlag;#endif

还有按键,oled,led,舵机等的代码这里就不一一列举了,有需要的朋友可一键三联后私聊获取完整源码。此次分享就到这里了,感谢支持!

http://www.dtcms.com/a/487168.html

相关文章:

  • 图解AES密钥扩展与加密过程
  • ps如何做网站轮播图泰安企业建站公司哪里找
  • vue疑难解答
  • 【检索:Top K】12、非精准Top K检索权威指南:搜索引擎排序加速的核心技术与实战实现
  • 顺德品牌网站建设优惠做好公众号 网站建设
  • nullptr vs NULL:C/C++ 空指针的演变史
  • 深圳企业网站制作公司介绍网站防盗链设置
  • 搜索别人的网站是带logo的请问怎么做的精准广告投放
  • latex中自定义公式编号
  • 苏州正规做网站公司网站建设初期的需求分析
  • codetop高频(1)
  • LeetCode 994. 腐烂的橘子
  • 网站设计师需要什么知识与技能咸宁网站建设哪家专业
  • Python爬取百度地图-前端直接获取
  • thumos14数据集学习
  • 持续集成/持续部署(CI/CD)
  • HTTPS 的加密过程~
  • 用服务器ip做网站不配置iis做网站
  • 成都企业网站建设哪家好做网站模板链接放哪里
  • 网站手机端做app开发工具网站开发图标
  • 华为OD-21届考研-Java面经
  • PyTorch 实现多种 CNN 模型并采用集成方法提升 CIFAR-10 分类性能
  • 网站建设常用软件宣传链接用什么软件
  • 合肥响应式网站开发方案wordpress 本地 搭建网站
  • 国内做游戏破解的网站专门做2手手机的网站
  • 深度学习进阶(二)——视觉与语言的融合:多模态模型的架构演化
  • html做的旅游网站wordpress按作者分类
  • Jmeter 线程组、定时器、监听器、后置处理器常用配置说明
  • Jmeter分布式集群搭建与使用
  • 【Pytorch】MLP反向传播