LusoRobótica - Robótica em Português

Sistemas específicos => Arduino / AVR => Tópico iniciado por: CBX em 10 de Julho de 2014, 21:38

Título: Mini "Pseudo" Osciloscópio
Enviado por: CBX em 10 de Julho de 2014, 21:38
Boas

Já por várias vezes que precisei de um osciloscópio para coisas básicas, mas nunca comprei nenhum por ser uma ferramenta cara e que iria ficar encostado por ser algo que raramente uso...

Quando não se tem o dinheiro improvisa-se, decidi fazer um brinquedo com componentes que por aqui tenho e ir evoluindo a coisa até se tornar algo minimamente usável.

Material que estou a usar:

O plano é começar com o avr, com que estou mais familiarizado, com o ADC interno, de seguida juntar um ADC externo que já tenho montado na breadboard ao lado, é um TLC5510, 20MSps, 8 bit, output paralelo, já tenho a parte analógica a funcionar, mas vai ficar encostado para já até ter tudo a funcionar com o ADC interno, assim que tiver o atmega a funcionar com o adc externo a ideia é portar o código para um LPC1313 (cortex m3) e por fim deitar as mãos a um LPC4370 que tem um ADC interno de 80MSps

Por agora, comecei por escrever o código para o SPI e o driver para o TFT, por fim o código do ADC, neste momento já consigo mostrar uma onda no ecrã, em seguida vou tentar fazer um trigger simples e é aqui que preciso de ajuda, ideias precisam-se  ;D

Código fonte:
Código: [Seleccione]
#define F_CPU 20000000UL
#include <avr/io.h>
#include <util/delay.h>
#include "ST7735.h"
#include "ADC.h"

uint8_t amostra[160], amostraAntiga[160];

int main(void)
{

ST7735_init();
ADC_init();

while(1)
{
for (int i = 0; i < 160; i++)
amostraAntiga[i] = amostra[i];

for (int i = 0; i < 160; i++)
amostra[i] = ADC_read(0)/2;

for (int i = 0; i < 160; i++)
{
ST7735_draw_pixel(amostraAntiga[i], i, COLOR_BLACK);
ST7735_draw_pixel(amostra[i], i, COLOR_GREEN);
}
}
}

O código é bastante simples, são lidas 160 amostras o mais rápido possível (comprimento em pixeis do ecrã) e os pixeis são escritos no lcd um a um, para apagar o conteúdo do ecrã o mais rápido possível no inicio do loop copio o array com as amostras lidas para outro antes de ser renovado e antes de um pixel ser "escrito" o pixel na mesma posição horizontal é apagado

O ADC está a funcionar com um clock de 1,25Mhz (20Mhz/16), o máximo recomendado penso que seja 1Mhz, mas não é critico neste ponto...

Em funcionamento a mostrar uma onda quadrada de ~450Hz do PWM de um atmega8:

