collapse

* Posts Recentes

Emulador NES em ESP32 por dropes
[22 de Abril de 2024, 14:14]


Arame de Estendal por almamater
[18 de Abril de 2024, 16:16]


O que é isto ? por SerraCabo
[12 de Abril de 2024, 14:20]


Amplificador - Rockboard HA 1 In-Ear por almamater
[11 de Abril de 2024, 20:46]


Meu novo robô por josecarlos
[29 de Março de 2024, 18:30]


Bateria - Portátil por almamater
[25 de Março de 2024, 22:14]


Escolher Osciloscópio por jm_araujo
[06 de Fevereiro de 2024, 23:07]


TP4056 - Dúvida por dropes
[31 de Janeiro de 2024, 14:13]


Leitura de dados por Porta Serie por jm_araujo
[22 de Janeiro de 2024, 14:00]


Distancia Cabo por jm_araujo
[08 de Janeiro de 2024, 16:30]

Autor Tópico: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]  (Lida 116638 vezes)

0 Membros e 1 Visitante estão a ver este tópico.

Offline senso

  • Global Moderator
  • Mini Robot
  • *****
  • Mensagens: 9.733
  • Helpdesk do sitio
Está no datasheet, mas tenho ideia que é entre 8 e 16Mhz.
Avr fanboy

Offline CBX

  • Mini Robot
  • *
  • Mensagens: 1.315
após uma leitura mais atenta da datasheet encontrei os valores correctos: de 3 a 8mhz o valor de CKSEL3 a 1 é 1, o de CKSEL0 também 1, SUT1 1 e SUT0 0 enquanto o CKOPT fica a 1.

corrigindo os valores do outro post:

low bits 1110 1111 - 0xEF
high bits fica default - 0x99

Código: [Seleccione]
avrdude -c usbasp -p m16 -U lfuse:w:0xef:m -U hfuse:w:0x99:m
parece estar a funcionar como deve ser  ;D

a preguiça de não ler a datasheet como deve ser ia dando #$%&€  ;D

cumps e obrigado

Offline senso

  • Global Moderator
  • Mini Robot
  • *****
  • Mensagens: 9.733
  • Helpdesk do sitio
Ainda bem ;)
Qualquer duvida que tenhas, vai postando por aqui para a informação e duvidas ficarem juntas.
Provavelmente se tiver tempo no domingo irei escrever sobre o conversor analógico-digital, mas ainda não sei se vou ter tempo.
Depois disso, timers.
Tens alguma sugestão a fazer?
Avr fanboy

Offline CBX

  • Mini Robot
  • *
  • Mensagens: 1.315
parece-me bem como tens planeado, também não posso falar muito porque pouco sei  ;D

cumps

Offline GnGz

  • Mini Robot
  • *
  • Mensagens: 665
Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
« Responder #94 em: 22 de Novembro de 2010, 19:47 »
Boas , e como se pode programar atmeis em ambiente linux?

Offline senso

  • Global Moderator
  • Mini Robot
  • *****
  • Mensagens: 9.733
  • Helpdesk do sitio
Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
« Responder #95 em: 22 de Novembro de 2010, 21:15 »
O avrdude funciona tal e qual como no linux, o compilador tambem, o avr-gcc e o avr-libc, depois é linha de comandos, mas para projectos com multiplos ficheiros .c e .h como por exemplo bibliotecas tens de ser tu a fazer um makefile ou a passar tudo na linha de comandos quando compilas, vai ao avr-freaks que está como sticky a explicação de como montar uma toolchain em linux.
Avr fanboy

Offline Cynary

  • Mini Robot
  • *
  • Mensagens: 182
Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
« Responder #96 em: 22 de Novembro de 2010, 22:01 »
No linux, tens outra opção para programar AVRs: usar um IDE com plugin para AVRs. Neste momento uso o eclipse, e este tem um plugin para AVR que permite compilar os ficheiros, compilar vários ficheiros (faz o makefile automaticamente) e ainda fazer o upload para o AVR (para cada projecto configura-se qual o mcu, as flags que se passa ao compilador/avrdude, e qual o programador que se tem).
Mais informações: http://avr-eclipse.sourceforge.net/wiki/index.php/The_AVR_Eclipse_Plugin

