Skip to content

ChibiOS HAL代码编写指南

Wu Feiyang edited this page Feb 11, 2023 · 7 revisions

内容部分来源于playembedded网站,获取详细信息请登录:https://www.playembedded.org/blog/chibioshal-design-an-object-oriented-approach

一、ChibiOS-HAL库

ChibiOS HAL库架构 通过ChibiOS-HAL库架构可知,HAL库由用户顶层调用相关外设驱动(PAL,串口,CAN等),并由外设驱动调用底层驱动。 image

二、GPIO通用输入输出

在ChibiOS-HAL中,GPIO外设由PAL(Port Abstraction Layer, 端口抽象层)驱动。使用C++ wrapper并配置完成工程之后,我们可以使用如下代码:

#include "hal.h"    /*HAL库头文件*/
#inlcude "ch.hpp"   /*ChibiOS头文件*/

int main(){
    halInit();                            /*HAL库初始化*/
    chibios_rt::System::init();           /*ChibiOS系统初始化*/
}

其中当我们开启了hal_init()之后,系统会自动执行pal_init()的PAL初始化函数,因此无需在main函数中添加。

接下来我们介绍几个PAL的API。在PAL中, Port代表端口,即GPIOA,GPIOB等等,PAD代表引脚,例如1,2,3,4 ……

palSetPadMode(port,pad,mode);  /*GPIO引脚模式设置,如推挽输出*/
palSetPad(port,pad);           /*GPIO引脚电平置高电平*/
palClearPad(port,pad);         /*GPIO引脚电平置低电平*/
palTogglePad(port,pad);        /*GPIO引脚电平反转*/

其中,port参数可以填写GPIOx,例如GPIO1,GPIO2,GPIO3……

pad参数填写数字即可。

mode可以填写的内容一共有若干种,具体可以查阅源代码,最常用的是推挽输出模式:PAL_MODE_OUTPUT_PUSHPULL

三、串口驱动

讲到串口驱动,我们不得不首先提及一下HAL库外设的状态机(以SPI为例)。 image HAL库状态机 要启动一个HAL库外设,我们先要对我们的外设进行初始化。比如串口驱动是Serial Driver,简写作sd。但是当我们在halconf.h中打开了串口驱动相关的宏,hal_init()中就已经调用了sdInit()函数。因此我们要做的无非就是调用sdStart()函数。我们来看一下sdStart的定义:

msg_t sdStart(SerialDriver *sdp, const SerialConfig *config); 在ChibiOS移植的过程中,我们会修改hal_conf.h和mcu_conf.h这个头文件。在hal_conf.h中修改一个宏HAL_USE_SERIAL为TRUE,那么我们就在HAL中使用了串口。由于我使用的开发板的串口是USART1,所以在 mcu_conf.h中我将STM32_SERIAL_USE_USART1这个宏改为TRUE。阅读源码,这时候条件编译选项会把一个SerialDriver类型的驱动器SD1编译进去。因此,需要向sdStart中的第一个参数传入指向这个驱动器的指针,&SD1。第二个参数是配置结构体,该结构体会进一步配置USART对应的寄存器。如果输入NULL就会使用默认结构体。该串口的默认波特率为38400。

下面的代码展示了一个串口打印的程序,每隔500ms就会向串口打印 "Hello"。

#include "ch.hpp"
#include "hal.h"
#include "hal_serial.h"
#include "chprintf.h"

int main(){
    halInit();
    chibios_rt::System::init();
    sdStart(&SD1, NULL);            /* default baud rate 38400 */
    while(1){
        chprintf((BaseSequentialStream*)&SD1,"Hello\r\n");
        chThdSleepMilliseconds(500);        /* 500 ms delay*/
    }
}

四、CAN

接下来将以Can驱动器为例,讲解配置结构体的配置方法。

int main(){
    halInit();
    chibios_rt::System::init();
    CANConfig can_cfg = {
            CAN_MCR_ABOM | CAN_MCR_AWUM | CAN_MCR_TXFP,
            CAN_BTR_SJW(0) | CAN_BTR_TS2(3) |
            CAN_BTR_TS1(8) | CAN_BTR_BRP(2)
    };
    canStart(&CAND1, &can_cfg);
}

代码定义了一个CanConfig配置结构体。以下是CANConfig在"hal_can.h"中的定义:

typedef struct hal_can_config {
  /**
   * @brief   CAN MCR register initialization data.
   * @note    Some bits in this register are enforced by the driver regardless
   *          their status in this field.
   */
  uint32_t                  mcr;
  /**
   * @brief   CAN BTR register initialization data.
   * @note    Some bits in this register are enforced by the driver regardless
   *          their status in this field.
   */
  uint32_t                  btr;
} CANConfig;

该结构体有两个方法,第一个方法是MCR寄存器,第二个方法是BTR寄存器。CAN_MCR_ABOM等宏定义,有些是ST官方的宏定义,表示寄存器特定的位。而用位操作“|”运算符,则可以将所有不同位置上的1连接在一起。

这是STM32F4中文参考手册中提供的寄存器信息: image (1)

STM32F4中文参考手册,CAN的MCR寄存器 在官方提供的库文件中(stm32f4xx.h),CAN_MCR_ABOM的定义为:

#define  CAN_MCR_ABOM                        ((uint16_t)0x0040)            /*!<Automatic Bus-Off Management */

也就是说这个数字的二进制表示中的第六位是1。可以看到,ABOM值正好是MCR寄存器的第六位。那么把这个宏和其他的宏用位运算或连接在一起,可以最终达成配置寄存器的目的。

STM32F4对ABOM位的描述 当我们将这个宏给或运算进去,相当于将这位置为1,即一旦监测到 128 次连续 11 个隐性位,即通过硬件自动退出总线关闭状态。

如果我们使用Clion界面看这些代码,因为有自动提示功能,我们可以更方便地看到MCR和BTR寄存器的信息。

image (3)

具体该使用哪些宏,我们可以查询CAN的工作原理以及STM32参考手册。CAN驱动启动之后,我们就可以调用更多CAN的API了,不再赘述,相关内容查询Documentation和源代码。对于上述串口的配置也是类似,请大家自行捣鼓。

更新历史

Feb 11th, 2023 by Wu Feiyang, First Version

Clone this wiki locally