ОСРВ MULTEX-ARM
Руководство программиста
Аппаратные интерфейсы

GPIO – Порты ввода/вывода

См. также
Описание методов работы с портами ввода/вывода в файле gpio.h.

Для работы с портами общего назначения как с простыми линиями ввода/вывода их следует сконфигурировать — настроить мультиплексор используемой линии, подключив её к регистру чтения или записи. Такая настройка выполняется с помощью функций gpio_direction_input() и gpio_direction_output(). После настройки уровень сигнала на линии может быть считан с помощью gpio_get_value() или выставлен с помощью gpio_set_value(). Линии, сконфигурированные как входные, могут быть подтянуты к нулю или плюсу питания с помощью функций gpio_pull_up() и gpio_pull_down(). Выбор конфигурируемой линии осуществляется как сумма имени порта из набора макросов и номера линии.

В следующем примере PE1 настраивается как выход с установкой высокого уровня на линии. Далее уровень изменяется на низкий.

// ...
#define P_E
Definition: gpio.h:85
int gpio_direction_output(unsigned gpio, int value)
Сконфигурировать пин порта как выход и выставить заданное значение.
int gpio_set_value(unsigned gpio, int value)
Выставить значение на линии вывода.

Пример настройки PD5 как линии ввода с подтяжкой к плюсу питания.

int gpio_direction_input(unsigned gpio)
Сконфигурировать пин порта как вход.
#define P_D
Definition: gpio.h:84
int gpio_pull_up(unsigned gpio)
Подтянуть линию к плюсу питания.

Для подключения линий портов ввода/вывода к аппаратным модулям процессора используется функция gpio_set_mux(). Эта же функция может использоваться для настройки линий как входных и выходных, так как предоставляет возможность управления мультиплексором в общем виде. Для настройки мультиплексора каждой линии порта используется 4 бита, а следовательно линия может иметь до 16 вариантов подключения. Возможные значения мультиплексора каждой линии описаны в документации на используемый процессор.

Пример подключения линии PB4 к выходу PWM0 модуля ШИМ. В данном примере используется значение мультиплексора PWM_MUX, определённое как 2.

gpio_set_mux (P_B+4, PWM_MUX);
#define P_B
Definition: gpio.h:82
int gpio_set_mux(unsigned int gpio, int mux)
Сконфигурировать пин порта.

PWM (ШИМ)

См. также
Описание методов работы с линиями вывода ШИМ в файле pwm.h.

Поддержка аппаратных модулей ШИМ в MULTEX-ARM реализована в двух режимах — импульсном и непрерывном. Для каждого режима необходимо сначала вызвать функцию инициализации аппаратного модуля для выбранного канала. Далее в непрерывном режиме следует указать коэффициент заполнения ШИМ. Изменяя коэффициент заполнения в непрерывном режиме можно, например, управлять яркостью дисплея. В импульсном режиме для запуска каждого импульса следует вызывать запускающую функцию. Подробное описание функций можно найти в файле pwm.h.

В различных процессорах для выходных линий ШИМ используются разные пины. Конфигурация линий конкретного процессора считывается при старте операционной системы. И далее, при вызове функции инициализации для одного из каналов ШИМ, соответствующий каналу вывод процессора будет настроен должным образом.

SPI

См. также
Описание методов работы с интерфейсом SPI в файле spi.h.

Шина SPI - последовательный синхронный интерфейс передачи данных в режиме полного дуплекса, предназначенный для обеспечения простого высокоскоростного сопряжения микроконтроллеров и периферии. SPI также иногда называют четырёхпроводным (four-wire) интерфейсом.

SPI является синхронным интерфейсом, в котором любая передача синхронизирована с общим тактовым сигналом, генерируемым ведущим устройством (процессором). Принимающая (ведомая) периферия синхронизирует получение битовой последовательности с тактовым сигналом. К одному последовательному периферийному интерфейсу ведущего устройства может присоединяться несколько ведомых. Ведущее устройство выбирает ведомое для передачи, активируя сигнал CS (Chip Select) на ведомой микросхеме. Периферия, не выбранная процессором, не принимает участия в передаче по SPI. В процессорах Allwinner может содержаться до четырёх аппаратных интерфейса SPI и для каждого из них предусмотрено до четырёх сигналов CS. Число линий CS может быть увеличено путём программного использования линий ввода/вывода общего назначения GPIO.