Offline senso

  • Global Moderator
  • Mini Robot
  • *****
  • Mensagens: 9.733
  • Helpdesk do sitio
Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
« Responder #97 em: 31 de Dezembro de 2010, 04:40 »
   Bom dia caros colegas, hoje o nosso tema de estudo será o conversor analógico-digital do nosso arduino, mais conhecido por ADC.
   Um conversor analógico-digital permite-nos ler um sinal analógico com o nosso micro-controlador ou um chip com um ADC e transformar esse sinal analógico num sinal digital que varia consoante a voltagem que queremos medir, a titulo de exemplo e as coisas mais comuns que regra geral geram um sinal analógico são: potenciómetros, LDR's, sensores de temperatura, sensores de humidade, acelerómetros, giroscópios, sensores de pressão e muitos outros sensores.

   Mais especificamente o nosso atmega328p tem um conversor analógico digital integrado que nos permite efectuar este tipo de medições sem adicionar-mos nenhum ou quase nenhum hardware para além do micro-controlador e do sensor em questão, este conversor está ligado a 6 pinos através de um circuito chamado multiplexer, que permite ligar muitas entradas a apenas um conversor adc, mas isto trás uma limitação, que é o facto de apenas pudermos medir um canal/pino de cada vez, no caso do atmega328p os pinos que estão ligados a este multiplexer são todos os pinos do porto C.
   Como o nosso chip funciona a 5v então pudemos colocar em cada um destes pinos um sinal que varia entre 0 e 5v (podemos usar outro valor para a voltagem máxima, colocando uma voltagem no pino 21 do nosso atmega, chamado de Aref e configurando no código o ADC para ter em conta esta voltagem e não os 5v do resto do chip, mas por agora não vamos explorar essa opção), o ADC do nosso atmega tem ainda outra característica chave, que é o facto de ser um ADC de 10bits, isto quer dizer que a nossa voltagem de entrada irá ser convertida num numero que varia entre 0 e 1023, o que com uma voltagem de 5v faz com que a resolução seja de 5v/1023 = 4.88mV para cada bit, mas podemos também configurar o ADC para ter uma resolução de apenas 8bits, o seja 255 valores diferentes, que nos dá uma resolução de 19,6mV por bit, apesar a resolução ser muito menor que no modo de 10 bits pode ser util para aplicações especificas.
   Podemos também configurar o nosso conversor para fazer uma conversão de cada vez, ou para estar continuamente a fazer conversões sucessivas, a primeira opção é melhor quando queremos ler vários pinos diferentes, enquanto que a segunda é melhor para quando só lemos um pino e não andamos a saltar de pino em pino para ler vários sensores.
   Temos ainda de ter em atenção que o conversor ADC tem uma frequência máxima de funcionamento, que vem especificada na datasheet e que é de 200Khz, esta frequência é gerada ao dividir a frequência de relógio do atmega, que no meu/nosso caso é de 16Mhz, como não temos todos os valores de divisores á nossa disposição a frequência máxima a que podemos usar o conversor ADC é de 125Khz, podemos usar frequências maiores mas ai deixamos de ter os nossos 10bits de resolução a passamos a ter menos, se usarmos uma frequência de 250Khz, que é o dobro posso garantir que têm 8 bits de resolução e ai podemos usar o nosso conversor no modo de 8bits, mas frequências mais elevadas vão diminuir ainda mais a nossa resolução e ai os dados convertidos deixam basicamente de fazer sentido/ter utilidade.

   Vamos começar por fazer um simples programa de teste do nosso conversor, vai ler um potenciómetro de 10K ligado no porto C, pino 0, que corresponde ao analogic0 do arduino, e consoante o valor lido vai acender ou apagar o led que está no porto B, pino 5, ou seja o led do digital13.
   O potenciómetro será ligado da seguinte forma:

   Abram o AvrStudio, iniciem um novo projecto, e chamem-lhe por exemplo adc_test, o nosso pseudo-código será então assim:
Código: [Seleccione]
Configurar pino do led
Configurar conversor analógico-digital
Ligar o conversor ADC
Iniciar as conversões

Loop infinito{
Ler valor do conversor
Se valor_ADC superior a 512 ligar led
Se não desligar led
}

   Vamos começar por configurar o pino do nosso led:
Código: [Seleccione]
#include <avr/io.h>

int main(void){

DDRB |= (1<<PB5); //Define PB5/digital13 como saída digital

//TODO: Configurar conversor analógico-digital
//TODO: Ligar o conversor ADC
//TODO: Iniciar as conversões

for(;;){ //O nosso loop infinito
//TODO: Ler valor do conversor
//TODO: Se valor_ADC superior a 512 ligar led
//TODO: Se não desligar led
}

return 0;
}

   Até agora nada de novo, mas a partir daqui queremos configurar o nosso conversor, por isso, pegamos na nossa datasheet, e saltamos para o Capitulo 23 / página 251 que é o capitulo referente ao conversor analógico-digital.
   Primeiramente, e como já foi visto anteriormente temos de fornecer um sinal de relógio, e como já vimos esse sinal tem um valor máximo, para configuramos qual o sinal de relógio que é transmitido ao nosso conversor temos de mexer no registo chamado ADCSRA, e os bits que influenciam a o sinal do relógio são os bits ADPS2, ADPS1, e ADPS0, estes bits configuram um prescaller, que é um divisor de sinais de relógio, como temos de usar um relógio inferior a 200Khz e como só temos prescallers de 2,4,8,16,32,64 e 128, temos de usar o prescaller de 128, indo á página 256 da datasheet vemos que para activar esse valor temos de colocar os 3 bits de controlo do prescaller a 1.
   Em seguida temos de configurar qual é a nossa referência de comparação, mais uma vez, como foi dito anteriormente vamos usar a voltagem que está presente no resto do nosso atmega e que alimenta todo o circuito do conversor, essa configuração é feita no registo ADMUX, nos bits REFS1 e REFS0, que estão descritos na página 263, e que no nosso caso erá o REFS1 a 0 e o REFS0 a 1, é também no registo ADMUX que podemos seleccionar qual o canal/pino que vamos ler com o conversor, actuando sobre os bits MUX3, MUX2, MUX1, e MUX0, explicados na mesma página, neste pequeno código nem precisamos de mexer nos valores MUX3..0 pois estão definidos a 0 por defeito e como vamos ler o canal 0(analogic0 do arduino e PORTC0 na nomenclatura de portos e pinos do AVR) não precisamos de adicionar código redundante e desnecessário.
   Depois de configurar o conversor podemos liga-lo, e para isso temos de colocar o bit ADEN a 1 no registo ADCSRA e por fim colocamos o bit ADSC a 1 no mesmo registo para iniciar as conversões, ainda no registo ADCSRA, precisamos de colocar os bits ADTS2, ADTS1, ADTS0 a 0 pois estes registos definem quem é que origina uma nova conversão do ADC, e como queremos em modo free-runing, ou seja sempre a serem efectuadas novas conversões temos de os colocar todos a 0, para verem outros modos, mais uma vez a datasheet tem toda a informação, podemos por exemplo usar um timer para despoletar uma nova conversão, ou outras fontes, e depois temos o bit ADATE, que é o bit que liga ou desliga as conversões automáticas a partir de um sinal, que neste caso é o modo free-runing, vamos então passar ao código:

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

int main(void){

DDRB |= (1<<PB5); //Define PB5/digital13 como saída digital

ADCSRA |= ((1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)); //Prescaller a 128 para termos uma frequencia de 125Khz
ADMUX |= (1<<REFS0);
ADMUX &= ~(1<<REFS1); //Avcc como referência de voltagem
ADCSRB &= ~((1<<ADTS2)|(1<<ADTS1)|(1<<ADTS0)); //ADC no modo free-runing
ADCSRA |= (1<<ADATE); //Fonte de sinal para iniciar conversão, neste caso é o free-runing
ADCSRA |= (1<<ADEN); //Liga o circuito do ADC
ADCSRA |= (1<<ADSC); //Inicia as conversões

for(;;){ //O nosso loop infinito
//TODO: Ler valor do conversor
//TODO: Se valor_ADC superior a 512 ligar led
//TODO: Se não desligar led
}

return 0;
}

   A única coisa nova que nos falta aprender é como ler o valor do ADC, isso é muito simples, basta uma linha de código, e o resto é apenas uma condição de comparação e de uso de um if, vamos então aprender a ler um valor do ADC:

Código: [Seleccione]
#include <avr/io.h>
int adc_value; //Variavel usada para guardar o valor lido pelo ADC
int main(void){

DDRB |= (1<<PB5); //Define PB5/digital13 como saida digital

ADCSRA |= ((1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)); //Prescaller a 128 para termos uma frequencia de 125Khz
ADMUX |= (1<<REFS0);
ADMUX &= ~(1<<REFS1); //Avcc como referência de voltagem
ADCSRB &= ~((1<<ADTS2)|(1<<ADTS1)|(1<<ADTS0)); //ADC no modo free-runing
ADCSRA |= (1<<ADATE); //Fonte de sinal para iniciar conversão, neste caso é o free-runing
ADCSRA |= (1<<ADEN); //Liga o circuito do ADC
ADCSRA |= (1<<ADSC); //Inicia as conversões

for(;;){ //O nosso loop infinito
adc_value = ADCW; //Ler valor do conversor
//TODO: Se valor_ADC superior a 512 ligar led
//TODO: Se não desligar led
}

return 0;
}

   Graças ao compilador e ás bibliotecas que temos á nossa disposição, ler o conversor adc é tão simples quanto usar uma simples palavra, e agora o resto do nosso programa:

Código: [Seleccione]
#include <avr/io.h>
int adc_value; //Variável usada para guardar o valor lido pelo ADC

int main(void){

DDRB |= (1<<PB5); //Define PB5/digital13 como saída digital

ADCSRA |= ((1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)); //Prescaller a 128 para termos uma frequencia de 125Khz
ADMUX |= (1<<REFS0);
ADMUX &= ~(1<<REFS1); //Avcc como referência de voltagem
ADCSRB &= ~((1<<ADTS2)|(1<<ADTS1)|(1<<ADTS0)); //ADC no modo free-runing
ADCSRA |= (1<<ADATE); //Fonte de sinal para iniciar conversão, neste caso é o free-runing
ADCSRA |= (1<<ADEN); //Liga o circuito do ADC
ADCSRA |= (1<<ADSC); //Inicia as conversões

for(;;){ //O nosso loop infinito
adc_value = ADCW; //Ler valor do conversor
if(adc_value > 512){
PORTB |= (1<<PB5); //Se valor_ADC superior a 512 ligar led
}
else {
PORTB &= ~(1<<PB5); //Se não desligar led
}
}

return 0;
}

   Agora é só compilar o código, que não deverá apresentar erros, se der erros verifiquem se não é nenhum erro de escrita ou se o copy-paste correu mal, e só falta fazer o upload do código para o arduino e testar, no meu caso criei um novo projecto no Avr Studio chamado adc, se usaram outro nome tomem atenção quanto a isso quando usarem o avrdude.



   E aqui o upload já feito, agora basta rodarem o potenciómetro e o led deverá acender/apagar conforme o valor que o conversor ADC lê (agora estou sem máquina fotográfica mas posteriormente poderei colocar umas fotos do mini circuito/fios a ligar o potenciómetro ao arduino, e um filme do funcionamento).




   Até aqui podem considerar como a parte I do tutorial sobre ADC, agora irei escrever um pouco mais sobre como usar o conversor ADC para ler vários canais/pinos usando uma função simples e enviar esses mesmos valores pela porta serial até ao nosso terminal, usando para isso o código que fizemos no tutorial sobre comunicação serial.

Tiago Angelo
31/12/2010
Avr fanboy