(https://lusorobotica.com/index.php?action=dlattach;topic=7474.0;attach=2799;image)

Projecto do atmel studio 6.1 em anexo

O que acham?
Título: Re: Mini "Pseudo" Osciloscópio
Enviado por: senso em 10 de Julho de 2014, 22:06
Conheces o XprotoLab?
Se não, pesquisa, tem código disponivel e faz exactamente o que queres fazer.
O trigger é onde está a magia de um osciloscópio, e por software não será de todo super fiavel, convinha teres um comparador externo.
Título: Re: Mini "Pseudo" Osciloscópio
Enviado por: CBX em 10 de Julho de 2014, 22:14
sim conheço o xprotolab, tive a olhar para o esquemático e parece que o trigger é feito por software, vou dar uma vista de olhos no código...

de qualquer forma devo poder usar o comparador interno do atmega...
Título: Re: Mini "Pseudo" Osciloscópio
Enviado por: senso em 10 de Julho de 2014, 22:41
Se estiver depois do condicionamento de sinal, possivelmente, depois precisas é de um DAC para mudar o ponto de comparação.
O xprotolab é por software, referi porque sempre terás código para tirar umas ideias.
Título: Re: Mini "Pseudo" Osciloscópio
Enviado por: CBX em 11 de Julho de 2014, 17:46
fui ler o código do xprotolab e não consegui perceber nada, o código é extremamente difícil de ler mesmo com os comentários, fui então investigar e encontrei esta explicação bastante boa: http://www.radio-electronics.com/info/t_and_m/oscilloscope/oscilloscope-trigger.php (http://www.radio-electronics.com/info/t_and_m/oscilloscope/oscilloscope-trigger.php)

após alguma diarreia cerebral temos um trigger funcional, acrescentei o seguinte código ao loop principal:

Código: [Seleccione]
max = 0;
min = amostra[0];

for (int i = 0; i < 160; i++) // encontrar o valor minimo e maximo das amostras
{
if (amostra[i] > max)
max = amostra[i];

if (amostra[i] < min)
min = amostra[i];
}

if (max != min)
trigger = (((max - min) / 2) + min); // o valor do trigger será o meio da onda capturada
else
trigger = max;

flag = 1;
contador = 0;
amostra2Antiga = 0;
passo = 0;

for (int i = 0; i < 10000; i++) // loop até que o inicio da onda capturada se repita
{
amostra2 = ADC_read(0)/2;

if ((passo == 0) && (amostra2 > trigger) && (amostra2Antiga < amostra2)) // encontrar o inicio do periodo
passo = 1;

if ((passo == 1) && (amostra2 < trigger) && (amostra2Antiga < amostra2)) // encontrar o "rising edge"
passo = 2;

if ((passo == 1) && (amostra2 > trigger) && (amostra2Antiga < amostra2)) // quando o inicio do proximo periodo for encontrado
break;         // iniciar novamente a amostragem
}

basicamente calcula o meio da onda capturada inicialmente e um novo loop vai lendo amostras até encontrar o novo período.

o próximo passo é adicionar dois botões para aumentar\diminuir o delay entre a captura das amostras

em que biblioteca do avrgcc é que estão as variáveis do tipo booleano?
Título: Re: Mini "Pseudo" Osciloscópio
Enviado por: senso em 11 de Julho de 2014, 17:55
Penso que nativamente não tem, faz uma struct e usas um byte para meter 8 bools, a nivel de velocidade de código vai dar exactamente ao mesmo.
Título: Re: Mini "Pseudo" Osciloscópio
Enviado por: dropes em 07 de Outubro de 2014, 11:17
Evita usar divisões ou multiplicações, neste caso shift right funcionaria para as divisões por 2 já que os números são inteiros.
Recomendaria assembler para alguns procedimentos, mas isso já é um pouco distante...
Título: Re: Mini "Pseudo" Osciloscópio
Enviado por: LuísR.A. em 07 de Outubro de 2014, 12:18
Vi fazerem um osciloscopio, obviamente tambem assim  muito fraco com um MSP430 mais fraco que o atmega328 e não foi preciso assembly... mas foi em acesso directo de registos
Título: Re: Mini "Pseudo" Osciloscópio
Enviado por: Njay em 07 de Outubro de 2014, 12:18
typedef unsigned char bool;

Quanto às divisões/multiplicações por uma constante, quase de certeza que o GCC a compilar com optimizações ligadas reconhece a operação por uma potência de 2 e optimiza para um shift. Mas pode-se olhar para o .LST (que tem o assembly) e confirmar. Aí até poderás perceber se o código está a sair mesmo optimizado ou se está a haver alguma "promoção" a int aí pelo meio levando a código maior.

p.s. Aliás, estás a usar um contador do tipo int no for; o tipo int são 16 bits, logo o teu ciclo de aquisição for é bastante lento face ao que podia ser, mas por outro lado estás a usar uma função para a leitura do ADC e isso deita logo por terra qualquer desejo de performance (a não ser que a função esteja implementada com performance em mente, do género ser uma macro etc).
Título: Re: Mini "Pseudo" Osciloscópio
Enviado por: senso em 07 de Outubro de 2014, 13:17
Isso devia correr praticamente tudo na interrupção do ADC, mas o ADC do atmega tambem é horrivelmente lento..
Título: Re: Mini "Pseudo" Osciloscópio
Enviado por: CBX em 07 de Outubro de 2014, 17:20
Antes de mais isto é um "proof of concept", não tem em mente performance, também não conseguia retirar muita do ADC interno do atmega...

Tenho duas opções, utilizar um ADC externo que aqui tenho de 20Msps (estaria limitado a 10) ou saltar directamente para o LCP4370 que tem um ADC interno de 80Msps, que para além de ser um cortex m4 tem mais dois cores cortex m0 em que um deles poderia estar encarregue do lcd e o outro dos inputs por exemplo.

Evita usar divisões ou multiplicações, neste caso shift right funcionaria para as divisões por 2 já que os números são inteiros.
Recomendaria assembler para alguns procedimentos, mas isso já é um pouco distante...

O meu assembly é muito mau e penso que o incremento de performance não seria assim tão grande se o compilador estiver a fazer o seu trabalho como deve ser

typedef unsigned char bool;

Quanto às divisões/multiplicações por uma constante, quase de certeza que o GCC a compilar com optimizações ligadas reconhece a operação por uma potência de 2 e optimiza para um shift. Mas pode-se olhar para o .LST (que tem o assembly) e confirmar. Aí até poderás perceber se o código está a sair mesmo optimizado ou se está a haver alguma "promoção" a int aí pelo meio levando a código maior.

p.s. Aliás, estás a usar um contador do tipo int no for; o tipo int são 16 bits, logo o teu ciclo de aquisição for é bastante lento face ao que podia ser, mas por outro lado estás a usar uma função para a leitura do ADC e isso deita logo por terra qualquer desejo de performance (a não ser que a função esteja implementada com performance em mente, do género ser uma macro etc).

Não tenho nenhum ficheiro .LST, o mais parecido é um .LSS e parece que está a optimizar, um excerto:

Código: [Seleccione]
for (uint8_t i = 0; i < 160; i++)
{
amostra[i] /= 2;
 1ce: f6 01        movw r30, r12
 1d0: 80 81        ld r24, Z
 1d2: 86 95        lsr r24
 1d4: 81 93        st Z+, r24
 1d6: fc 83        std Y+4, r31 ; 0x04
 1d8: eb 83        std Y+3, r30 ; 0x03
 1da: 48 01        movw r8, r16
 1dc: 8a 18        sub r8, r10
 1de: 9b 08        sbc r9, r11

Entretanto substituí todos os int por uint8_t, expecto uma das variáveis que tem de ser de 16bits

Isso devia correr praticamente tudo na interrupção do ADC, mas o ADC do atmega tambem é horrivelmente lento..

Podes elaborar?  :P

Obrigado pelas sugestões
Título: Re: Mini "Pseudo" Osciloscópio
Enviado por: Njay em 07 de Outubro de 2014, 17:24
Sim, nessa gama de preço era preferível uma placa de prototipagem PSoC4, sempre tem um ADC de 1Msps e ampops e interface USB, e ainda DACs e comparadores de tensão + alguma lógica programável que deve ser fixe para implementar o trigger em hw.

Mas tamos a falar dum AVR :) . Mostra lá a tua função de leitura do ADC, CBX. 1.25MHz de clock no ADC é na boa, o que se perde são bits de resolução, mas para esta aplicação não é critico perder 2 ou 3 bits.
Título: Re: Mini "Pseudo" Osciloscópio
Enviado por: Njay em 07 de Outubro de 2014, 17:32
Tens espaço para código, o mais rápido é desdobrar o ciclo

amostra[0] = ADC_read(0);
amostra[1] = ADC_read(0);
amostra[2] = ADC_read(0);
....
amostra[159] = ADC_read(0);

Isto quando tiveres um ADC externo, o do mega é lento portanto é quase irrelevante.

O código assembly que aí tens já é depois da tua alteração para uint8_t, portanto não vês a diferença.
Mas que ciclo é esse do excerto de assembly? Não vejo esse código no que já colocaste aqui.
Título: Re: Mini "Pseudo" Osciloscópio
Enviado por: CBX em 07 de Outubro de 2014, 17:51
o que eu queria destacar era a divisão estar a ser optimizada.

o adc já está a funcionar a 1,25MHZ (20Mhz / prescaler de 16)

o código está todo no primeiro post, mas fica apenas o do ADC:

Código: [Seleccione]
#include <avr/io.h>

void ADC_init()
{
ADMUX |= (1<<REFS0) | (1<<ADLAR); // AVCC as reference voltage, value left adjusted; comment ADLAR for full 10 bit
ADCSRA |= (1<<ADPS2) | (1<<ADEN) | (1<<ADSC); // prescaler set to 16, enable ADC, free-running mode, start conversion
}

uint8_t ADC_read(uint8_t c) // should be uint16_t if working with 10 bit values
{
ADMUX &= 0xF0; // clear the older channel
ADMUX |= c; // select the channel
ADCSRA |= (1<<ADSC); // start conversion
while(ADCSRA & (1<<ADSC)); // wait until ADC conversion is completed
return ADCH; // 8 bit only
//return ADCW; // 10 bit value
}

Já agora aproveito para perguntar, o ADC que aqui tenho é um TLC5510 e pelo que percebi da datasheet tenho de ler os outputs no falling edge do sinal de clock, se usar o PB0 como clock output existe algum registo que me diga quando devo ler o porto completo?

obrigado  ;)
Título: Re: Mini "Pseudo" Osciloscópio
Enviado por: senso em 07 de Outubro de 2014, 18:03
Usas interrupção de conversão completada e metes o sample novo para um array/buffer e siga.
Comparações lógicas e mais umas coisas no gcc são promovidos a 16bits para o caso de haver carry, nem sempre é simples de seguir o porquê de o compilador o fazer, mas tipicamente tentar ser mais esperto que o compilador corre mal, até porque já dizia o Knutt algo sobre optimização prematura..

