Circuitos não padronizados: indicador de sete segmentos no ATtiny13

Não estamos procurando maneiras simples.


imagem

Anteriormente, é minha primeira publicação que causou ressonância entre os usuários do Habr. Decidiu não parar. Continuamos a extrair o impossível do ATtiny13. Eu o aviso imediatamente, as soluções descritas não são padrão novamente e alguém pode causar indignação e dissonância cognitiva ("E qual é o sentido do artigo então? Mostrar quais elementos podem ser conectados?"). Além disso, essa solução também é realmente impraticável, que vou escrever com mais detalhes abaixo. Mas aconteceu que as soluções padrão são conhecidas há muito tempo, e ler sobre elas nem sempre é interessante, mas escrever é ingrato.

Eu realmente gosto desse garoto ATtiny13. Ele tem cérebro suficiente para resolver muitos problemas do dia a dia (acenda as luzes, ventilação, corra até a loja para beber cerveja ). E o preço é simplesmente ridículo. Aqui estão apenas algumas pernas e não há alças. Portanto, você deve recorrer a todo tipo de truque para resolver o problema da falta de pernas.

No processo de estudar a programação de microcontroladores (no ambiente do Arduino, não conte a ninguém), como muitos, passei pela etapa de conectar um sensor de distância ultrassônico, algo como isto:

imagem

Como o método de transmissão de informações do sensor para o controlador é simples de desonrar, o ATtiny13 lida com isso facilmente. Em seguida, foi necessário exibir informações em um indicador de sete segmentos usando registros de turno. Ou seja, o esquema da parte da tela era várias vezes maior que o próprio controlador. Naquele momento, eu brinquei e segui em frente.

Recentemente, pensei, o que mais seria uma tarefa esmagadora para atribuir a Tinka? O que ela ainda não conseguiu lidar nos exemplos descritos. A primeira coisa que lembrei da indicação. Há algum tempo, eu já estava procurando informações sobre um tópico semelhante. Então eu achei uma opção tão interessante .

Imagem
imagem

21 segmentos de 5 pés do controlador. Uau! Eu nem preciso de muito, dois sinais são suficientes, mais um ponto, um total de 15 segmentos. E se com quatro pernas? Em seguida, obtenha no máximo 13 segmentos, não o suficiente. À vista do circuito, surgiu imediatamente um desejo de montar e experimentar, embora não seja fácil elaborar um algoritmo de operação. Mas, com uma análise mais detalhada, você entende que não funcionará na coleta, pois esses animais de sete segmentos não existem na natureza (provavelmente). Você pode fazer, é claro, mas este é um nível diferente. Então a idéia foi adiada para tempos melhores.

Offtopic: Por que não existem indicadores de sete segmentos com lógica integrada? Onde os desenvolvedores estão olhando? Quão conveniente é a instalação e o controle - duas pernas de força e 3 (1, 2) pernas de dados. E, afinal, eles estavam na URSS: 490IP1, 490IP2. Dentro do indicador mais comum para 2 ... 4 dígitos, há muito espaço para colocar o microcircuito do chip, e o preço do registro de turno é de 0,064 cu junto com o estojo. Oh bem.

E então eu pensei novamente, como reduzir o número de pernas para trabalhar com o indicador de sete segmentos? As saídas do controlador podem assumir três estados (na verdade 4, mas agora não importa). Existe alguma maneira de usar isso? Se dois estados em relação ao LED puderem ser interpretados apenas como brilhantes, não brilhantes, então com três será um pouco mais interessante. Ainda não descobri como usá-lo, mas me ocorreu o seguinte esquema:

imagem

Se a saída do controlador estiver no estado zero, os LEDs não acendem (o que é óbvio).

Se a saída estiver no estado da unidade, os LEDs acenderão, o que também é compreensível.