Интерфейс SPI использует следующие сигналы:

  • SCLK – тактовый сигнал (от ведущего к ведомому);
  • MOSI – данные, передаваемые (от ведущего к ведомому);
  • MISO – принимаемые данные (от ведомого к ведущему);
  • CSn – выбор абонента (от ведущего к выбираемому ведомому).

В MULTEX-ARM реализован драйвер, позволяющий работать с несколькими интерфейсами SPI в режиме ведущего устройства (MASTER). Для подключения одного из имеющихся интерфейсов SPI в MULTEX-ARM нужно инициализировать нужный модуль с помощью spiInit() и затем вызывать функцию spiTransfer() для запуска цикла обмена данными с выбранным устройством. Для подключения аппаратных линий CS следует использовать функцию инициализации spiInitChipSelectLine() для каждой используемой линии. При подключении выбранного аппаратного модуля SPI будут использованы настройки интерфейса по умолчанию. Для изменения настроек следует воспользоваться функциями из соответствующей группы. Изменять данные настройки можно перед каждым циклом обмена данными с устройством. Это может понадобится, если к одному интерфейсу подключены устройства с разными параметрами передачи данных.

Ниже приведён пример инициализации интерфейса SPI0 и получение данных от подключенного устройства. При инициализации выбирается основной набор линий ввода/вывода и подключается линия выбора ведомого устройства CS0. Тактовая частота CLK устанавливается равной 4 МГц. Остальные настройки используются по умолчанию. Далее выполняется чтение из устройства по адресу 0x10 (это частный случай определённый протоколом обмена конкретного устройства). Для этого в первый байт массива данных кладётся нужный адрес и задаётся длина значащих передаваемых байт равная единице. Остальные передаваемые данные будут иметь нулевое значение. Такое поведение задаётся аппаратным модулем SPI. Общее количество обменов задаётся равным трём. После получения первого байта ведомое устройство начнёт передачу данных, таким образом прочитанное значение после окончания обмена будет лежать в последних двух байтах массива.

// read from 0x10
char data[3];
data[0] = 0x10;
spiTransfer (SPI_0, SPI_CS_0, data, 1, 3);
#define SPI_CS_0
Definition: spi.h:118
STATUS spiSetClkFrequency(unsigned int n, unsigned int f)
Задать частоту тактового сигнала CLK на шине SPI.
STATUS spiInitChipSelectLine(unsigned int n, unsigned int gpion, unsigned int csn)
Задействовать аппаратное управление линий CS.
STATUS spiInit(unsigned int n, unsigned int gpion)
Инициализация аппаратного модуля SPI.
STATUS spiTransfer(unsigned int n, unsigned int csn, void *data, unsigned int txLength, unsigned int totalLength)
Запуск обмена по шине данных SPI.
#define SPI_0
Definition: spi.h:107
#define SPI_GPIO_DEFAULT
Definition: spi.h:129

I2C

См. также
Описание методов работы с интерфейсом I2C в файле i2c.h.

Интерфейс I2C — это широко распространенный последовательный синхронный полудуплексный двухпроводный внутрисхемный интерфейс, используемый для управления многими устройствами, например, CMOS видеокамерами, или часами реального времени RTC.

В составе встроенных аппаратных модулей ARM процессоров Allwinner может содержаться до пяти контроллеров последовательных шин I2C. В ОС MULTEX-ARM для подключения драйвера одного из аппаратных интерфейсов следует вызвать функцию инициализации i2cInit() с указанием тактовой частоты сигнала CLK. Как правило, устройства на шине I2C работают на частотах 100 или 400 кГц и в этих случах для выбора частоты шины можно воспользоваться значениями из группы макросов. На самом деле при инициализации можно использовать и другие частоты работы шины. В этом случае частоту следует указать в виде числа в герцах. Далее можно читать и писать в подключенные по данному интерфейсу устройства по адресу с помощью функций i2cRead() и i2cWrite(). Адрес устройства должен быть указан в документации на само устройство.