Se isso é SPI tens os CPOL e outro registo, basicamente existem 4 modos de SPI, está na datasheet como configurar isso.
Título: Re: Mini "Pseudo" Osciloscópio
Enviado por: CBX em 07 de Outubro de 2014, 18:10
não é SPI, são 8 bits paralelos e estou a falar da função CLKO do pino PB0 que nada tem a haver com o PB5 (SPI), na datasheet só consigo encontrar 2 capítulos com informação desta função, 8.9 e 13.3.1 ao fim
Título: Re: Mini "Pseudo" Osciloscópio
Enviado por: CBX em 09 de Outubro de 2014, 20:44
e se usar um timer como fonte de clock do ADC, já tenho acesso a algum registo que me detete o falling edge?
Título: Re: Mini "Pseudo" Osciloscópio
Enviado por: senso em 09 de Outubro de 2014, 23:12
Penso que não, o que podes fazer, é ligar o pino de saida a um de entrada e gerar uma interrupção na falling edge, a bit crusty I know..
Título: Re: Mini "Pseudo" Osciloscópio
Enviado por: Njay em 10 de Outubro de 2014, 00:23
Não tou a perceber muito bem do que tás a falar, mas tens interrupções de compare match, se gerares o clock a partir de PWM dum contador (parece-me lento, se calhar não é a isso que te referes) o compare match ocorre quando (por exemplo) o pino vai a low.

