Написание драйверов устройств для микроконтроллеров, где определить выводы портов ввода-вывода? - PullRequest
3 голосов
/ 27 мая 2010

Я всегда сталкиваюсь с этой дилеммой при написании низкоуровневого кода для MCU. Я никогда не знаю, где объявлять определения выводов, чтобы сделать код максимально пригодным для повторного использования.

В этом случае я пишу драйвер для подключения 8051 к MCP4922 12-битному последовательному ЦАП . Я не уверен, как / где я должен объявить определения выводов для CS (выбор чипа) и LDAC (защелка данных) для ЦАП. На данный момент там объявлен файл заголовка для драйвера.

Я провел много исследований, пытаясь выяснить лучший подход, но на самом деле ничего не нашел.

Я в основном хочу знать, какие лучшие практики ... если есть какие-то книги, которые стоит прочитать или информацию в Интернете, примеры и т. Д., Любые рекомендации будут приветствоваться.

Просто фрагмент драйвера, чтобы вы поняли

/**
    @brief  This function is used to write a 16bit data word  to DAC B -12 data bit plus 4 configuration bits
    @param  dac_data A 12bit word 
    @param  ip_buf_unbuf_select Input Buffered/unbuffered  select bit. Buffered = 1; Unbuffered = 0
    @param  gain_select Output Gain Selection bit. 1 = 1x (VOUT = VREF * D/4096).  0 =2x (VOUT = 2 * VREF * D/4096)
*/
void MCP4922_DAC_B_TX_word(unsigned short int dac_data, bit ip_buf_unbuf_select, bit gain_select)
{                                             

    unsigned char low_byte=0, high_byte=0;
    CS = 0;                                               /**Select the chip*/

    high_byte |= ((0x01 << 7) | (0x01 << 4));            /**Set bit to select DAC A and Set SHDN bit high for DAC A active operation*/
    if(ip_buf_unbuf_select) high_byte |= (0x01 << 6);
    if(gain_select)         high_byte |= (0x01 << 5);

    high_byte |= ((dac_data >> 8) & 0x0F);
    low_byte |= dac_data;
    SPI_master_byte(high_byte);
    SPI_master_byte(low_byte);

    CS = 1;                                               
    LDAC = 0;                                             /**Latch the Data*/
    LDAC = 1;                                         
}

Ответы [ 4 ]

3 голосов
/ 27 мая 2010

Это то, что я сделал в аналогичном случае, этот пример для написания драйвера I²C:

// Structure holding information about an I²C bus
struct IIC_BUS
{
    int pin_index_sclk;
    int pin_index_sdat;
};

// Initialize I²C bus structure with pin indices
void iic_init_bus( struct IIC_BUS* iic, int idx_sclk, int idx_sdat );

// Write data to an I²C bus, toggling the bits
void iic_write( struct IIC_BUS* iic, uint8_t iicAddress, uint8_t* data, uint8_t length );

Все индексы выводов объявляются в зависимом от приложения заголовочном файле для быстрого обзора, например ::

// ...
#define MY_IIC_BUS_SCLK_PIN 12
#define MY_IIC_BUS_SCLK_PIN 13
#define OTHER_PIN 14
// ...

В этом примере реализация шины I²C полностью переносима. Это зависит только от API, который может записывать в контакты чипа по индексу.

Edit:

Этот драйвер используется так:

// main.c
#include "iic.h"
#include "pin-declarations.h"

main()
{
    struct IIC_BUS mybus;
    iic_init_bus( &mybus, MY_IIC_BUS_SCLK_PIN, MY_IIC_BUS_SDAT_PIN );

    // ...

    iic_write( &mybus, 0x42, some_data_buffer, buffer_length );
}
2 голосов
/ 29 июля 2010

Ответ с конфигурацией во время выполнения будет работать для приличного процессора, такого как ARM, PowerPC ... но автор здесь запускает 8051. #define, вероятно, лучший способ пойти. Вот как я бы это сломал:

blah.h:

#define CSN_LOW()   CS = 0
#define CSN_HI()    CS = 1
#define LATCH_STROBE() \
 do { LDAC = 0; LDAC = 1; } while (0)

blah.c:
#include <blah.h>
void blah_update( U8 high, U8 low ) 
{
   CSN_LOW();
   SPI_master_byte(high);
   SPI_master_byte(low);
   CSN_HI();
   LATCH_STROBE();
} 

Если вам нужно изменить определение вывода или переместить его на другой процессор, должно быть очевидно, где вам нужно обновить. И это также помогает, когда вам нужно настроить синхронизацию на шине (то есть вставить задержку здесь и там), так как вам не нужно менять повсюду. Надеюсь, это поможет.

2 голосов
/ 28 мая 2010

Если только драйвер должен знать о выводе CS, то объявление должно появляться не в заголовке, а в самом модуле драйвера. Повторное использование кода лучше всего обслуживать, скрывая данные в максимально ограниченном объеме.

В случае, когда внешнему модулю необходимо управлять CS, добавьте функцию доступа к модулю драйвера устройства, чтобы у вас было управление одной точкой. Это полезно, если во время отладки вам необходимо знать, где и когда применяется вывод ввода-вывода; у вас есть только одна точка для применения контрольно-измерительных приборов или контрольных точек.

2 голосов
/ 28 мая 2010

В одном магазине, где я работал, определения выводов были помещены в заголовочный файл для конкретного процессора. В другом магазине я разбил заголовочные файлы на темы, связанные с модулями процессора, такие как DAC, DMA и USB. Главный включаемый файл для процессора включает все эти тематические заголовочные файлы. Мы могли бы моделировать разные варианты одного и того же процессора, включив в файл процессора разные заголовочные файлы модуля.

Вы можете создать заголовочный файл реализации. Этот файл будет определять выводы ввода / вывода в терминах файла заголовка процессора. Это дает вам один уровень абстракции между вашим приложением и оборудованием. Идея состоит в том, чтобы максимально свободно связать приложение с аппаратным обеспечением.

...