Offline senso

  • Global Moderator
  • Mini Robot
  • *****
  • Mensagens: 9.733
  • Helpdesk do sitio
Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
« Responder #98 em: 31 de Dezembro de 2010, 04:41 »
   Parte 2:
        Vamos então por partes, primeiro temos de aprender a mudar o canal/pino que o conversor ADC lê, como disse ao inicio podemos alterar isso mexendo nos bits MUX3..MUX0 do registo ADMUX, como já sabemos operar sobre os bits esta parte é relativamente simples de se fazer, e o mapeamento entre os valores do ADMUX e o pino que é lido é o seguinte:

Código: [Seleccione]
MUX3 MUX2 MUX1 MUX0 Pino a ler
0 0 0 0 PORTC0 Analogic0
0 0 0 1 PORTC1 Analogic1
0 0 1 0 PORTC2 Analogic2
0 0 1 1 PORTC3 Analogic3
0 1 0 0 PORTC4 Analogic4
0 1 0 1 PORTC5 Analogic5

   Estes são os valores possíveis que podemos usar com o atmega328p versão DIP, ou seja o chip com patinhas, pois o SMD tem mais dois canais/pinos que podemos usar com o ADC.
   Como vamos querer ler vários pinos diferentes, já não faz sentido usar o conversor ADC em modo free-runing até porque manter o modo free-runing e querer mudar de canal tem algumas limitações(temos sempre de fazer duas conversões depois de mudar-mos de canal, uma conversão é para dar tempo ao multiplexer que liga os canais ao ADC ter tempo de mudar e para a voltagem estabilizar e a outra conversão é que nos dá o valor correcto que queremos), por isso vamos mudar o canal, iniciar uma conversão "á força" e vamos esperar que esta seja feita e depois le-mos o valor, assim não precisamos de fazer duas conversões, logo é mais rápido que manter-mos o modo free-runing, basta-nos fazer uma conversão inicial quando configura-mos o ADC, pois segundo a datasheet a primeira conversão após ligar-mos o arduino demora cerca de 23 ciclos de relógio em vez dos normais 13 ciclos, vamos portanto passar ao código:

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

uint16_t adc_value; //Variável usada para guardar o valor lido pelo ADC
void adc_init(void); //Função para inicializar/configurar o conversor
uint16_t read_adc(uint8_t channel); //Função para ler-mos um canal arbitrário do ADC

int main(void){

for(;;){ //O nosso loop infinito
}

return 0;
}

void adc_init(void);{

ADCSRA |= ((1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)); //16Mhz/128 = 125Khz
ADMUX |= (1<<REFS0); //Referencia de 5v
ADCSRA |= (1<<ADEN); //Adc ligada
ADCSRA |= (1<<ADSC); //Fazer uma primeira conversão para iniciar o circuito e porque é a mais lenta
}

uint16_t read_adc(uint8_t channel){
ADMUX &= 0xF0; //Limpa o canal anterior
ADMUX |= channel; //Define o novo canal a ler do ADC
ADCSRA |= (1<<ADSC); //Inicia uma nova conversão
while(ADCSRA & (1<<ADSC)); //Espera que a conversão seja feita
return ADCW; //Retorna o valor do ADC
}

   Como podem ver o código é bastante simples, é basicamente o que tínhamos anteriormente mas "enrolado" em duas funções, agora para ler um canal do ADC, basta usar a função read_adc e no argumento passam o numero que está no Analog In da placa do Arduino, ou então vão ver qual é o pino que corresponde a cada Analog In, vão á tabela que relaciona o valor dos MUX3..0 e passam o valor hexadecimal para decimal para realmente perceberem o porque dos numeros, ou então pensem simplesmente que é o numero do AnalogIn e também o numero do pino do porto, por exemplo o Analog In 3 é o PORTC3, e 3 em hexadecimal é 0011.
   Agora um pequeno salto no tempo até ao tutorial sobre a comunicação serial e voltamos com o seguinte código para nos ajudar:

Código: [Seleccione]
#define F_CPU 16000000UL
#define BAUDRATE 9600
#define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1)

void USART_init(void){

UBRR0H = (uint8_t)(BAUD_PRESCALLER>>8);
UBRR0L = (uint8_t)(BAUD_PRESCALLER);
UCSR0B = (1<<RXEN0)|(1<<TXEN0);
UCSR0C = (3<<UCSZ00);
}