p.s. Já agora, se falas dum chip menos corriqueiro convém deixar logo um link para a datasheet ;)
Título: Re: Mini "Pseudo" Osciloscópio
Enviado por: CBX em 10 de Outubro de 2014, 02:10
http://www.ti.com.cn/cn/lit/ds/symlink/tlc5510.pdf (http://www.ti.com.cn/cn/lit/ds/symlink/tlc5510.pdf) também não é assim muito difícil de encontrar :P

já agora do atmega: http://www.atmel.com/Images/doc8161.pdf (http://www.atmel.com/Images/doc8161.pdf)

a minha ideia é ter o porto D do atmega328 a ler os 8 outputs do ADC simultaneamente, para os ler tenho de o fazer no falling edge do clock e para isso tenho de detectar o falling edge de alguma forma para saber o momento exacto de leitura (pelo menos foi o que percebi ao ler a datasheet), o clock é fornecido pelo atmega, inicialmente pensei em usar a função CLKO do pino PB0, que pelo que percebi é mesmo essa a função dele (pagina 35 da datasheet do atmega), mas não encontro nenhuma forma de detectar o falling edge de forma simples, quais são as minhas alternativas?

penso que me expliquei bem   :P
Título: Re: Mini "Pseudo" Osciloscópio
Enviado por: Njay em 10 de Outubro de 2014, 02:50
O ADC dá inicio à actualização da saída digital no flanco ascendente do clock, demorando td(D) (máx 30ns para as condições que eles indicam). Portanto podes ler os dados em qualquer ponto do intervalo de tempo que vai desde flanco-ascendente + td(D) até ao próximo flanco ascendente, por exemplo no flanco descendente como referes. Não tens como acertar a operação de leitura do porto com um flanco do clock à escolha, mas tás com "sorte" :) ; o mega fá-lo no flanco descendente (e isso não dá para mudar). Esta informação está na secção de portos (13.2), em particular lê a secção 13.2.4 e mantém a figura 13-2 à mão.

