ELT048 - SOE
Controladora de Drivers
Rodrigo Almeida
Universidade Federal de Itajubá
Revisão
● Sistema de tempo real
● Requisitos temporais
Driver
● Driver é uma abstração em software do
hardware da placa, suas funcionalidades,
opções e modos de funcionamento.
● É dependente do processador,dos
componentes conectados e das ligações
entre eles.
Criação de um driver
● Para a criação de um driver basta
encapsular estas funções num conjunto
organizado e de fácil acesso.
● Headers e defines
● A mudança do driver não deve gerar
nenhuma alteração no código da aplicação.
● A criação de um driver deve se concentrar
na função e não nos recursos do
dispositivo.
Padronização
Exercício
● Desenvolver um driver para o LCD
utilizando o modelo proposto
Controladora de Drivers
● É utilizada como uma interface entre o
kernel e os drivers
● É a responsável pela inicialização e
manutenção dos drivers
● Armazena informações sobre os drivers
carregados
● Funciona como uma camada de segurança,
evitando que comandos “errados” sejam
repassados aos drivers.
● A implementação a ser apresentada possui
apenas 2 funções:
● Uma para inicialização de um driver
– Além da inicialização deve fornecer um ID para o
driver.
● Uma para repassar os comandos da aplicação
para os drivers
Controladora de Drivers
● Para obter acesso à todos os drivers é
necessário que a controladora obtenha o
endereço de uma estrutura do tipo driver.
● Estáticamente:
● é montada uma lista com funções que retornam
esta estrutura.
● Dinâmicamente:
● recebe-se esta estrutura via comunicação
serial e retorna-se o endereço onde os dados
foram salvos.
Controladora de Drivers
static driver* driversLoaded[QNTD_DRV];
static char qntDrvLoaded;
char initDriver(char newDriver) {
char resp = FAIL;
if(dLoaded < QNTD_DRV) {
//obtem a estrutura do driver desejado
drivers[dLoaded] = drvInitVect[newDriver]();
//should test if driver was loaded correcly
resp = drivers[dLoaded]->drv_init(&newDriver);
dLoaded++;
}
return resp;
}
Controladora de Drivers
● As funções de retorno de estrutura são
apresentadas à controladora no arquivo de
header.
● O header da controladora inclui cada um dos
arquivos de header dos drivers conhecidos em
tempo de execução.
● Uma lista com as funções é montada
juntamente com um enumerado descritivo
desta mesma lista.
Controladora de Drivers
#ifndef ctrdrv_h
#define ctrdrv_h
#define QNTD_DRV 20
char initCtrDrv(void);
char callDriver(char drv_id, char func_id, void
*parameters);
char initDriver(char newDriver);
// Drivers conhecidos estáticamente
enum {
DRV_END /*DRV_END must always be the last*/
};
static ptrGetDrv drvGetFunc[DRV_END] = {
};
#endif // ctrdrv_h
Controladora de Drivers
//ddCtr.h
#include "drvGenerico.h"
#include "drvInterrupt.h"
#include "drvTimer.h"
enum {
DRV_GEN, /*1st driver*/
DRV_INTERRUPT, /*2nd driver*/
DRV_TIMER, /*3rd driver*/
DRV_END /*DRV_END must always be the last*/
};
//the functions to get the drivers should be put in
the same order as in the enum
static ptrGetDrv drvGetFunc[DRV_END] = {
getGenericoDriver, /*1st driver*/
getInterruptDriver, /*2nd driver*/
getTimerDriver /*3rd driver*/
};
Controladora de Drivers
char callDriver(char drv_id,
char func_id,
void *param) {
char i;
for (i = 0; i < dLoaded; i++) {
//find the right driver
if (drv_id == drivers[i]->drv_id) {
return
drivers[i]->func[func_id].func_ptr(param);
}
}
return DRV_FUNC_NOT_FOUND;
}
Controladora de Drivers
void main(void) {
//Atualizar kernelInit para inicializar a Ctr
kernelInit();
initDriver(DRV_LCD);
callDriver(DRV_LCD, LCD_CHAR, 'U');
callDriver(DRV_LCD, LCD_CHAR, 'N');
callDriver(DRV_LCD, LCD_CHAR, 'I');
callDriver(DRV_LCD, LCD_CHAR, 'F');
callDriver(DRV_LCD, LCD_CHAR, 'E');
callDriver(DRV_LCD, LCD_CHAR, 'I');
callDriver(DRV_LCD, LCD_CHAR, '@');
callDriver(DRV_LCD, LCD_CHAR, '0');
callDriver(DRV_LCD, LCD_CHAR, '4');
callDriver(DRV_LCD, LCD_CHAR, '8');
}
Controladora de Drivers
Exercício
● Montar um relógio
com o LCD
utilizando a
controladora para
acesso ao driver.
Controladora de Drivers
Camada de Abstração de Interrupções
Interrupt Abstract Layer - IAL
Interrupt Abstract Layer - IAL
● Um tipo de hardware comum a quase todo
microcontrolador é o dispositivo de
interrupção.
● Este dispositivo pausa o processador
quando há uma chamada de interrupção.
● Em seguida ele verifica qual a fonte da
chamada e, após salvar as variáveis da
CPU na stack, começa a executar de um
endereço pré-definido.
Interrupt Abstract Layer - IAL
● Com o intuito de simplificar este dispositivo
do ponto de vista do software é comum
criar um driver para realizar a gestão deste.
● Este driver receberá o endereço de funções
que serão executadas quando uma
determinada interrupção acontecer.
● É necessário uma função para receber o
endereço e uma variável para armazená-lo
internamente
Interrupt Abstract Layer - IAL
//drvInterrupt.c
//define o ponteiro de função a ser chamado
//na interrupção
typedef void (*intFunc)(void);
//variável para armazenar o endereço da função
//a ser executada na interrupção
static intFunc thisInterrupt;
//setup da função de interrupção
char setInterruptFunc(void *parameters) {
thisInterrupt = (intFunc) parameters;
return SUCESS;
}
Interrupt Abstract Layer - IAL
● A função para execução da interrupção é
dependente do compilador e do
processador utilizado. Deste modo a função
deve ser customizada para cada aplicação.
● Seguem-se três exemplos de
implementação.
Interrupt Abstract Layer - IAL
//mc9s12 + Codewarrior
typedef void (*near tIsrFunc)(void);
static const tIsrFunc _InterruptVectorTable[] @0xFFF0={isrVrti};
void interrupt isrVrti (void){
thisInterrupt();
}
//pic18f4550 + SDCC
void isr(void) interrupt 1 {
thisInterrupt();
}
//pic18f4550 + C18
void isr (void){
thisInterrupt();
}
#pragma code highvector=0x08
void highvector(void){
_asm goto isr _endasm
}
#pragma code
Interrupt Abstract Layer - IAL
● A IAL facilita o uso das interrupções por
parte do programador, bastando apenas 3
passos
● O driver desejado é inicializado.
● O driver de interrupção é inicializado
● A função desejada é configurada
Interrupt Abstract Layer - IAL
//Função desejada para rodar na interrupção do timer
void timerISR(void) {
callDriver(DRV_TIMER, TMR_RESET, 1000);
kernelClock();
}
void main (void){
kernelInit();
//inicializando e configurando o timer
initDriver(DRV_TIMER);
callDriver(DRV_TIMER, TMR_START, 0);
callDriver(DRV_TIMER, TMR_INT_EN, 0);
//inicializando e configurando a interrupção
initDriver(DRV_INTERRUPT);
callDriver(DRV_INTERRUPT, INT_ENABLE, 0);
//configurando a rotina a ser chamada
callDriver(DRV_INTERRUPT, INT_TIMER_SET, (void*)timerISR);
kernelLoop();
}
Controladora de Drivers
Callback para drivers
Callback
● Funções de callback lembram os eventos
nas linugagens de alto nível
● Quando o mouse clicar no botão Y execute X
● Quando a tecla “t” for pressionada execute Z
● Para isso é necessário que o hardware seja
capaz de iniciar uma interrupção
Controladora de Drivers
● No processo do callback existem duas
partes separadas que devem ser
executadas sequencialmente.
● A primeira é o código que é executado dentro
da interrupção. Ele deve ser rápido e utilizar
poucos recursos. Geralmente são apenas
salvos os dados ou informações geradas pela
interrupção e seu processamento é postergado
neste momento
● A segunda é o processo de callback, rodado
agora pelo kernel pode levar mais tempo sem
atrapalhar a temporização do sistema.
Callback
:Process A :Driver
:Interrupt
Layer
ISRsetup
(isrPtr)
DataRequest
(&callback)
HW interrupt
isrPtr()
:Kernel
kernelAddProc(&callback)
KernelLoop:
ProcessA()
KernelLoop:
callback()
Callback
//********** Excerpt from drvAdc.c **********
// called from setup time to enable ADC interrupt
// and setup ADC ISR callback
char enableAdcInterrup(void* parameters){
callDriver(DRV_INTERRUPT,INT_ADC_SET,(void*)adcISR);
BitClr(PIR1,6);
return FIM_OK;
}
//********** Excerpt from drvInterrupt.c **********
// store the pointer to the interrupt function
typedef void (*intFunc)(void);
static intFunc adcInterrupt;
// function to set ADC ISR callback for latter use
char setAdcInt(void *parameters) {
adcInterrupt = (intFunc)parameters;
return FIM_OK;
}
Callback
//********** Excerpt from main.c **********
// Process called by the kernel
char adc_func(void) {
//creating callback process
static process proc_adc_callback = {adc_callback, 0, 0};
callDriver(DRV_ADC,ADC_START,&proc_adc_callback);
return REPEAT;
}
//********** Excerpt from drvAdc.c **********
//function called by the process adc_func (via drv controller)
char startConversion(void* parameters){
callBack = parameters;
ADCON0 |= 0b00000010; //start conversion
return SUCCESS;
}
Callback
//********** Excerpt from drvInterrupt.c **********
//interrupt function
void isr(void) interrupt 1 {
if (BitTst(INTCON, 2)) { //Timer overflow
}
if (BitTst(PIR1, 6)) { //ADC conversion finished
//calling ISR callback stored
adcInterrupt();
}
}
//********** Excerpt from drvAdc.c **********
//ADC ISR callback function
void adcISR(void){
value = ADRESH;
value <<= 8;
value += ADRESL;
BitClr(PIR1,6);
kernelAddProc(callBack);
}
Callback
//********** Excerpt from main.c **********
//callback function started from the kernel
char adc_callback(void) {
unsigned int resp;
//getting the converted value
callDriver(DRV_ADC,ADC_LAST_VALUE,&resp);
//changing line and printing on LCD
callDriver(DRV_LCD,LCD_LINE,1);
callDriver(DRV_LCD,LCD_INTEGER,resp);
return SUCCESS;
}