void USART_send( unsigned char data){

while(!(UCSR0A & (1<<UDRE0)));
UDR0 = data;

}

void USART_putstring(char* StringPtr){

while(*StringPtr != 0x00){
USART_send(*StringPtr);
StringPtr++;}

}

   Agora vamos juntar isto ao nosso código actual, espero que não se percam, pois ainda não estou a introduzir nada de novo, é apenas um corta e cola aqui e ali:

Código: [Seleccione]
#include <avr/io.h>
#define F_CPU 16000000UL
#define BAUDRATE 9600
#define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1)

uint16_t adc_value; //Variavel usada para guardar o valor lido pelo ADC
void adc_init(void); //Função para inicializar/configurar o conversor
uint16_t read_adc(uint8_t channel); //Função para ler-mos um canal arbitrário do ADC
void USART_init(void); //Função para inicialiazar a USART
void USART_send( unsigned char data); //Função que envia um caracter pela USART
void USART_putstring(char* StringPtr); //Função que envia uma string pela USART

int main(void){
adc_init(); //Inicializa-mos o conversor ADC
USART_init(); //Inicializa-mos a USART

for(;;){ //O nosso loop infinito
//TODO: Ler os canais um a um e enviar os dados por serial
}

return 0;
}

void adc_init(void){

ADCSRA |= ((1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)); //16Mhz/128 = 125Khz
ADMUX |= (1<<REFS0); //Referencia de 5v
ADCSRA |= (1<<ADEN); //Adc ligada
ADCSRA |= (1<<ADSC); //Fazer uma primeira conversão para iniciar o circuito e porque é a mais lenta
}

uint16_t read_adc(uint8_t channel){
ADMUX &= 0xF0; //Limpa o canal anterior
ADMUX |= channel; //Define o novo canal a ler do ADC
ADCSRA |= (1<<ADSC); //Inicia uma nova conversão
while(ADCSRA & (1<<ADSC)); //Espera que a conversão seja feita
return ADCW; //Retorna o valor do ADC
}

void USART_init(void){

UBRR0H = (uint8_t)(BAUD_PRESCALLER>>8);
UBRR0L = (uint8_t)(BAUD_PRESCALLER);
UCSR0B = (1<<RXEN0)|(1<<TXEN0);
UCSR0C = (3<<UCSZ00);
}

void USART_send( unsigned char data){

while(!(UCSR0A & (1<<UDRE0)));
UDR0 = data;

}

