initialise driver in C with xc8 compiler with gpio pins

一世执手 提交于 2019-12-14 04:01:34

问题


Lets say I have a driver "foo.h" and "foo.c" implementation. The driver interacts with the gpio pins on a microcontroller.

In Arduino, each GPIO pin has it's own pin number (just an integer). Then you can initialise foo with the function initFoo(1) or initFoo(2), to choose the pin you want to use.

In Mbed online c++ compiler, each GPIO can be controlled with a DigitalIn or DigitalOut object.

In JAL (Just Another Language) that I'm also familiar with, they use the alias keyword.

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()

How do I achieve similar functionality with the Microchip xc8 c compiler? I tried the JAL approach with the define keyword, but the compiler doesn't know what 'x' is. It says undefined identifier 'x'... ?

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

回答1:


There are two solutions that i've used. Remember, that xc8 in the lite version is a horrible compiler and produces strange, long, unoptimized, filled with horror assembly code, so switch to gcc+stm32 right away and toss xc8 into the trash.

You can create a custom container/class to interact with the pin just like arduino does with DigitalIn classes. We can store a handle to a pin using pointer to the PORTA or PORTB or PORTX register and a bit position inside that register. Then we can calculate the needed bitmask to set the pin using simple bit operations. In my early programing days i did that and you can browse source pinpointer.h. Basically it works like this:

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

A pinpointer_t has 2 bytes. The first byte is the position of the {PORT,LAT,TRIS}{A,B,C,...}. Nowing that microchip produces microcontrolers so that port registers are one after another PORTB - PORTA = 1 the first byte stores the number that needs to be added to PORTA to get the address of the port. We could store a pointer here, but that would make pinpointer_t have 4 or maybe more bytes. The second byte stores the pin position using a bitmask. Now the PP_RB7 is equal o 0x180, ie. the second port PORTA + 1 = PORTB with 0x80 bitmask, that is the 7th bit. PP_SET_PIN_AS_OUTPUT(PP_RB7) translates roughly to smth like ((TRISA) + (PP_RB7>>8)) &= ~PP_RB7 ).
Knowing that the pin number is never greater then 7 and that there are never more than 7 ports, we can store the same information using single byte (first 4 bits for port, second 4 bits for pin), but i discovered that the XC8 free version compiler generates **** code for shift operations like PP_RB7>>4 and usually using 2 bytes resulted in smaller code.
So your foo library may look like this:

// 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);
}

But it's better without global variables:

// 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);
}

The other solution is to use macros. Macros produces significantly smaller and faster code, but are horrible to maintain. I would create a common *-config file, as macros are determined not by the compiler but by the proceprocessor. Errors are common. I would do it like this:

// 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();
}

The foo-config.h is a file created with your project that contains the definitions for macros used by the foo library. I have once written a library for hd44780 which for all gpio/pin operations used macro callbacks. The application needs to add an include path to the compilation process with the configuration.




回答2:


It is because the 'x' is really not defined in the foo library scope.

This only works when you have directly include the source (.c) file which do not need header file.

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

It is best if you have another header to define GPIO pins. For example GPIO_x.h

Then inside it.

#ifndef GPIO_X_H
#define GPIO_X_H

#include "MCUXXXXX.h"

#define x PortDbits.RD0 

#endif

after this, just include this file in foo.c

//this is foo.c

#include "GPIO_x.h
#include "foo.h"

/* your codes */


来源:https://stackoverflow.com/questions/52154443/initialise-driver-in-c-with-xc8-compiler-with-gpio-pins

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!