嵌入式硬件寄存器操作层次
寄存器操作—>基于CMSIS-CORE操作—>基于硬件抽象层hal操作—>基于API操作(针对hal api再次作了面向对象的封装)
1.寄存器操作
基于寄存器操作的闪灯程序
int main()
{
unsigned int mask_pin18 = 1 << 18;
volatile unsigned int *port1_dir = (unsigned int
*)0x2009C020;
volatile unsigned int *port1_set = (unsigned int
*)0x2009C038;
volatile unsigned int *port1_clr = (unsigned int
*)0x2009C03C;
*port1_dir |= mask_pin18;
while (true)
{
*port1_set |= mask_pin18;
wait(0.5);
*port1_clr|= mask_pin18;
wait(0.5);
}
}
将寄存器的地址赋值给指针,通过地址指针操作寄存器的相关功能位
2.基于CMSIS-CORE操作
前面的代码已经表明,基于MCU寄存器的应用程序其大量代码是寄存器地址的定义,为了减少用户的代码量,ARM公司已经要求各个厂商把这部分代码写成统一的接口格式给用户使用,这就是CMSIS-CORE的目标.
如在类似kinetis MK64F12.h 和STM32 stm32f103.h 文件中,你可以找到以下相关代码.
#define PORTA_BASE (0x40049000u)
/
* Peripheral PORTA base pointer /
#define PORTA ((PORT_Type *)PORTA_BASE)
#define PORTA_BASE_PTR (PORTA)
/** PORT - Register Layout Typedef */
typedef struct {
__IO uint32_t PCR[32]; /**< Pin Control Register n, array offset: 0x0, array step: 0x4 */
__O uint32_t GPCLR; /**< Global Pin Control Low Register, offset: 0x80 */
__O uint32_t GPCHR; /**< Global Pin Control High Register, offset: 0x84 */
uint8_t RESERVED_0[24];
__IO uint32_t ISFR; /**< Interrupt Status Flag Register, offset: 0xA0 */
uint8_t RESERVED_1[28];
__IO uint32_t DFER; /**< Digital Filter Enable Register, offset: 0xC0 */
__IO uint32_t DFCR; /**< Digital Filter Clock Register, offset: 0xC4 */
__IO uint32_t DFWR; /**< Digital Filter Width Register, offset: 0xC8 */
} PORT_Type, *PORT_MemMapPtr;
这样一来,我们就可以基于CMSIS-CORE使用以下代码来完成我们的功能了。
int main()
{
unsigned int mask_pin18 = 1 << 18;
while (true) {
PORTA->FIOSET |= mask_pin18;
wait(0.5);
PORTA->FIOCLR |= mask_pin18;
wait(0.5);
}
}
3.基于硬件抽象层hal操作
虽然从代码量上来说,基于CMSIS-CORE的闪灯程序已经比原先的改进了不少,但操作方式上并没有任何变化,用户依然需要理解寄存器的相关概念,而对于应用程序开发人员来说,他们更能理解的是SDK,即各种各样得函数调用,于是,mbed在cmsis-core的基础之上专门设计了一个硬件抽象层,把MCU的所有寄存器操作都整合成hal api函数调用方式,对于应用开发人员来说,他只需要调用hal api即可,而对于底层开发人员来说,他只需要实现hal api即可。
Hal api的所有函数原型都在hal目录下,与闪灯相关的gpio 函数原型如下:
uint32_t gpio_set(PinNamepin);
void gpio_init (gpio_t *obj,PinName pin, PinDirection direction);
void gpio_mode (gpio_t *obj,
PinMode mode);
void gpio_dir (gpio_t *obj, PinDirection direction);
void gpio_write(gpio_t *obj,int value);
int gpio_read (gpio_t *obj);
这样一来,我们就可以基于hal api使用以下代码来完成我们的功能了。
int
main() {
gpio_set(LED1);
gpio_t gpio;
gpio_init(&gpio, LED1, PIN_OUTPUT);
while (true)
{
gpio_write(&gpio,1);
wait(0.5);
gpio_write(&gpio,0);
wait(0.5);
}
}
4.基于API操作(针对hal api再次作了面向对象的封装)
基于hal api操作的应用程序虽然已经可以做到与具体的mcu无关了,但习惯了java,c#程序设计的程序员来说还是不太习惯,于是,mbed又针对hal api再次作了面向对象的封装,使得用户可以更简单地来编写上层应用,它对应的函数定义在api目录下,相应的实现在common目录下,闪灯程序相关的定义在DigitalOut.h中,其中也包括了对应的实现,代码如下:
class DigitalOut
{
public:
DigitalOut(PinName pin) {
gpio_init(&gpio, pin, PIN_OUTPUT);
}
void write(int value) {
gpio_write(&gpio, value);
}
int read() {
return gpio_read(&gpio);
}
#ifdef
MBED_OPERATORS
DigitalOut& operator= (int value) {
write(value);
return *this;
}
DigitalOut& operator= (DigitalOut& rhs) {
write(rhs.read());
return *this;
}
operator int() {
return read();
}
#endif
protected:
gpio_t gpio;
};
这样一来,我们就可以基于mbed api使用以下代码来完成我们的功能了。
DigitalOut led1(LED1);
int main()
{
while (true)
{
led1 = 1;
wait(0.5);
led1 = 0;
wait(0.5);
}
}