инициализировать драйвер в C с компилятором xc8 с помощью выводов gpio - PullRequest
0 голосов
/ 03 сентября 2018

Допустим, у меня есть реализация драйвера "foo.h" и "foo.c". Драйвер взаимодействует с выводами gpio на микроконтроллере.

В Arduino каждый вывод GPIO имеет свой собственный номер вывода (просто целое число). Затем вы можете инициализировать foo с помощью функции initFoo (1) или initFoo (2), чтобы выбрать пин-код, который вы хотите использовать.

В онлайн-компиляторе c ++ Mbed каждый GPIO может управляться с помощью объекта DigitalIn или DigitalOut.

В JAL (просто другом языке), с которым я также знаком, они используют ключевое слово 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? Я попробовал подход JAL с ключевым словом define, но компилятор не знает, что такое «x». Там написано неопределенный идентификатор "х" ...?

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

Ответы [ 2 ]

0 голосов
/ 04 сентября 2018

Это потому, что '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 */
0 голосов
/ 03 сентября 2018

Есть два решения, которые я использовал. Помните, что 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

A 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 ).
Зная, что число выводов никогда не превышает 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 использовала обратные вызовы макросов. Приложению необходимо добавить включаемый путь к процессу компиляции с конфигурацией .

...