entao, eu trabalho bastante com sistemas realtime no dia a dia e tem N formas de fazer isso, algumas mais sofridas, outras mais simples.
daih vem uma pergunta: o microcontrolador tem um interface I2C dedicada ou vc faz via GPIO? qual a taxa de amostragem que vc precisa? nada critico acho eu, eh apenas para balizar o raciocinio a seguir... independente do caso, suponho que consiste em algo atomico como:
REPLY = i2c_request(REQUEST);
se for um I2C dedicado, provavelmente eh algo como colocar REQUEST em um buffer de transmissao, setar um bit e entao ficar lendo um flag de status enquanto o hardware serializa aquela mensagem e espera o periferico serializar a resposta, algo como:
- Código: Selecionar todos
int i2c_request(int REQUEST)
{
I2C_XFIFO = REQUEST;
I2C_CTRL.XMIT = 1;
while(I2C_STAT.BUSY); // espera o HW enviar e receber os dados serializados naquela cadencia lerda do I2C
return I2C_RFIFO;
}
um processo demorado em que ele fica esperando a resposta (vc comentou em torno de 2ms). no caso de GPIO, nao sei se o microcontrolador nao eh rapido demais para gerar um I2C diretamente, entao acredito que seja algo como:
- Código: Selecionar todos
switch(i2c_state)
case 0:
GPIO.PTB0 = 0; // CLK = 0
GPIO.PTB1 = REQUEST&1; // DAT = bit0
usleep(DELAY); // delay para nao exceder a velocidade maximo do periferico
GPIO.PTB0 = 1; // CLK = 1
break;
case 1:
GPIO.PTB0 = 0; // CLK = 0
GPIO.PTB1 = REQUEST&2; // DAT = bit1
usleep(DELAY); // delay para nao exceder a velocidade maximo do periferico
GPIO.PTB0 = 1; // CLK = 1
break;
...
na pratica, ele fica a maior parte do tempo em um while(1) fazendo hora para dar o delay ali. de fato, em ambos os casos, daria para dizer que o microcontrolador fica a maior parte do tempo em idle, esperando. eh normal, eu vejo no meu dia a dia processadores cavalares multicore na mesma situacao: em idle esperando. a ideia eh aproveitar melhor esse tempo e vc consegue isso colocando prioridades. e no caso, temos I2C e refresh do display. eu diria que colocar o refresh do display como maior prioridade eh legal, pq eh facil de granular a tarefa. granular o I2C eh possivel, mas eh um pouco mais complicado... como soh tem dois processos, eh mais facil seguir o caminho mais facil.
assim, permitir a interrupcao de refresh do display em cima desses processos nao deve causar problemas, desde que a interrupcao de refresh do display seja jogo rapido. se vc codificar o I2C via GPIO, o que vai perceber eh um pequeno jitter nos sinais, justamente referente a execucao da interrupcao de refresh de display. escapar desse jitter, soh mesmo em uma FPGA, onde eh possivel ter o hardware todo em paralelo... e olhe lah! mesmo um sistema todo paralelizado e pipelinezado acaba tendo seus interlocks no meio da logica! se fosse tudo assim facil, eu jah estaria trilionario e estaria construindo uma estacao espacial para conquistar a galaxia! \o/ hahaha
no seu caso, eu faria o seguinte: calcularia um "tic" para o sistema, digamos, de 3kHz, como vc comentou. a vantagem de trabalhar com um numero inteiro eh que vc consegue derivar "soft timers" a partir desse "tic". nessa interrupcao do "tic" eu faria um unico passo de refresh do display, ou seja, desligaria um segmento e ativaria o proximo segmento. tem que ser um negocio bem montado e bem otimizado, mas acho que como vc jah pescou como fazer o refresh de 2 partes, fazer parte por parte nao deve ser complexo. seria tipo uma funcao em que cada vez q vc entra, ela desliga o segmento atual e ativa o proximo, algo assim:
- Código: Selecionar todos
void refresh_display()
{
static int count_display = 0;
static int count_segment = 0;
turn_off(count_display,count_segment);
count_segment++;
if(count_segment == 8)
{
count_segment = 0;
count_display++;
if(count_display == 6)
{
count_display=0;
}
}
turn_on(count_display,count_segment);
}
as funcoes turn_off() e turn_on(), no caso apenas desativam e ativam exatamente 1 segmento, indicado na chamada da funcao, onde eh passado tb o display em uso. dah para otimizar esse codigo bem! fiz as contas ali para 7 segmentos x 5 displays. como eh o processo mais rapido (3kHz), ficaria na interrupcao e ficaria bem deterministico. tambem na mesma interrupcao eu decrementaria um contador, que divide os 3kHz, digamos, por 30 (contando de 29 a 0), resultando em uma sub-contagem de 100Hz. digamos que essa contagem de 100Hz eh usava para coletar dados do I2c:
- Código: Selecionar todos
int i2c_timer_req;
int i2c_timer_cnt;
interrupt tic()
{
refresh_display();
if(!i2c_timer_cnt--)
{
i2c_timer_cnt=29;
i2c_timer_req++;
}
return;
}
note que soh tem o refresh do segmento do display como processamento. no caso do i2c, apenas incremento uma variavel, mas nao perco tempo fazendo o i2c aqui para nao lockar na interrupcao pelos 2ms que vc comentou. para ter uma contagem, digamos de 1 segundo, vc faria na mesma interrupcao algo similar, mas nesse caso a contagem eh de 2999 a 0:
- Código: Selecionar todos
int 1sec_timer_req;
int 1sec_timer_cnt;
interrupt tic()
{
...
if(!1sec_timer_cnt--)
{
1sec_timer_cnt=2999;
1sec_timer_req++;
}
...
soh como exemplo mesmo, depois uso lah no outro loop para mostrar como encadear essas coisas... novamente, apenas incremento uma variavel. a ideia eh que o codigo na interrupcao eh bem rapido. na pratica, ele faz o minimo possivel. soh faco o refresh do display aqui pq esta EH a tarefa em realtime: se vc fizer de outra forma, vai ter um grande jitter. e jitter nesse caso implica em maior corrente/brilho para um segmento do que para outro. para eles ficarem perfeitamente homogeneos, eu diria que tem que ser feito esse refresh aqui na interrupcao! mas acho que jah escrevi sobre isso acima hehehe
lah no processo principal eu teria tarefas que nao sao realtime:
- Código: Selecionar todos
main()
{
GPIO.PTA0 = 1; // red led on
startup_sequence(); // funcao que inicializa todas as tranqueiras
GPIO.PTA0 = 0; // red led off
interrupt_enable();
GPIO.PTA1 = 1; // green led on
while(1)
{
if(i2c_timer_req)
{
REPLY = i2c_send(REQUEST);
uart_send_data(REPLY);
i2c_timer_req--;
}
// else <- se eu colocar esse else, eu priorizo a tarefa de cima sobre a debaixo! :O
if(1sec_timer_req)
{
GPIO.PTA1 ^= 1; // green led blink = good! :D
if(1sec_timer_req!=1) // more than 1 credit! :O
GPIO.PTA0 = 1; // red led on! panic! :O
if(GPIO.PTA2==1)
GPIO.PTA0 = 0; // alarm override! :'(
1sec_timer_req--;
}
}
}
eu estou meio enferrujado nessas coisas, mas enfim, eh um sistema de "creditos": quando a interrupcao ocorre lah em cima, os timers incrementam variaveis que dao creditos para o codigo aqui embaixo. quando ocorrem 30 contagens do tic, a variavel de i2c eh incrementada. aqui embaixo, se houver credito na variavel, a tarefa do i2c roda e gasta o credito (decrementa a variavel). eh tipo um master/slave: a interrupcao dah creditos, o loop principal gasta eles. se vc ativar um GPIO cada vez que a tarefa de i2c roda, vc vai ver que ela roda a 100Hz. a outra tarefa roda a cada 1 segundo e analisa dados de performance. no caso, espera-se que existe apenas 1 credito disponivel sempre. se houver mais de 1, eh pq o processador ficou mais de 1 segundo ocupado e essa tarefa nao rodou no tempo dela! se tudo estiver normal, um led verde pisca, do contrario, um led vermelho de alarme ativa para permitir que o operador veja a falha e aperte um botao para desligar o alarme!
caraca! isso ficou tao profissa que merecia um "selo dollynho de excelencia corporativa"! huahuahua
ze escreveu:Nem vi que moderador tinha migrado pra cá. Título nada a ver pode ser alterado de acordo com o desenrolar ou sua vontade
MOR_AL escreveu:Zé.
Tudo bem.
MOR_AL
Moris, tudo bem se eu considerar isso como "perdão. realmente a intenção foi te ajudar" E "seu devaneio noturno tem sentido" ? ok.. considerado
Por uma destas raras boas coincidências do destino, um comprador nosso veio até mim com um display 7seg pra ser homologado. Testei no projeto novo, ele tem um brilho bom com 330R @ 5V com 5 sendo multiplexados a 400Hz, cada um oscila a 80Hz. Mesmo assim penso que ao acender 8 ele abusa um pouco da capacidade de corrente do mc. O que me remonta à ideia do Marcelo San...
M.San... acho que vou testar sua ideia de acender um por vez. Tudo a ver a ideia de dar exclusividade de corrente ao pino. Mas o meu desafio é:
-tenho que fazer uma interrupção de uns 3Khz pros 35 segmentos, ou seja , a cada 330uS o sistema seria interrompido pra tratar dos segmentos. Apesar do mc estar a 16Mhz, pretende-se ter pouca tarefas na interrupt.
-o mc tem que ler o sensor de temperatura em I2C e este leva um tempo de ~2mS. Este tempo "parado" deve comprometer a demonstração do display. De fato, não sei como o hw do I2C se comportaria sendo interrompido
Se vc (ou vc) tiver alguma ideia, por gentileza não te acanhes em expô-la. Algo como menos freq de interrupt, ao invés de 1, acender 2 ou 3 segmentos, ou algo do gênero
Agradeço!