STM32 ROS控制器底层代码讲解

2023-05-16

本文主要对控制器底层代码的整天架构进行讲解。

控制器由两部分组成一部分是BootLoader,另一部分是APP;BootLoader主要用于固件升级,APP则作为应用程序。

BootLoader的地址为:0x8000000~0x8008000

App的地址为:0x8010000~0x8FFFFFF

参数保存地址为:0x8008000~:0x8010000

BootLoader使用的是裸机,主要是对升级标志位进行判断然后对运行区进行跳转和通过UART1、UART2把APP固件写入,目前只支持通过UART1(PA9,PA10)、UART2(PD5,PD6),对于首次更新则只能使用UART1或者UART2对APP进行写入,BootLoader代码目录如下图所示。BootLoader代码目前是不开源的,由博主进行维护,大家在使用中遇到问题博主会及时进行测试修改和同步更新烧入文件。

APP是由FreeRTOS作为嵌入式系统,使用rosserial(roslib库)和ROS1进行交互,使用标准的接口协议,让ROS的开发者不需要去了解底层的通行层,只需要关注底层控制器发布和订阅的节点名称即可,使用rosserial还可以使用ros的参数服务器,在ROS层即可对底层的一些开放参数进行修改(使用方法参考博主的另一篇博客 通过ROS修改控制器参数:https://blog.csdn.net/qq_36349536/article/details/126515022?spm=1001.2014.3001.5501);除了通信部分控制器编写了D2(差速两轮)、D4(差速四轮)、T2、T4、O3、O4、M4、A1、A2等车型的运动学解算,PS2手柄驱动代码,MPU6050、MPU9250、MPU6500、GY85等IMU驱动代码、位置式PID代码,代码使用函数指针对应用层和驱动层进行了标准接口分离解耦每个文件的相互依赖,让用户可以更好的进行二次开发。下面主要对用户在二次开发过程中可能遇到的一些疑惑进行说明。

1、初始化入口讲解,初始化入口是用户任务代码和驱动代码的初始化位置,因为激活校准函数是授权使用的所以对main函数进行了封装,留出了任务代码入口和驱动代码入口,如下图所示:

用户在开发过程中任务初始化需要放到InitTaskArr[]中,驱动代码需要放到InitSetupArr[]中,InitLoopArr[]是作为运行逻机的预留接口,没用,用户无需对该数组进行操作。

2、roslib库的使用讲解,roslib库是rosserial生成过来用于嵌入式设备的一个库,具体的打包过程和怎么使用自定义msgs可以参考另一篇博客;roslib库主要是提供了3个函数接口,read(),wirte(),time(),我们只需要去实现这三条函数即可,接口函数是在stm32Hardware.h中。

我们需要先确定我们使用的STM32串口或者USB_VCP的收发函数是否正常,然后把实现好的函数在stm32Hardware.h文件中对应的位置加入即可。读写函数实现完成,我们还需要对这个库进行初始化

第一步创建一个nh句柄

ros::NodeHandle nh;

第二步对句柄进行初始化

nh.initNode();

第三步创建发布的msgs,订阅的回调函数,发布的节点名称和订阅的节点名称

starrobot_msgs::Imu        raw_imu_msg;
starrobot_msgs::Velocities raw_vel_msg;
starrobot_msgs::analog     raw_analog_msg;
starrobot_msgs::info_show  raw_info_show_msg;
starrobot_msgs::Sonar      raw_sonar_msg;
starrobot_msgs::Relaid     raw_Relaid_msg;
starrobot_msgs::Upgrader   raw_Up_msg;

void pid_callback( const starrobot_msgs::PID& pid);
void command_callback(const geometry_msgs::Twist& cmd_msg);
void swerve_callback(const std_msgs::Int16& swerve_servo);
void servo_callback(const starrobot_msgs::Servo& servo_data);
void relaid_callback(const starrobot_msgs::Relaid& data_t);
void upgrader_callback(const starrobot_msgs::Upgrader& data_t);
void handleParam_callback(const std_msgs::Int8& data_t);

ros::Subscriber<geometry_msgs::Twist> cmd_sub("cmd_vel", command_callback);
ros::Subscriber<starrobot_msgs::PID>  pid_sub("pid", pid_callback);
ros::Subscriber<std_msgs::Int16> swerve_sub("initial_angle",swerve_callback); 
ros::Subscriber<starrobot_msgs::Servo>servo_sub("servo",servo_callback);
ros::Subscriber<starrobot_msgs::Relaid>relaid_sub("setRelaid",relaid_callback); 
ros::Subscriber<std_msgs::Int8>handleParam_sub("handleParam",handleParam_callback); 
ros::Subscriber<starrobot_msgs::Upgrader>upgrader_sub("PubUpgrade",upgrader_callback); 

ros::Publisher raw_vel_pub("raw_vel", &raw_vel_msg);
ros::Publisher raw_imu_pub("raw_imu", &raw_imu_msg);
ros::Publisher raw_battery_pub("raw_analog", &raw_analog_msg);
ros::Publisher raw_info_show_pub("info_show", &raw_info_show_msg);
ros::Publisher raw_sonar_pub("sonar", &raw_sonar_msg);
ros::Publisher raw_relaid_pub("getRelaid", &raw_Relaid_msg);
ros::Publisher raw_upgrader_pub("SubUpgrade", &raw_Up_msg);

第四步对发布和订阅的节点名称进行注册

    nh.subscribe(cmd_sub);
    nh.subscribe(pid_sub);
    nh.subscribe(servo_sub);
    nh.subscribe(swerve_sub);
    nh.subscribe(relaid_sub);
    nh.subscribe(handleParam_sub);
    nh.subscribe(upgrader_sub);
    
    nh.advertise(raw_vel_pub);
    nh.advertise(raw_imu_pub);
    nh.advertise(raw_battery_pub);
    nh.advertise(raw_info_show_pub);
    nh.advertise(raw_sonar_pub);
    nh.advertise(raw_relaid_pub);
    nh.advertise(raw_upgrader_pub);

第五步在循环中进行等待即可完成roslib库的使用,大家可以根据自己的实际情况对节点进行修改增加和删除

for(;;){
        while (!nh.connected()){    
            nh.spinOnce();
            hStr_t = 1;
            vTaskDelay(300);
        }
        if(hStr_t){
            watchdogInit(600);
            initParam(&nh);
            ShowHardwareStr();
            vTaskDelay(100);
            showParamSet(&nh);
            setBaseClassInit();
            setBaseClassPid();
            imuDriveInit();
            configParam.IsRosNodePub = 1;
            setMotorDebugMaxRpm();
            hStr_t = 0;
            watchdogInit(WATCHDOG_RESET_MS);
        }
        if(IsParamShow){
            IsParamShow = 0;
            ShowHardwareStr();
            showParamSet(&nh);
        }
        nh.spinOnce();
        vTaskDelay(100);
    }

3、如何编写一个订阅节点,订阅节点第一步是需要编写一个回调函数,用于roslib库当接收到订阅的话题时进行一个响应;第二步需要编写订阅的节点名称和消息类型;第三步则是需要对订阅的节点进行注册。

void command_callback(const geometry_msgs::Twist& cmd_msg);//函数声明
ros::Subscriber<geometry_msgs::Twist> cmd_sub("cmd_vel", command_callback);//订阅的话题名称和消息类型
//回调函数的编写
void command_callback(const geometry_msgs::Twist& cmd_msg){
    stcTwist str_p;
    str_p.linearVelX = cmd_msg.linear.x;
    str_p.linearVelY = cmd_msg.linear.y;
    str_p.angularVelZ = cmd_msg.angular.z;
    if(false ==twistlinkSendPacket( &str_p))
        nh.loginfo("twistlinkSendPacket error");
}
nh.subscribe(cmd_sub);//回调函数的注册

4、如何编写一个发布节点,发布节点第一步需要创建发布的消息类型变量;第二步需要编写发布的节点名称和消息类型;第三步是编写一个函数对发布节点的消息类型数据进行处理和发布;第四步是需要对发布的节点进行注册。

starrobot_msgs::Imu        raw_imu_msg; //创建消息类型变量
ros::Publisher raw_imu_pub("raw_imu", &raw_imu_msg); //发布的节点的名称和消息类型
//对发布节点的消息类型数据处理和发布
void PublishImu(){
    stcATBuff sBatBuffr_p;
    if((configParam.IsRosNodePub != 0) && (upStatus==0)){
        switch(configParam.IMUType){   
            case 1:
                raw_imu_msg.linear_acceleration = imu_gy85.readAccelerometer();
                raw_imu_msg.angular_velocity = imu_gy85.readGyroscope();
                raw_imu_msg.magnetic_field = imu_gy85.readMagnetometer();
            break;
            case 2:
            case 3:
                raw_imu_msg.linear_acceleration = imu_mpu9250.readAccelerometer();
                raw_imu_msg.angular_velocity = imu_mpu9250.readGyroscope();
                raw_imu_msg.magnetic_field = imu_mpu9250.readMagnetometer();
            break;
            case 4:
            case 5:
                raw_imu_msg.linear_acceleration = imu_mpu6050.readAccelerometer();
                raw_imu_msg.angular_velocity = imu_mpu6050.readGyroscope();
                raw_imu_msg.magnetic_field = imu_mpu6050.readMagnetometer();
            break;
            case 6:
            case 7:
                raw_imu_msg.linear_acceleration = imu_mpu6500.readAccelerometer();
                raw_imu_msg.angular_velocity = imu_mpu6500.readGyroscope();
                raw_imu_msg.magnetic_field = imu_mpu6500.readMagnetometer();
            break;
            default:break;
        }
        raw_imu_pub.publish(&raw_imu_msg);
    }    
} 

4、如何定义自己的硬件,底层代码是做了接口解耦合的,硬件定义的代码在HwConfig/hw_xxx.c文中,端口定义则在HwConfig/hw_xxx.h中大家可以根据自己的需要对HwConfig/hw_xxx.h文件里的端口进行修改,HwConfig/hw_xxx.c里面的函数名称最好不要修改,否则需要自己同步把调用该函数的地方全部进行修改才不会报错

5、如何编写一个任务,编写任务第一步是需要写一个任务函数;第二步是创建任务;第三步是把创建的任务初始化函数放到初始化函数入口数组中。

static void SysLedBeep_HTask(void *pvParameters){          //任务执行函数  
    for( ;; ){
        STARBOT_LED_RUN_Toggle();      
        vTaskDelay(300);
    }
}
void SysLedBeep_TaskInit(void){    
    LED_Init();
// 创建任务
    xTaskCreate(SysLedBeep_HTask,(const char *)"SysLedBeepHTask",128, NULL,LedBeep_Pri, NULL);
}
PrivateFun InitTaskArr[]={
    //函数名称
    SysLedBeep_TaskInit,//把任务初始化添加到任务初始化函数入口数组中
//    #ifndef STM32F10X_HD
    ros_task_create,
    OledShow_TaskInit,
    MoveBase_TaskInit,
    Terminal_TaskInit,
    usbLink_TaskInit,
    uart1_TaskInit,
    hcBle_TaskInit,
    uart3Link_TaskInit,
    uart4Link_TaskInit,
    uart5Link_TaskInit,
    ps2Manage_TaskInit,
    timeout_TaskInit,
    canLink_TaskInit,
    ErrorManage_TaskInit,
    Ws2812Run_TaskInit,
//    #endif
    NULL//这个NULL不能删除
};

欢迎大家进群一起交流学习:129923584

相关视频讲解链接:https://www.bilibili.com/video/BV1bD4y1g7V5/

博文使用的开发板购买链接:https://item.taobao.com/item.htm?spm=a1z10.1-c-s.w4004-21142386790.2.c0086177uv5IlA&id=606445592295

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32 ROS控制器底层代码讲解 的相关文章

  • STM32用一个定时器执行多任务写法

    文章目录 main c include stm32f4xx h uint32 t Power check times 电量检测周期 uint32 t RFID Init Check times RFID检测周期 int main Timer
  • STM32F103

    提示 来源正点原子 参考STM32F103 战舰开发指南V1 3PDF资料 文章目录 前言 一 pandas是什么 二 使用步骤 1 引入库 2 读入数据 总结 前言 提示 这里可以添加本文要记录的大概内容 开发环境硬件普中科技 接线图在g
  • 物联网网关

    物联网网关是 连接物联网设备和互联网的重要桥梁 它负责将物联网设备采集到的数据进行处理 存储和转发 使其能够与云端或其它设备进行通信 物联网网关的作用是实现物联网设备与云端的无缝连接和数据交互 物联网网关功能 数据采集 物联网网关可以从物联
  • STM32F103概要

    The STM32F103x4 STM32F103x6 STM32F103xC STM32F103xD and STM32F103xE are a drop in replacement for STM32F103x8 B medium d
  • 硬件基础-电容

    电容 本质 电容两端电压不能激变 所以可以起到稳定电压作用 充放电 电容量的大小 想使电容容量大 使用介电常数高的介质 增大极板间的面积 减小极板间的距离 品牌 国外 村田 muRata 松下 PANASONIC 三星 SAMSUNG 太诱
  • 跟着野火学FreeRTOS:第一段(任务定义,切换以及临界段)

    在裸机系统中 系统的主体就是 C P U CPU CP U 按照预先设定的程序逻辑在 m a i n
  • catkin_make 编译报错 Unable to find either executable ‘empy‘ or Python module ‘em‘...

    文章目录 写在前面 一 问题描述 二 解决方法 参考链接 写在前面 自己的测试环境 Ubuntu20 04 一 问题描述 自己安装完 anaconda 后 再次执行 catkin make 遇到如下问题 CMake Error at opt
  • catkin_make 编译报错 Unable to find either executable ‘empy‘ or Python module ‘em‘...

    文章目录 写在前面 一 问题描述 二 解决方法 参考链接 写在前面 自己的测试环境 Ubuntu20 04 一 问题描述 自己安装完 anaconda 后 再次执行 catkin make 遇到如下问题 CMake Error at opt
  • STM32 暂停调试器时冻结外设

    当到达断点或用户暂停代码执行时 调试器可以停止 Cortex 中代码的执行 但是 当皮质停止在暂停状态下执行代码时 调试器是否会冻结其他外设 例如 DMA UART 和定时器 您只能保留时间 r 取决于外围设备 我在进入主函数时调用以下代码
  • STM32F207 I2C 测试失败

    我正在使用 STM32F207 微控制器在 STM3220G EVAL 板上学习嵌入式开发 我尝试通过连接同一芯片上的两个 I2C2 和 I2C3 模块并发送 接收字符来测试 I2C 接口 这是我当前编写的代码 使用 mdk arm 5 i
  • STM32F4XX的12位ADC采集数值超过4096&右对齐模式设置失败

    文章目录 一 前言 二 问题1 数值超过4096 三 问题1的排错过程 四 问题2 右对齐模式设置失败 五 问题2的解决方法 5 1 将ADC ExternalTrigConv设置为0 5 2 使用ADC StructInit 函数 一 前
  • for循环延时时间计算

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 前言 一 pandas是什么 二 使用步骤 1 引入库 2 读入数据 总结 前言 之前做led点亮的实验 好像是被delay函数影响了 因为delay参数设置的不对
  • 库函数点亮Led

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 前言 一 pandas是什么 二 使用步骤 1 引入库 2 读入数据 总结 前言 提示 这里可以添加本文要记录的大概内容 例如 随着人工智能的不断发展 机器学习这门
  • 从没有中断引脚并且在测量准备好之前需要一些时间的传感器读取数据的最佳方法

    我正在尝试将压力传感器 MS5803 14BA 与我的板 NUCLEO STM32L073RZ 连接 根据 第 3 页 压力传感器需要几毫秒才能准备好读取测量值 对于我的项目 我对需要大约 10 毫秒来转换原始数据的最高分辨率感兴趣 不幸的
  • 如何从里程计/tf数据获取投影矩阵?

    我想将视觉里程计的结果与 KITTI 数据集提供的事实进行比较 对于地面中的每一帧 我都有一个投影矩阵 例如 1 000000e 00 9 043683e 12 2 326809e 11 1 110223e 16 9 043683e 12
  • 无法在 Ubuntu 20.04 上安装 ROS Melodic

    我正在尝试使用这些命令在 Ubuntu 20 04 上安装 ROS Melodic sudo sh c echo deb http packages ros org ros ubuntu lsb release sc main gt etc
  • HAL_Delay() 陷入无限循环

    我被 HAL Delay 函数困住了 当我调用此函数 HAL Delay 时 控制陷入无限循环 在寻找问题的过程中 我发现了这个 http www openstm32 org forumthread2145 threadId2146 htt
  • ROS中spin和rate.sleep的区别

    我是 ROS 新手 正在尝试了解这个强大的工具 我很困惑spin and rate sleep功能 谁能帮助我了解这两个功能之间的区别以及何时使用每个功能 ros spin and ros spinOnce 负责处理通信事件 例如到达的消息
  • 移动数组中的元素

    我需要一点帮助 我想将数组中的元素向上移动一个元素 以便新位置 1 包含位置 1 中的旧值 new 2 包含 old 1 依此类推 旧的最后一个值被丢弃 第一个位置的新值是我每秒给出的新值 我使用大小为 10 的数组 uint32 t TE
  • 读取STM32 MCU SPI数据寄存器的值

    有很多类似的问题 但似乎没有一个问题完全相同 我正在将 STML4 MCU 连接到 6 轴传感器 LSM6DS3 我已经成功地在 I2C 中实现了所有内容 但想要 SPI 的额外速度 和 DMA 如果我能让这些第一步工作起来的话 因此 第一

随机推荐