使用 xc8 编译器和 gpio 引脚初始化 C 中的驱动程序



假设我有一个驱动程序"foo.h"和"foo.c"实现。驱动器与微控制器上的 gpio 引互。

在Arduino中,每个GPIO引脚都有自己的引脚编号(只是一个整数(。然后你可以用函数 initFoo(1( 或 initFoo(2( 初始化 foo,以选择你要使用的引脚。

在 Mbed 在线 c++ 编译器中,每个 GPIO 都可以使用 DigitalIn 或 DigitalOut 对象进行控制。

在我也熟悉的JAL(只是另一种语言(中,他们使用别名关键字。

alias     x is pin_D3   -- the gpio pin called x used by the library
pin_D3_direction   = output 
include foo        -- jal doesn't use header files. 
foo_init()

如何使用Microchip xc8 c编译器实现类似的功能? 我尝试了带有定义关键字的 JAL 方法,但编译器不知道"x"是什么。它说未定义的标识符"x"... ?

#define x PortDbits.RD0 
#include "foo.h"
#foo_init();

我使用了两种解决方案。请记住,精简版中的 xc8 是一个可怕的编译器,会产生奇怪的、冗长的、未优化的、充满了恐怖的汇编代码,所以立即切换到 gcc+stm32 并将 xc8 扔进垃圾桶。

您可以创建自定义容器/类来与引互,就像arduino与DigitalIn类一样。我们可以使用指向 PORTA 或 PORTB 或 PORTX 寄存器的指针以及该寄存器内的位位置来存储引脚的句柄。然后,我们可以使用简单的位运算计算所需的位掩码来设置引脚。在我早期的编程日子里,我这样做了,你可以浏览源代码pinpointer.h。基本上它的工作原理是这样的:

pinpointer_t pp = PP_RB7;  // this is PORTBbits.RB7
PP_SET_PIN_AS_OUTPUT(pp); // set RB7 as output, equal to TRISBbits.RB7 = 0
PP_WRITE_PIN(pp, 1); // output 1 on this pin, equal to PORTBbits.RB7 = 1

pinpointer_t有 2 个字节。第一个字节是{PORT,LAT,TRIS}{A,B,C,...}的位置。现在,微芯片产生微控制器,以便端口寄存器一个接一个,PORTB - PORTA = 1第一个字节存储需要添加到PORTA以获取端口地址的数字。我们可以在这里存储一个指针,但这会使pinpointer_t有 4 个或更多的字节。 第二个字节使用位掩码存储引脚位置。现在PP_RB7等于 o0x180,即。第二个端口PORTA + 1 = PORTB0x80位掩码,即第 7 位。PP_SET_PIN_AS_OUTPUT(PP_RB7)大致翻译为像((TRISA) + (PP_RB7>>8)) &= ~PP_RB7 )一样的smth。
知道引脚号永远不会大于 7 并且永远不会超过 7 个端口,我们可以使用单字节存储相同的信息(前 4 位用于端口,后 4 位用于引脚(,但我发现 XC8 免费版本编译器为移位操作(如PP_RB7>>4(生成 **** 代码,通常使用 2 个字节导致代码较小。
所以你的 foo 库可能看起来像这样:

// foo.c
void foo_init(void) {
PP_SET_PIN_AS_OUTPUT(foo_pin);
}
// foo.h
extern pinpointer_t foo_pin;
void foo_init(void);
// main.c
pinpointer_t foo_pin = PP_RB7;
void main() {
foo_init(void);
}

但是没有全局变量会更好:

// foo.c
void foo_init(struct foo_s *foo, pinpointer_t pin) {
foo->pin = pin;
PP_SET_PIN_AS_OUTPUT(foo->pin);
}
// foo.h
struct foo_s {
pinpointer_t pin;
};
void foo_init(struct foo_s *foo, pinpointer_t pin);
// main.c
void main() {
struct foo_s foo;
foo_init(&foo, PP_RB7);
}

另一种解决方案是使用宏。宏生成明显更小、更快的代码,但维护起来很糟糕。我会创建一个通用的*-config文件,因为宏不是由编译器决定的,而是由处理器决定的。错误很常见。我会这样做:

// foo.c
#include <foo.h>
#ifndef FOO_CALLBACK_SET_PIN_AS_OUTPUT
#error You need to define FOO_CALLBACK_SET_PIN_AS_OUTPUT
#endif
void foo_init(void) {
FOO_CALLBACK_SET_PIN_AS_OUTPUT();
}
// foo.h
#include <foo-config.h>
void foo_init(void);
// foo-config.h
#define FOO_CALLBACK_SET_PIN_AS_OUTPUT  do{ TRISBbits.RB7 = 1; }while(0)
// main.c
#include <foo.h>
int main() {
foo_init();
}

foo-config.h是使用您的项目创建的文件,其中包含 foo 库使用的宏的定义。我曾经为 hd44780 编写了一个库,对于所有 gpio/pin 操作,它都使用宏回调。应用程序需要使用配置将包含路径添加到编译过程。

这是因为"x"实际上没有在 foo 库范围内定义。

这仅在直接包含不需要头文件的源 (.c( 文件时才有效。

#define x PortDbits.RD0 
#include "foo.c"
#foo_init();

最好有另一个标头来定义 GPIO 引脚。例如 GPIO_x.h

然后在里面。

#ifndef GPIO_X_H
#define GPIO_X_H
#include "MCUXXXXX.h"
#define x PortDbits.RD0 
#endif

在此之后,只需将此文件包含在 FOO.c 中即可

//this is foo.c
#include "GPIO_x.h
#include "foo.h"
/* your codes */

相关内容

  • 没有找到相关文章