Projeto de uma controladora de drivers

  • 1.
    ELT048 - SOE Controladorade Drivers Rodrigo Almeida Universidade Federal de Itajubá
  • 2.
    Revisão ● Sistema detempo real ● Requisitos temporais
  • 3.
    Driver ● Driver éuma abstração em software do hardware da placa, suas funcionalidades, opções e modos de funcionamento. ● É dependente do processador,dos componentes conectados e das ligações entre eles.
  • 4.
    Criação de umdriver ● Para a criação de um driver basta encapsular estas funções num conjunto organizado e de fácil acesso. ● Headers e defines ● A mudança do driver não deve gerar nenhuma alteração no código da aplicação. ● A criação de um driver deve se concentrar na função e não nos recursos do dispositivo.
  • 5.
  • 6.
    Exercício ● Desenvolver umdriver para o LCD utilizando o modelo proposto
  • 7.
    Controladora de Drivers ●É utilizada como uma interface entre o kernel e os drivers ● É a responsável pela inicialização e manutenção dos drivers ● Armazena informações sobre os drivers carregados ● Funciona como uma camada de segurança, evitando que comandos “errados” sejam repassados aos drivers.
  • 8.
    ● A implementaçãoa ser apresentada possui apenas 2 funções: ● Uma para inicialização de um driver – Além da inicialização deve fornecer um ID para o driver. ● Uma para repassar os comandos da aplicação para os drivers Controladora de Drivers
  • 9.
    ● Para obteracesso à todos os drivers é necessário que a controladora obtenha o endereço de uma estrutura do tipo driver. ● Estáticamente: ● é montada uma lista com funções que retornam esta estrutura. ● Dinâmicamente: ● recebe-se esta estrutura via comunicação serial e retorna-se o endereço onde os dados foram salvos. Controladora de Drivers
  • 10.
    static driver* driversLoaded[QNTD_DRV]; staticchar qntDrvLoaded; char initDriver(char newDriver) { char resp = FAIL; if(dLoaded < QNTD_DRV) { //obtem a estrutura do driver desejado drivers[dLoaded] = drvInitVect[newDriver](); //should test if driver was loaded correcly resp = drivers[dLoaded]->drv_init(&newDriver); dLoaded++; } return resp; } Controladora de Drivers
  • 11.
    ● As funçõesde retorno de estrutura são apresentadas à controladora no arquivo de header. ● O header da controladora inclui cada um dos arquivos de header dos drivers conhecidos em tempo de execução. ● Uma lista com as funções é montada juntamente com um enumerado descritivo desta mesma lista. Controladora de Drivers
  • 12.
    #ifndef ctrdrv_h #define ctrdrv_h #defineQNTD_DRV 20 char initCtrDrv(void); char callDriver(char drv_id, char func_id, void *parameters); char initDriver(char newDriver); // Drivers conhecidos estáticamente enum { DRV_END /*DRV_END must always be the last*/ }; static ptrGetDrv drvGetFunc[DRV_END] = { }; #endif // ctrdrv_h Controladora de Drivers
  • 13.
    //ddCtr.h #include "drvGenerico.h" #include "drvInterrupt.h" #include"drvTimer.h" enum { DRV_GEN, /*1st driver*/ DRV_INTERRUPT, /*2nd driver*/ DRV_TIMER, /*3rd driver*/ DRV_END /*DRV_END must always be the last*/ }; //the functions to get the drivers should be put in the same order as in the enum static ptrGetDrv drvGetFunc[DRV_END] = { getGenericoDriver, /*1st driver*/ getInterruptDriver, /*2nd driver*/ getTimerDriver /*3rd driver*/ }; Controladora de Drivers
  • 14.
    char callDriver(char drv_id, charfunc_id, void *param) { char i; for (i = 0; i < dLoaded; i++) { //find the right driver if (drv_id == drivers[i]->drv_id) { return drivers[i]->func[func_id].func_ptr(param); } } return DRV_FUNC_NOT_FOUND; } Controladora de Drivers
  • 15.
    void main(void) { //AtualizarkernelInit para inicializar a Ctr kernelInit(); initDriver(DRV_LCD); callDriver(DRV_LCD, LCD_CHAR, 'U'); callDriver(DRV_LCD, LCD_CHAR, 'N'); callDriver(DRV_LCD, LCD_CHAR, 'I'); callDriver(DRV_LCD, LCD_CHAR, 'F'); callDriver(DRV_LCD, LCD_CHAR, 'E'); callDriver(DRV_LCD, LCD_CHAR, 'I'); callDriver(DRV_LCD, LCD_CHAR, '@'); callDriver(DRV_LCD, LCD_CHAR, '0'); callDriver(DRV_LCD, LCD_CHAR, '4'); callDriver(DRV_LCD, LCD_CHAR, '8'); } Controladora de Drivers
  • 16.
    Exercício ● Montar umrelógio com o LCD utilizando a controladora para acesso ao driver.
  • 17.
    Controladora de Drivers Camadade Abstração de Interrupções Interrupt Abstract Layer - IAL
  • 18.
    Interrupt Abstract Layer- IAL ● Um tipo de hardware comum a quase todo microcontrolador é o dispositivo de interrupção. ● Este dispositivo pausa o processador quando há uma chamada de interrupção. ● Em seguida ele verifica qual a fonte da chamada e, após salvar as variáveis da CPU na stack, começa a executar de um endereço pré-definido.
  • 19.
    Interrupt Abstract Layer- IAL ● Com o intuito de simplificar este dispositivo do ponto de vista do software é comum criar um driver para realizar a gestão deste. ● Este driver receberá o endereço de funções que serão executadas quando uma determinada interrupção acontecer. ● É necessário uma função para receber o endereço e uma variável para armazená-lo internamente
  • 20.
    Interrupt Abstract Layer- IAL //drvInterrupt.c //define o ponteiro de função a ser chamado //na interrupção typedef void (*intFunc)(void); //variável para armazenar o endereço da função //a ser executada na interrupção static intFunc thisInterrupt; //setup da função de interrupção char setInterruptFunc(void *parameters) { thisInterrupt = (intFunc) parameters; return SUCESS; }
  • 21.
    Interrupt Abstract Layer- IAL ● A função para execução da interrupção é dependente do compilador e do processador utilizado. Deste modo a função deve ser customizada para cada aplicação. ● Seguem-se três exemplos de implementação.
  • 22.
    Interrupt Abstract Layer- IAL //mc9s12 + Codewarrior typedef void (*near tIsrFunc)(void); static const tIsrFunc _InterruptVectorTable[] @0xFFF0={isrVrti}; void interrupt isrVrti (void){ thisInterrupt(); } //pic18f4550 + SDCC void isr(void) interrupt 1 { thisInterrupt(); } //pic18f4550 + C18 void isr (void){ thisInterrupt(); } #pragma code highvector=0x08 void highvector(void){ _asm goto isr _endasm } #pragma code
  • 23.
    Interrupt Abstract Layer- IAL ● A IAL facilita o uso das interrupções por parte do programador, bastando apenas 3 passos ● O driver desejado é inicializado. ● O driver de interrupção é inicializado ● A função desejada é configurada
  • 24.
    Interrupt Abstract Layer- IAL //Função desejada para rodar na interrupção do timer void timerISR(void) { callDriver(DRV_TIMER, TMR_RESET, 1000); kernelClock(); } void main (void){ kernelInit(); //inicializando e configurando o timer initDriver(DRV_TIMER); callDriver(DRV_TIMER, TMR_START, 0); callDriver(DRV_TIMER, TMR_INT_EN, 0); //inicializando e configurando a interrupção initDriver(DRV_INTERRUPT); callDriver(DRV_INTERRUPT, INT_ENABLE, 0); //configurando a rotina a ser chamada callDriver(DRV_INTERRUPT, INT_TIMER_SET, (void*)timerISR); kernelLoop(); }
  • 25.
  • 26.
    Callback ● Funções decallback lembram os eventos nas linugagens de alto nível ● Quando o mouse clicar no botão Y execute X ● Quando a tecla “t” for pressionada execute Z ● Para isso é necessário que o hardware seja capaz de iniciar uma interrupção
  • 27.
    Controladora de Drivers ●No processo do callback existem duas partes separadas que devem ser executadas sequencialmente. ● A primeira é o código que é executado dentro da interrupção. Ele deve ser rápido e utilizar poucos recursos. Geralmente são apenas salvos os dados ou informações geradas pela interrupção e seu processamento é postergado neste momento ● A segunda é o processo de callback, rodado agora pelo kernel pode levar mais tempo sem atrapalhar a temporização do sistema.
  • 28.
    Callback :Process A :Driver :Interrupt Layer ISRsetup (isrPtr) DataRequest (&callback) HWinterrupt isrPtr() :Kernel kernelAddProc(&callback) KernelLoop: ProcessA() KernelLoop: callback()
  • 29.
    Callback //********** Excerpt fromdrvAdc.c ********** // called from setup time to enable ADC interrupt // and setup ADC ISR callback char enableAdcInterrup(void* parameters){ callDriver(DRV_INTERRUPT,INT_ADC_SET,(void*)adcISR); BitClr(PIR1,6); return FIM_OK; } //********** Excerpt from drvInterrupt.c ********** // store the pointer to the interrupt function typedef void (*intFunc)(void); static intFunc adcInterrupt; // function to set ADC ISR callback for latter use char setAdcInt(void *parameters) { adcInterrupt = (intFunc)parameters; return FIM_OK; }
  • 30.
    Callback //********** Excerpt frommain.c ********** // Process called by the kernel char adc_func(void) { //creating callback process static process proc_adc_callback = {adc_callback, 0, 0}; callDriver(DRV_ADC,ADC_START,&proc_adc_callback); return REPEAT; } //********** Excerpt from drvAdc.c ********** //function called by the process adc_func (via drv controller) char startConversion(void* parameters){ callBack = parameters; ADCON0 |= 0b00000010; //start conversion return SUCCESS; }
  • 31.
    Callback //********** Excerpt fromdrvInterrupt.c ********** //interrupt function void isr(void) interrupt 1 { if (BitTst(INTCON, 2)) { //Timer overflow } if (BitTst(PIR1, 6)) { //ADC conversion finished //calling ISR callback stored adcInterrupt(); } } //********** Excerpt from drvAdc.c ********** //ADC ISR callback function void adcISR(void){ value = ADRESH; value <<= 8; value += ADRESL; BitClr(PIR1,6); kernelAddProc(callBack); }
  • 32.
    Callback //********** Excerpt frommain.c ********** //callback function started from the kernel char adc_callback(void) { unsigned int resp; //getting the converted value callDriver(DRV_ADC,ADC_LAST_VALUE,&resp); //changing line and printing on LCD callDriver(DRV_LCD,LCD_LINE,1); callDriver(DRV_LCD,LCD_INTEGER,resp); return SUCCESS; }