void USART_putstring(char* StringPtr){

while(*StringPtr != 0x00){
USART_send(*StringPtr);
StringPtr++;}

}

   E agora, apesar de muitos não se darem conta disso temos um pequeno problema, nós estamos a ler dados de 10 bits do ADC, mas a nossa USART só consegue enviar um byte, ou seja 8 bits de cada vez, e depois temos outro problema, que é o facto de que não podemos simplesmente enviar um byte pela USART e esperar ler no terminal esse mesmo numero, porque os caracteres são codificados em ASCII(para mais informações sobre a tabela de caracteres ASCII vejam isto: http://www.asciitable.com/ ).
   Então para enviar-mos os nosso valores convertidos teremos de usar uma função que faz parte das bibliotecas base do C, e essa função é a ITOA, que é a abreviatura de Integer TO Ascii, esta função recebe um valor inteiro(uint8_t, uint16_t, int, e outros), em que base queremos converter, por exemplo 2 para binário, 8 para octal, 10 para binário e 16 para hexadecimal,e um array, e retorna esse mesmo array de caracteres(ou seja uma string), como vamos querer os numeros em decimal, a base de conversão é 10, e teremos de usar um array com uma dimensão de 5, pois o valor máximo é 1023, logo 4 caracteres, mais um caracter de terminação de array, de um modo genérico a função itoa usa-se assim:

Código: [Seleccione]
char buffer[5];
uint16_t adc_value;
itoa(adc_value, buffer, 10);

   Integrando isto no nosso código e mais umas quantas linhas de código para ler-mos todos os canais e enviar pela USART eis o código final:

Código: [Seleccione]
#include <avr/io.h>
#include <stdlib.h> //Necessário para a função itoa
#define F_CPU 16000000UL
#define BAUDRATE 9600
#define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1)
#include <util/delay.h>

int adc_value; //Variavel usada para guardar o valor lido pelo ADC
void adc_init(void); //Função para inicializar/configurar o conversor
char buffer[5]; //Buffer para a função itoa
uint16_t read_adc(uint8_t channel); //Função para ler-mos um canal arbitrário do ADC
uint8_t i=0; //Variavel usada no for
void USART_init(void); //Função para inicialiazar a USART
void USART_send( unsigned char data); //Função que envia um caracter pela USART
void USART_putstring(char* StringPtr); //Função que envia uma string pela USART

int main(void){

USART_init(); //Inicializa-mos a USART
adc_init(); //Inicializa-mos o conversor ADC

DDRB |= (1<<PB5);
for(;;){ //O nosso loop infinito
for(i=0; i<6;i++){
USART_putstring("A ler o canal ");
USART_send('0' + i); //Quando queremos enviar apenas um numero, entre 0 e 9 podemos simplesmente somar o numero ao caracter '0'
USART_putstring(" : "); //Apenas para embelezar a coisa
adc_value = read_adc(i); //Lê-mos um dos canais do conversor
itoa(adc_value, buffer, 10); //Convertemos o valor lido para uma string
USART_putstring(buffer); //Enviamos essa mesma string
USART_putstring("  "); //Apenas para embelezar a coisa
_delay_ms(500);
}
USART_send('\r'); //Para mudar de linha no terminal
USART_send('\n'); //Para mudar de linha no terminal

}

return 0;
}

void adc_init(void){

ADCSRA |= ((1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)); //16Mhz/128 = 125Khz
ADMUX |= (1<<REFS0); //Referencia de 5v
ADMUX &= ~(1<<REFS1);
ADCSRA |= (1<<ADEN); //Adc ligada
ADCSRA |= (1<<ADSC); //Fazer uma primeira conversão para iniciar o circuito e porque é a mais lenta
}

uint16_t read_adc(uint8_t channel){
ADMUX &= 0xF0; //Limpa o canal anterior
ADMUX |= channel; //Define o novo canal a ler do ADC
ADCSRA |= (1<<ADSC); //Inicia uma nova conversão
while(ADCSRA & (1<<ADSC)); //Espera que a conversão seja feita
return (ADCL + (ADCH<<8)); //Retorna o valor do ADC
}

void USART_init(void){

UBRR0H = (uint8_t)(BAUD_PRESCALLER>>8);
UBRR0L = (uint8_t)(BAUD_PRESCALLER);
UCSR0B = (1<<RXEN0)|(1<<TXEN0);
UCSR0C = (3<<UCSZ00);
}

void USART_send( unsigned char data){

while(!(UCSR0A & (1<<UDRE0)));
UDR0 = data;

}

void USART_putstring(char* StringPtr){

while(*StringPtr != 0x00){
USART_send(*StringPtr);
StringPtr++;}

}

   Depois de compilado, podem fazer o upload do programa para o arduino:



   Após isso abram o terminal(existe uma nova versão, aqui https://sites.google.com/site/terminalbpp/), configurem como no tutorial sobre como usar a porta serial e devem ter algo como isto, como estamos apenas a testar não é preciso ligar potenciometros em todas as entradas analógicas, pois o ruido que elas apanham chegam para termos valores diferentes de 0 a aparecer no terminal.



   E aqui está, mais um tutorial, se tiverem duvidas ou acharem que falta algo não hesitem em dizer, boa programação pessoal!

Tiago Angelo
31/12/2010
Avr fanboy

Offline Capeleiro

  • Mini Robot
  • *
  • Mensagens: 127
Re: Introdução ao avr-gcc usando o AvrStudio [Quinta parte - Conversor ADC]
« Responder #99 em: 31 de Dezembro de 2010, 17:13 »
Obigado por mais um tutorial, senso ;D
Tenho duas dúvidas: usando um cristal de 20MHz há basicamente duas alternativas para o clock do ADC, 156kHz ou 312kHz, é garantido que com 312kHz eu tenha 8 bits de resolução?
E era também importante para mim saber quanto tempo demora a conversão. No datasheet dizem que a conversão precisa de 13 ciclos de relógio para acontecer, e imaginando que iria usar o prescaler 128, isso são 1664 ciclos do clock principal, o que a 20MHz são 83,2us. É só isto, ou está-me a faltar alguma coisa?

Offline senso

  • Global Moderator
  • Mini Robot
  • *****
  • Mensagens: 9.733
  • Helpdesk do sitio
Re: Introdução ao avr-gcc usando o AvrStudio [Quinta parte - Conversor ADC]
« Responder #100 em: 31 de Dezembro de 2010, 17:17 »
Até aos 200Khz é garantidos os 10 bits, dai para cima convem ter um buffer no sinal com boa capacidade de corrente, para carregar o condensador de amostragem, se for por exemplo um potenciometro de 10Khz podes á vontade ler a 312Khz, se for um sensor com baixa corrente de saida podes ter menos de 10 bits.
Quanto ás contas, sim é isso, são 13 ciclos de relógio do relógio gerado pelos divisores(prescallers).
Avr fanboy

Offline Capeleiro

  • Mini Robot
  • *
  • Mensagens: 127
Re: Introdução ao avr-gcc usando o AvrStudio [Quinta parte - Conversor ADC]
« Responder #101 em: 01 de Janeiro de 2011, 20:47 »
Obrigado  ;)

Offline SJD22

  • Mini Robot
  • *
  • Mensagens: 660
Re: Introdução ao avr-gcc usando o AvrStudio [Quinta parte - Conversor ADC]
« Responder #102 em: 06 de Fevereiro de 2011, 01:35 »
Boa noite.

Senso, muitos parabéns e muito obrigado por este maravilhoso tutorial.

Passar do ambiente arduino para avrstudio é uma boa forma de se evoluir mais. Neste momento estou completamente desligado do arduino uma vez que estou a programar em avrstudio e com chips atmel soldados em PCB sem qq uso das placas de desenvolvimento arduino.

Uma dúvida: estou a usar o código que colocaste sobre USART. Mas o método Receive é sincrono, ou seja, bloqueia à espera de resposta do outro lado, certo? Como consigo colocá-lo assincronamente por forma a não ter de esperar obrigatoriamente por uma resposta do outro lado?

Obrigado.

Offline Cynary

  • Mini Robot
  • *
  • Mensagens: 182
Re: Introdução ao avr-gcc usando o AvrStudio [Quinta parte - Conversor ADC]
« Responder #103 em: 06 de Fevereiro de 2011, 12:37 »
Boa noite.

Senso, muitos parabéns e muito obrigado por este maravilhoso tutorial.

Passar do ambiente arduino para avrstudio é uma boa forma de se evoluir mais. Neste momento estou completamente desligado do arduino uma vez que estou a programar em avrstudio e com chips atmel soldados em PCB sem qq uso das placas de desenvolvimento arduino.

Uma dúvida: estou a usar o código que colocaste sobre USART. Mas o método Receive é sincrono, ou seja, bloqueia à espera de resposta do outro lado, certo? Como consigo colocá-lo assincronamente por forma a não ter de esperar obrigatoriamente por uma resposta do outro lado?

Obrigado.

Para isso tens de usar interrupções.
Basicamente, tiras o while que espera por uma resposta da função, e crias uma ISR, relacionada com a interrupção USART_RX.
Também não te esqueças de colocar um 1 no bit RXCIE0 no register UCSR0B, e ligar as interrupções globais para poder fazer isto.

Offline SJD22

  • Mini Robot
  • *
  • Mensagens: 660
Re: Introdução ao avr-gcc usando o AvrStudio [Quinta parte - Conversor ADC]
« Responder #104 em: 06 de Fevereiro de 2011, 20:10 »
Desculpa a ignorância mas o que é uma ISR?