Na verdade não é bem "sorte". Em sistemas digitais costuma-se actualizar as saídas num dos flancos e fazer as leituras no outro, e assim tudo flui rapidamente.

De qualquer forma podias sempre "acertar o flanco" externamente, introduzindo um inversor no CLKO para o ADC.
Título: Re: Mini "Pseudo" Osciloscópio
Enviado por: CBX em 10 de Outubro de 2014, 16:31
Sendo assim posso ler o ADC a qualquer altura que vai estar sempre "sincronizado"?

Por essa é que não esperava, adoro quando as coisas estão bem pensadas  ;D

Agora esta parte é que vai dar luta, o front end, não percebo muito de eletrotécnica analógica, mas penso que o meu raciocínio está correcto:

(https://lusorobotica.com/index.php?action=dlattach;topic=7474.0;attach=2991)

O input está pensado para uma tensão de +\-20v ou 40vpp, o divisor resistivo baixa o sinal para 1vpp, o primeiro opamp é um simples buffer, o segundo está configurado como amplificador não-inversor com ganho de 2 que aumenta o sinal para 2vpp, que é a grandeza para que o input ADC está preparado, por fim preciso de centrar o GND nos 1,6v, uma vez que o Vtop do ADC é de 2,6v e o Vbottom é de 0,6v, para isso usei um transístor e um pot para criar um offset de -1,6v

Não simulei o circuito, tentei usar o ltspice, mas não consegui, testei na breadboard e parece-me estar tudo a funcionar

Que me dizem?
Título: Re: Mini "Pseudo" Osciloscópio
Enviado por: Njay em 10 de Outubro de 2014, 17:07
Também não posso falar do front-end, mas não explicaste os filtros que aí tens (nem disseste que performance queres atingir) e quanto à secção de atenuação na entrada acho que devias pelo menos meter mais um switch ("sem atenuação") para teres mais resolução em tensões baixas e se calhar devias ter alguma protecção da entrada, uns TVS ou coisa do género. Leitura muito interessante aqui, http://www.davmar.org/concepts.html (http://www.davmar.org/concepts.html) , relativo a front ends tens o "Horizontal Amplifier Circuits".