Mas se a saída não é a saída, mas está conectada à entrada, uma corrente flui através do circuito de dois resistores e o LED HL1, criando uma queda de tensão no ponto de conexão dos resistores de aproximadamente (5-1,7) / (2,2 + 1,5) * 1,5 + 1,7 = 3,0 V. Isso não é suficiente para a corrente fluir através do circuito VD1_R3_HL2 (são necessários aproximadamente 3,4 V). O VD1 é um LED adicional usado como diodo zener (o estabilizador está mais correto); portanto, não o consideraremos um LED, para não confundir. Não importa se o resistor de pull-up está ligado dentro do microcontrolador, sua resistência (20 kOhm) praticamente não afeta a situação. Não cheguei a essas classificações imediatamente, antes disso, tentei com um diodo regular como o VD1, ele também funciona muito bem com os mesmos resistores R1 e R2. Mas é melhor que o R2 seja cerca de uma vez e meia maior que o R1. E quase esqueci a coisa mais importante: tudo o que é descrito é possível apenas com o uso de LEDs vermelhos tanto no indicador quanto nos adicionais. Em casos extremos, o indicador ou os LEDs adicionais podem ser aplicados em verde. E com uma tensão de alimentação de 4,5 V a 5 V.

O que temos no final? Três estados: nenhum LED (0) brilha, HL1 (1) brilha ou HL1 e HL2 (2) brilham. Muito parecido com o sistema ternário. Mas não podemos acender o HL2 sem o HL1, isso deve ser lembrado. Mas agora, com a ajuda das quatro pernas do microcontrolador, podemos controlar oito LEDs (eu acho que sim).

Depois, tentei dividir os segmentos indicadores em pares (como em um jardim de infância: uma menina). A principal condição é que em cada par um dos segmentos não possa brilhar por si próprio, tal discriminação. Aqui está o que eu tenho:

imagem

Quatro pares de segmentos, em cada letra maiúscula, é indicado o segmento dominante, que pode funcionar por si só, o segundo somente com ele. Você pode notar que o segmento "a" se agita com dois de uma vez e ninguém chegou ao ponto ruim. Como parece a vida!

Mas com esses pares você pode exibir (quase) todos os números:

Imagem
imagem

Cada par é pintado em sua própria cor. Um espectador atento percebeu que algo estava errado com o empate. Por enquanto, não vamos nos concentrar nisso. Tentei mais algumas opções para agrupar segmentos, é melhor não aparecer. Talvez alguém sugira. Talvez a rede neural possa lidar com isso.

Na segunda etapa dos experimentos, tive que usar um indicador com um ânodo comum. Portanto, o esquema final é o seguinte:

Esquema
imagem

Alguém pode perguntar: para onde foram os resistores de 100 ohm? Há muito se sabe (e é usado ativamente) que, com indicações dinâmicas feitas corretamente, os resistores limitadores de corrente podem ser dispensados. Mesmo que a tensão seja constantemente aplicada a partir da saída do controlador para os dois LEDs conectados em série por engano, o microcontrolador e os LEDs normalmente suportam isso, a corrente é limitada pela resistência de transição dentro do MC. E mais sobre resistores. A corrente máxima através do HL1, de acordo com o esquema anterior, é de cerca de 2 mA e, através do HL2, atinge 25 ... 40 mA (presumivelmente, mais tarde vou lhe dizer de onde esses números vieram). Isso significa que a saída de luz de diferentes segmentos será diferente. Porém, como a indicação dinâmica será usada, isso pode ser facilmente resolvido devido ao tempo de exibição diferente dos segmentos.

Todas as experiências que realizei no Arduino Nano no Arduino IDE. Uma excelente placa de prototipagem, fica boa na placa de ensaio, é exibida via USB sem problemas. Algo falhou? Corrigi o esboço e em um minuto fiz o upload de um novo firmware. E quando você depurou o código, pode ir para o firmware no ATtiny13, ainda são necessários mais alguns gestos.

A propósito, também estou piscando com o Arduino no ambiente do Arduino, isso praticamente elimina a possibilidade de travar o MK com os fusíveis errados e muito mais fácil.

Aqui está um exemplo de exibição do número 4 no código:

pinMode(f_a, INPUT); //    f_a   digitalWrite(f_a, 1); //   F pinMode(d_e, OUTPUT); //    d_e   digitalWrite(d_e, 1); //     1,     D  E pinMode(b_a, INPUT); //    b_a   digitalWrite(b_a, 1); //   B pinMode(c_g, OUTPUT); //    c_g   digitalWrite(c_g, 0); //     0,   C,  G delayMicroseconds (150); // F, B, C, G c  150  pinMode(c_g, INPUT); //    c_g   digitalWrite(c_g, 1); //   G ,     delay (time_2); // F, B, C c  2  