Ниже приведён пример инициализации драйвера интерфейса I2C0 и запись массива данных в устройство по адресу 0x10.

char data[5];
// ...
i2cWrite (I2C_0, 0x10, data, sizeof (data));
bool i2cInit(unsigned int n, unsigned int set, unsigned int clk)
Инициализация канала I2C.
#define I2C_GPIO_DEFAULT
Definition: i2c.h:79
#define I2C_CLK_400_kHz
Definition: i2c.h:92
#define I2C_0
Definition: i2c.h:65
STATUS i2cWrite(unsigned int n, unsigned int addr, const void *data, unsigned int len)
Отправка данных по шине I2C в режиме ведущего устройства.

UART

См. также
Описание методов работы с последовательным портом UART в файле uart.h.

В состав MULTEX-ARM включен драйвер, обеспечивающий работу с последовательными портами UART в режиме потоковой записи / чтения. После настройки порт следует открыть с помощью функции open(), которая вернет дескриптор выбранного устройства. Используя этот дескриптор, записывать и читать данные можно стандартными средствами ОС – функциями read() и write().

Ниже приведён пример использования драйвера для приёма и пересылки обратно принятых данных. В начале инициализируется выбранный для передачи аппаратный модуль UART2. При инициализации он получает стандартные настройки, некоторые из которых бывает необходимо изменить. В примере изменяется скорость передачи данных на 38400 бит в секунду. Так же имеет смысл изменить таймаут на приём данных, чтобы задача периодически получала управление при отсутствии данных. Таймаут на добавление данных в очередь отправки можно сделать нулевым (NO_WAIT), так как переполнений выходного буфера не предвидится. Размер буферов приёма и отправки можно было бы оставить без изменений, так как при создании каждому из них выделяется по 4096 байт, но на деле достаточно выделить под буферы память просто большую, чем ожидаемый размер пакетов. В примере буферы получают размер вдвое больший, чем нужно для приёма и передачи данных.

После инициализации аппаратного модуля происходит открытие устройства с помощью open(). В качестве имени устройства следует использовать функцию получения имени uartName(). После успешного открытия порта данные из порта можно читать порциями с помощью read() с указанием полученного дескриптора. Если данных достаточно они будут считаны сразу. Если данных не достаточно функция будет ждать накопления данных до истечения указанного при инициализации таймаута. По истечении заданного времени функция вернёт количество прочитанных байт. Если же указан таймаут WAIT_FOREVER – функция может не возвращать управление задаче разбора пакетов до накопления нужных данных. Отправку данных следует производить с помощью функции write() с указанием полученного при открытии дескриптора.

int maxLen = 32;
char data[maxLen];
int uartFd = open (uartName (UART_2), O_RDWR);
while (true) {
int len = read (uartFd, data, maxLen);
if (len)
write (uartFd, data, len);
else
printf ("no data\n");
}
int write(int fd, const void *buffer, int maxBytes)
Записать буфер в файл.
int open(const char *path, int flags)
Открыть файл или устройство.
#define O_RDWR
Definition: iolib.h:345
int read(int fd, void *buffer, int maxBytes)
Прочитать буфер из файла.
#define NO_WAIT
Definition: semlib.h:65
int printf(const char *restrict format,...)
STATUS uartSetBaudRate(unsigned int n, unsigned int baud)
Задать частоту передачи данных.
#define UART_2
Definition: uart.h:97
STATUS uartInit(unsigned int n, unsigned int gpion)
Настройка аппаратного модуля UART.
#define UART_BAUD_38400
Definition: uart.h:128
#define UART_GPIO_DEFAULT
Definition: uart.h:112
const char * uartName(unsigned int n)
Имя устройства в текстовом виде.
STATUS uartSetWriteBufferSize(unsigned int n, unsigned int size)
Настройка размера буфера отправки данных.
STATUS uartSetReadBufferSize(unsigned int n, unsigned int size)
Настройка размера приёмного буфера.
STATUS uartSetReadTimeout(unsigned int n, int timeout)
Настройка таймаута приёма данных.
STATUS uartSetWriteTimeout(unsigned int n, int timeout)
Настройка таймаута передачи данных.