可以说我有一个驱动程序“foo.h”和“foo.c”实现。驱动器与微控制器上的gpio引脚交互。
在Arduino中,每个GPIO引脚都有自己的引脚号(只是一个整数)。然后,您可以使用函数initFoo(1)或initFoo(2)初始化foo,以选择要使用的引脚。
在Mbed在线c ++编译器中,可以使用DigitalIn或DigitalOut对象控制每个GPIO。
在我也熟悉的JAL(Just Another Language)中,他们使用alias关键字。
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编译器实现类似功能?我用define关键字尝试了JAL方法,但编译器不知道'x'是什么。它说未定义的标识符'x'......?
#define x PortDbits.RD0
#include "foo.h"
#foo_init();
我使用过两种解决方案。请记住,Lite版本中的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
等于o 0x180
,即。第二个端口PORTA + 1 = PORTB
,带有0x80位掩码,即第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
文件,因为宏不是由编译器决定的,而是由proceprocessor决定的。错误很常见。我会这样做:
// 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操作都使用宏回调。应用程序需要使用configuration添加编译过程的包含路径。
这是因为'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 */