Em princípio, tudo deve ficar claro, mesmo para quem não conhece o Arduino, mas entende um pouco sobre os controladores. Figuras de 150 microssegundos e 2 milissegundos são selecionadas experimentalmente pelo brilho dos segmentos. No código final, você precisa colocá-los em variáveis ​​separadas para poder alterá-los durante a depuração. A partir dessas figuras, é possível determinar aproximadamente a ordem das diferenças de correntes em dois segmentos em um par. Como o segmento G brilha cerca de 13 vezes menos que os outros e fornece o mesmo brilho, pode-se supor que a corrente nesse segmento é 13 vezes mais que nos outros. De fato, a dependência do brilho da corrente é não linear; portanto, a corrente pode ser 25 vezes maior, ou seja, 50 mA. Que, com esse ciclo de trabalho, seja bastante seguro para a saída do MK. A propósito, essa diferença de correntes foi jogada nas mãos ao resolver o problema da figura 2. Como escrevi acima, o segmento G só pode ser iluminado junto com o segmento C. Mas se você passar 0 para a perna MK, responsável por C e G, em 150 μs e depois 2 ms para manter 1 nele, o segmento G “funcionará” com brilho total e o segmento C terá apenas um pouco de tempo para acender pelos mesmos 150 microssegundos. Temos dois quase cheios. Assim, consegui quebrar a regra que eu mesmo estabeleci. O que não pode ser feito por desesperança.

Então, iluminamos a figura com as quatro pernas do MK. Na verdade, eu perdi esse estágio para mim, imediatamente exibi dois personagens. Para fazer isso, desconecte a saída do ânodo comum de um dos bits indicadores da power plus e conecte-a a outra saída MK, e o ânodo da outra descarga na próxima saída (6 pernas já). Agora, por sua vez, defina 1 no ânodo do dígito menos significativo, exiba o dígito do dígito menos significante, depois 1 no ânodo do dígito menos significativo e exiba o dígito do dígito mais alto e assim por diante em um círculo. Eu conduzi esse experimento com o Arduino Nano, ela tem pernas suficientes. Todo o código foi depurado nele, não na primeira vez. E assim funcionou, como deveria.

Esquema
imagem

Como os ânodos são conectados por sua vez, com a ajuda de um refinamento simples, mais uma saída do MK pode ser liberada. Aqui está o diagrama de resumo:

Esquema
imagem

Total use 5 pernas MK para exibir um número de dois dígitos. Nesta fase, você já pode tentar com o bebê ATtiny. O que eu fiz. Mas não imediatamente. Um esboço compilado em um ambiente Arduino para ATtiny13 ocupava aproximadamente 1,7 kB de memória com 1 kB disponível. Para reduzir o tamanho, tive que recorrer diretamente às portas, o que faria mais tarde. A propósito, no Arduino eu usei as mesmas portas que eu usaria no ATtiny, é muito conveniente. Eles já estão indicados no último diagrama. Após o processamento, o código perdeu um kilobyte.

Aqui está o código resultante para ATtiny13:

Código
 #define time_2 2 //    ,  #define time_3 150 //    ,  byte in1_; byte in2_; int disp_; int d_ = 0; void setup() { } void loop() { d_ = d_ + 1; if (d_ > 150) { //    150     1 d_ = 0; disp_ = disp_ + 1; if (disp_ > 99)(disp_ = 0); //   0  99 } in2_ = disp_ / 10; //     -    10 in1_ = disp_ % 10; //          10 } switch (in1_) { case 0: DDRB = B00001111; PORTB = B00010000; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; case 1: DDRB = B00000111; PORTB = B00000110; delay (time_2); break; case 2: DDRB = B00011111; PORTB = B00000010; delayMicroseconds (time_3); DDRB = B00010011; PORTB = B00010010; delay (time_2); break; case 3: DDRB = B00011011; PORTB = B00000110; delayMicroseconds (time_3); DDRB = B00000011; delay (time_2); break; case 4: DDRB = B00010101; PORTB = B00001110; delayMicroseconds (time_3); DDRB = B00000101; delay (time_2); break; case 5: DDRB = B00011011; PORTB = B00001100; delayMicroseconds (time_3); DDRB = B00001001; delay (time_2); break; case 6: DDRB = B00011111; PORTB = B00001000; delayMicroseconds (time_3); DDRB = B00001001; delay (time_2); break; case 7: DDRB = B00001111; PORTB = B00010110; delayMicroseconds (time_3); DDRB = B00000111; delay (time_2); break; case 8: DDRB = B00011111; PORTB = B00000000; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; case 9: DDRB = B00011011; PORTB = B00000100; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; } switch (in2_) { case 0: DDRB = B00001111; PORTB = B00010001; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; case 1: DDRB = B00000111; PORTB = B00000111; delay (time_2); break; case 2: DDRB = B00011111; PORTB = B00000011; delayMicroseconds (time_3); DDRB = B00010011; PORTB = B00010011; delay (time_2); break; case 3: DDRB = B00011011; PORTB = B00000111; delayMicroseconds (time_3); DDRB = B00000011; delay (time_2); break; case 4: DDRB = B00010101; PORTB = B00001111; delayMicroseconds (time_3); DDRB = B00000101; delay (time_2); break; case 5: DDRB = B00011011; PORTB = B00001101; delayMicroseconds (time_3); DDRB = B00001001; delay (time_2); break; case 6: DDRB = B00011111; PORTB = B00001001; delayMicroseconds (time_3); DDRB = B00001001; delay (time_2); break; case 7: DDRB = B00001111; PORTB = B00010111; delayMicroseconds (time_3); DDRB = B00000111; delay (time_2); break; case 8: DDRB = B00011111; PORTB = B00000001; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; case 9: DDRB = B00011011; PORTB = B00000101; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; } DDRB = B00011111;//   PORTB = B00011110; delay (5); } 


O código acima permitirá que o seu ATtiny13 leia de 0 a 99. Seria mais correto prever a possibilidade de reatribuir as pernas do MK. Os gurus da programação podem reduzir o código várias vezes ( onde está o limite mínimo do Hello World no AVR? ).

Você pode adicionar a função necessária ao código para que o MK exiba algo consciente. É verdade que Tinki já tem todas as pernas ocupadas. Há também uma perna de redefinição que pode ser usada como uma porta de entrada / saída. Mas usá-lo foi mais difícil do que eu pensava. Portanto, por mim mesmo deixo "para depois". Mas há um recurso interessante que nem todo mundo conhece. A entrada analógica ADC0 é enviada para a mesma perna e funciona! É verdade que quando a tensão é menor que 1/4 da tensão de alimentação, o MK entra no modo de redefinição. Mas de 1/4 à tensão de alimentação, é bem possível medir a tensão de entrada. Eu aproveitei isso:

