Classes C ++ para abstração de pinos de E / S


13

Estou procurando abstrações em C ++ para pontos ou pinos de E / S de hardware. Coisas como in_pin, out_pin, inout_pin, talvez open_collector_pin, etc.

Certamente eu mesmo posso criar esse conjunto de abstrações, então não estou procurando o tipo de respostas 'ei, você pode fazer desta maneira', mas sim o 'olhar para esta biblioteca que foi usada neste e este projeto'.

O Google não apareceu nada, talvez porque não sei como os outros chamariam isso.

Meu objetivo é criar bibliotecas de E / S baseadas nesses pontos, mas também fornecê-los, por isso seria fácil, por exemplo, conectar um HD44780 LCd aos pinos de E / S do chip ou a um I2C (ou SPI) Extensor de E / S ou qualquer outro ponto que possa ser controlado de alguma forma, sem nenhuma alteração na classe do LCD.

Eu sei que isso está no limite da eletrônica / software, desculpe se ele não pertence aqui.

@leon: fiação Esse é um grande pacote de software, vou precisar olhar mais de perto. Mas parece que eles não usam uma abstração de pinos como eu quero. Por exemplo, na implementação do teclado, vejo

digitalWrite(columnPins[c], LOW);   // Activate the current column.

Isso implica que há uma função (digitalWrite) que sabe gravar em um pino de E / S. Isso torna impossível adicionar um novo tipo de pino de E / S (por exemplo, um que esteja em um MCP23017, portanto ele deve ser gravado via I2C) sem reescrever a função digitalWrite.

@Oli: pesquisei no Google um exemplo de IO do Arduino, mas eles parecem usar a mesma abordagem da biblioteca Wiring:

int ledPin = 13;                 // LED connected to digital pin 13
void setup(){
    pinMode(ledPin, OUTPUT);      // sets the digital pin as output
}

Que microcontrolador estamos falando aqui?
Majenko 4/09/11

Isso é irrelevante; para um microcontrolador específico, os pinos io desse uC implementarão as interfaces apropriadas. Mas isso é para C ++, então pense em chips de 32 bits como ARM, Cortex e MIPS.
Wouter van Ooijen

1
Eu nunca usei um, mas o Arduino não abstrai todos os pinos assim? Você pode (ou não) obter informações úteis sobre como elas fizeram as coisas.
precisa saber é o seguinte

1
E quanto a reescrever a função digitalWrite, veja "sobrecarregar" em C ++. Há alguns instantes, escrevi uma função sobrecarregada digitalWrite para uma placa de expansão de E / S para o Arduino. Desde que você use parâmetros diferentes (substitui o primeiro "int" por um "struct"), ele selecionará seu digitalWrite de preferência ao padrão.
Majenko

1
Fiz uma palestra sobre o encontro de C ++ em Berlim sobre o meu trabalho nesse assunto. Ele pode ser encontrado no youtube: youtube.com/watch?v=k8sRQMx2qUw Desde então, mudei para uma abordagem um pouco diferente, mas a conversa ainda pode ser interessante.
Wouter van Ooijen

Respostas:


3

Resposta curta: infelizmente, não há biblioteca para fazer o que você deseja. Eu já fiz isso várias vezes, mas sempre em projetos não de código aberto. Estou pensando em colocar algo no github, mas não tenho certeza de quando posso.

Por que C ++?

  1. O compilador é livre para usar a avaliação dinâmica de expressão de tamanho de palavra. C propaga para int. Sua máscara / deslocamento de byte pode ser feita mais rapidamente / menor.
  2. Inlining.
  3. As operações de modelagem permitem variar o tamanho da palavra e outras propriedades, com segurança de tipo.

5

Permita-me conectar descaradamente meu projeto de código aberto https://Kvasir.io . A parte Kvasir :: Io fornece funções de manipulação de pinos. Você deve primeiro definir seu pino usando um Kvasir :: Io :: PinLocation da seguinte maneira:

constexpr PinLocation<0,4> led1;    //port 0 pin 4
constexpr PinLOcation<0,8> led2;

Observe que isso realmente não usa RAM porque essas são variáveis ​​constexpr.

Em todo o seu código, você pode usar esses locais de pinos nas funções de "fábrica de ações", como makeOpenDrain, set, clear, makeOutput e assim por diante. Um 'action factory' não executa a ação, mas retorna um Kvasir :: Register :: Action que pode ser executado usando Kvasir :: Register :: apply (). A razão para isso é que apply () mescla as ações transmitidas a ele quando elas agem em um mesmo registro para que haja um ganho de eficiência.

apply(makeOutput(led1),
    makeOutput(led2),
    makeOpenDrain(led1),
    makeOpenDrain(led2));

Como a criação e mesclagem de ações são feitas em tempo de compilação, isso deve produzir o mesmo código do assembler que o equivalente codificado manualmente:

PORT0DIR |= (1<<4) | (1<<8);
PORT0OD |= (1<<4) | (1<<8);


2

Em C ++, é possível escrever uma classe para que você possa usar portas de E / S como se fossem variáveis, por exemplo

  PORTB = 0x12; / * Grava em uma porta de 8 bits * /
  se (RB3) LATB4 = 1; / * Leia um bit de E / S e escreva condicionalmente outro * /