Código
 #define time_2 2 //    ,  #define time_3 150 //    ,  byte in1_; byte in2_; int disp_; int d_ = 0; void setup() { } void loop() { d_ = d_ + 1; if (d_ > 50) { //   50 ... d_ = 0; disp_ = analogRead(A0) / 10; // ...   ,   10,    . if (disp_ > 99)(disp_ = 99); in2_ = disp_ / 10; //     -    10 in1_ = disp_ % 10; //          10 } switch (in1_) { case 0: DDRB = B00001111; PORTB = B00010000; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; case 1: DDRB = B00000111; PORTB = B00000110; delay (time_2); break; case 2: DDRB = B00011111; PORTB = B00000010; delayMicroseconds (time_3); DDRB = B00010011; PORTB = B00010010; delay (time_2); break; case 3: DDRB = B00011011; PORTB = B00000110; delayMicroseconds (time_3); DDRB = B00000011; delay (time_2); break; case 4: DDRB = B00010101; PORTB = B00001110; delayMicroseconds (time_3); DDRB = B00000101; delay (time_2); break; case 5: DDRB = B00011011; PORTB = B00001100; delayMicroseconds (time_3); DDRB = B00001001; delay (time_2); break; case 6: DDRB = B00011111; PORTB = B00001000; delayMicroseconds (time_3); DDRB = B00001001; delay (time_2); break; case 7: DDRB = B00001111; PORTB = B00010110; delayMicroseconds (time_3); DDRB = B00000111; delay (time_2); break; case 8: DDRB = B00011111; PORTB = B00000000; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; case 9: DDRB = B00011011; PORTB = B00000100; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; } switch (in2_) { case 0: DDRB = B00001111; PORTB = B00010001; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; case 1: DDRB = B00000111; PORTB = B00000111; delay (time_2); break; case 2: DDRB = B00011111; PORTB = B00000011; delayMicroseconds (time_3); DDRB = B00010011; PORTB = B00010011; delay (time_2); break; case 3: DDRB = B00011011; PORTB = B00000111; delayMicroseconds (time_3); DDRB = B00000011; delay (time_2); break; case 4: DDRB = B00010101; PORTB = B00001111; delayMicroseconds (time_3); DDRB = B00000101; delay (time_2); break; case 5: DDRB = B00011011; PORTB = B00001101; delayMicroseconds (time_3); DDRB = B00001001; delay (time_2); break; case 6: DDRB = B00011111; PORTB = B00001001; delayMicroseconds (time_3); DDRB = B00001001; delay (time_2); break; case 7: DDRB = B00001111; PORTB = B00010111; delayMicroseconds (time_3); DDRB = B00000111; delay (time_2); break; case 8: DDRB = B00011111; PORTB = B00000001; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; case 9: DDRB = B00011011; PORTB = B00000101; delayMicroseconds (time_3); DDRB = B00000001; delay (time_2); break; } DDRB = B00011111;//   PORTB = B00011110; delay (5); } 


A experiência mostra que o indicador pode ser reduzido para até 21; somente então o MK entra no modo de redefinição e começa a trabalhar quando volta para cerca de 25 anos ou mais. Portanto, é possível criar um "indicador" muito errado para indicar tensão de 25 a 99 volts, é claro, com um divisor na entrada de medição.

Agora sobre a aplicação prática. A idéia original de exibir dados de um sensor de distância foi adiada para tempos melhores devido à falta de uma entrada digital. Por que mais você pode aplicar o esquema até que as idéias cheguem? Outra ressalva: não há dúvida de rentabilidade. Mesmo que você pague todos os segmentos, uma corrente de 2,5 mA fluirá através do resistor R2 (de acordo com o primeiro esquema), no total 10 mA para o indicador, mais o controle do transistor adicionará cerca de 5 mais. Eu não mencionei, o transistor de quase qualquer pnp é moderno.

Sobre conveniência. A opção mais barata de saída para os sete segmentos é ATtiny13 mais 74HC595. Dois casos de SMD me custarão aproximadamente 0,50 cu O mais simples é o ATmega8 (e é isso, sem resistores, nada mais), é 0,68 cu E a opção descrita acima é o custo do ATtiny, 9 resistores, 4 LEDs, um transistor (todos os SMDs) - isso é cerca de 0,46 cu, embora seja peça por peça, tudo fica mais caro às vezes. Além disso, juntar tudo é mais complicado do que nas versões anteriores.

Na verdade, a única opção que posso ver é se você possui um ATtiny13 completo, mas ainda precisa ir à loja do ATmega. Bem, se um indicador de sete segmentos é a decoração principal do seu dispositivo, eu não recomendaria esse esquema, a exibição não é perfeita, em algumas combinações os segmentos desnecessários são destacados levemente. Ocorre que a indicação é ocasionalmente necessária durante a instalação e depois no próprio local.

Em geral, passei alguns dias em vão.

Além das críticas, estou aguardando sugestões para melhorar o código e simplificar o esquema. Ou funcionalidade melhorada sem complicações. O que mais me interessa é como esclarecer um ponto, isso ampliaria o escopo. Mas se também pudéssemos liberar uma conclusão dos 5 envolvidos, seria possível vagar.

Ficaria feliz se minha solução fora do padrão beneficiaria alguém, agora não, algum dia.

UPD: Eu sei sobre Charliplexing! O esquema de terceiros que citei no início deste artigo é Charliplexing. E lá escrevi por que Charliplexing não é adequado.
Por favor, não escreva comentários se você ler o artigo na diagonal.

Source: https://habr.com/ru/post/pt446796/


All Articles