sem considerar a implementação subjacente. Por exemplo, se alguém estiver usando uma plataforma de hardware que não suporta operações no nível de bits, mas suporta operações de registro no nível de bytes, é possível (provavelmente com a ajuda de algumas macros) definir uma classe estática IO_PORTS com leitura / gravação em linha propriedades chamadas bbRB3 e bbLATB4, de modo que a última declaração acima se tornaria

  if (IO_PORTS.bbRB3) IO_PORTS.bbLATB4 = 1;

que por sua vez seria processado em algo como:

  if (!! (PORTB & 8)) (1? (PORTB | = 16): (PORTB & = ~ 16));

Um compilador deve ser capaz de perceber a expressão constante no operador?: E simplesmente incluir a parte "true". Pode ser possível reduzir o número de propriedades criadas fazendo com que as macros se expandam para algo como:

  if (IO_PORTS.ppPORTB [3]) IO_PORTS.ppPORTB [4] = 1;

ou

  if (IO_PORTS.bb (addrPORTB, 3)) IO_PORTS.bbPORTB (addrPORTB, 4) = 1;

mas não tenho certeza se um compilador seria capaz de alinhar o código da mesma forma.

De maneira alguma desejo sugerir que o uso de portas de E / S como se fossem variáveis ​​é necessariamente uma boa idéia, mas como você menciona C ++, é um truque útil para saber. Minha preferência em C ou C ++, se a compatibilidade com o código que usa o estilo mencionado acima não for necessária, provavelmente seria definir algum tipo de macro para cada bit de E / S e depois definir macros para "readBit", "writeBit", "setBit" e "clearBit" com a condição de que o argumento de identificação de bits passado para essas macros deve ser o nome de uma porta de E / S destinada ao uso com essas macros. O exemplo acima, por exemplo, seria escrito como

  if (readBit (RB3)) setBit (LATB4);

e traduzido como

  if (!! (_ PORT_RB3 & _BITMASK_RB3)) _PORT_LATB4 | = _BITMASK_LATB4;

Isso seria um pouco mais trabalhoso para o pré-processador do que o estilo C ++, mas seria menos trabalhoso para o compilador. Isso também permitiria a geração ideal de código para muitas implementações de E / S e a implementação decente de código para quase todos.


3
Uma citação da pergunta: "Não estou procurando o tipo de respostas 'ei, você pode fazê-lo desta maneira'" ...
Wouter van Ooijen

Acho que não estou claro o que você está procurando. Certamente, eu esperaria que muitas pessoas interessadas em classes para reconstrução de pinos de E / S também estivessem interessadas em saber que, usando propriedades, é possível criar código que é escrito para um estilo de E / S, praticamente qualquer outra coisa. Eu usei propriedades para fazer uma declaração como "LATB3 = 1;" envie uma solicitação de E / S para um fluxo TCP.
Supercat

Tentei ser claro na minha pergunta: quero poder acomodar novos tipos de pinos de E / S sem reescrever o código que usa pinos de E / S. Você escreve sobre conversões de tipo definidas pelo usuário e operadores de atribuição, que certamente são interessantes, eu as uso o tempo todo, mas não uma solução para o meu problema.
Wouter van Ooijen

@Wouter van Ooijen: Quais "novos tipos de pinos de E / S" você espera? Se o código fonte for escrito com sintaxe como "if (BUTTON_PRESSED) MOTOR_OUT = 1;", seria de esperar que, para qualquer mecanismo pelo qual o processador pudesse ler um controle de botão ou um motor, pudesse escrever uma biblioteca para que a fonte acima código ligaria o motor se o botão fosse pressionado. Essa biblioteca pode não representar a maneira mais eficiente de ligar o motor, mas deve funcionar.
Supercat

@Wouter van Ooijen: Talvez se possa melhorar a eficiência se for necessário que o código-fonte invoque uma macro UPDATE_IO () ou UPDATE_INPUTS () algum tempo antes de ler qualquer entrada e execute uma UPDATE_IO () ou UPDATE_OUTPUTS () algum tempo após qualquer saída, com a semântica de que as entradas podem ser amostradas no código que as lê ou na chamada anterior UPDATE_INPUTS () / UPDATE_IO (). Da mesma forma, os resultados podem ocorrer imediatamente ou ser adiados. Se uma E / S for implementada usando algo como um registro de deslocamento, as ações adiadas permitiriam a consolidação de várias operações.
Supercat

1

Se você está procurando algo realmente impressionante para abstrair o hardware e está confiante em suas habilidades em C ++, tente este padrão:

https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

Eu usei-o em uma tentativa de abstrair o hardware de um chip Cortex-M0. Ainda não escrevi nada sobre essa experiência (farei isso algum dia), mas acredite que tem sido muito útil por causa de sua natureza estática polimórfica: o mesmo método para chips diferentes, sem nenhum custo (comparado ao polimorfismo dinâmico).


Nos anos seguintes a este post, participei de "aulas" separadas para pin_in, pin_out, pin_oc e pin_in_out. Para desempenho ideal (tamanho e velocidade), uso classes estáticas, passadas como parâmetros de modelo. Falei sobre isso no Meeting C ++ em Berlim
Wouter van Ooijen
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.