LusoRobótica - Robótica em Português

Sistemas específicos => Arduino / AVR => Tópico iniciado por: senso em 28 de Setembro de 2010, 01:07

Título: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 28 de Setembro de 2010, 01:07
Boa noite a todos, então aqui vai uma introdução muito básica de como começar a programar em C um arduino, quem diz arduino diz qualquer outro micro-controlador atmel, mas por agora digamos que é arduino pois é para quem quiser dar o salto do ide do arduino para algo melhor.

Primeiro temos de começar por instalar o AvrStudio, que é o IDE oficial da atmel e que podem fazer o download do mesmo aqui:
Citar
http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725

Andam com a página para baixo até encontrarem a linha que diz:
Citar
AVR Studio 4.18 (build 684) (116 MB, updated 11/09)
Têm de se registar para fazer o download do mesmo, mas o registo é rápido, depois fazem o download do seguinte ficheiro(que está pouco acima do outro):
Citar
AVR Studio 4.18 SP3 (b716) (31 MB, updated 9/10)

E por fim fazem o download do WinAvr que é o compilador C para os avrs, e que se encontra aqui:
Citar
http://sourceforge.net/projects/winavr/files/WinAVR/20100110/WinAVR-20100110-install.exe/download

Depois de fazerem o download destes 3 ficheiros podem começar a instalação, primeiramente o ficheiro intitulado AVR Studio 4.18 depois o AVR Studio 4.18 SP3 e por fim do WinAvr, não mudem nenhuma das pastas de instalação quer do AvrStudio quer do WinAvr pois poderam ter problemas com a compilação pois o WinAvr espera que as bibliotecas estejam num sitio e não noutro e não querem ir editar Makefiles para o meter a funcionar.
No fim de tudo instalado recomendo que criem uma pasta onde vão guardar os programas feitos ao longo deste tutorial, aconselho a colocar a pasta logo em c:/ ou d:/ pois vamos andar na linha de comandos e quanto menos sub pastas melhor, aconselho a fazer como eu e criar uma pasta chamada AVR em c:/ e é ai que vão ficar os programas quer em C, quer todos os ficheiros gerados pelo compilador, depois o IDE encarrega-se de criar uma pasta por cada projecto que criam, por isso fica tudo separado e organizado.

Tendo tudo isto instalado podem começar por iniciar o AvrStudio e irá aparecer-vos a seguinte janela:
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg191.imageshack.us%2Fimg191%2F6118%2Finiciowc.jpg&hash=401da485ddea5023b6c73ed3885e1f2e5a3cd914)

De seguida clicam em "New Project" e a janela passa a mostrar a seguinte informação:
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg265.imageshack.us%2Fimg265%2F8394%2Finicio2g.jpg&hash=8a201417223f396afea5a893589ddcb138558d6a)

Como irão notar eu já preenchi alguns dos campos e voces devem fazer tal e qual como está na imagem.
O "Project Name" neste caso é blinky, o famoso piscar um led, simples mas no entanto serve para vos iniciar neste novo mundo, quando estão a escrever blinky no "Project Name" podem notar que o programa copia automaticamente o que escrevem para o campo Initial file.
Devem tambem selecionar as caixas "Create initial file" e "Create folder", assim o programa deixa-vos tudo arrumado e pronto a programar, e por fim devem selecionar "AVR GCC" no "Project type" pois querem programar em C e não em Assembly.
Não esquecer de mudar a localização("Location") para a pasta anteriormente criada em c:/.
Tendo tudo isto feito cliquem em "Next >>", ao clicar vais-vos aparecer duas listas:
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg688.imageshack.us%2Fimg688%2F5167%2Finicio3k.jpg&hash=3151da1ae788988181654f286217e4bbd68a2214)

Como podem ver pela imagem selecionem "AVR Simulator 2" como a "Debug platform" e seleccionem o "ATmega328p" como o "Device" pois é esse o chip que o arduino trás, se o vosso for um dos arduinos com um ATmega168p procedam de igual modo para a "Debug platform" mas no "Device" nem precisam de andar para baixo na lista, pois o ATmega168p aparece logo no inicio da lista.
Tendo o vosso chip seleccionado cliquem em "Finish" e ai está o vosso projecto pronto para começarem a programar em C.
Eis o aspecto do IDE quando clicam em "Finish":
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg295.imageshack.us%2Fimg295%2F4185%2Finicio4.jpg&hash=e8bfbe9eb0488c0cb2e3190c0733614b6a3fdf07)

Agora é começar a escrever código, neste caso é algo bastante simples:
Código: [Seleccione]
Loop infinito{
Acender led;
esperar 1s;
Apagar led;
esperar 1s;
}

Primeiramente e ao contrário do IDE do arduino que faz muitas coisas sem nós saber-mos temos que dizer ao compilador para incluir alguns ficheiros, um deles é um ficheiro com as definições genéricas de registos e portos do nosso chip, outro á uma biblioteca que nos permite criar os delays que precisamos para esperar neste caso 1s.
Como podemos ter o nosso atmega328p com um grande numero de diferentes cristais de diferentes frequências temos de dizer ao compilador qual é a frequência do nosso cristal para que ele saiba gerar delays com precisão.
Assim ainda no pseudo código teremos então:
Código: [Seleccione]
Incluir os ficheiros necessários
Definir a frequência do relógio/cristal
Incluir biblioteca de delays
Loop infinito{
Acender led;
esperar 1s;
Apagar led;
esperar 1s;
}

Começando agora a passar um pouco para c:
Código: [Seleccione]
#include <avr/io.h> //Definições dos portos do m328p, assim pudemos usar os portos para controlar o led
#define F_CPU 16000000UL //F_CPU é uma constante especifica do compilador que serve para armazenar a frequencia do nosso cristal, 16000000 é os 16Mhz escritos em hz, e UL é uma indicação de que queremos o numero representado por uma variavel capaz de o armazenar para não termos problemas de overflow se o compilador usar uma variavel em que o numero não cabe
#include <util/delay.h> //Biblioteca que nos fornece o delay, tem de ser sempre declarada depois da declaração de F_CPU e não antes, se não o compilador não sabe qual é a frequencia do nosso cristal

Loop infinito{
Acender led;
esperar 1s;
Apagar led;
esperar 1s;
}

Agora que já dissemos ao compilador o que ele tem de ir buscar fora do nosso programa vamos passar para C o nosso pseudo-código:
Código: [Seleccione]
#include <avr/io.h> //Definições dos portos do m328p, assim pudemos usar os portos para controlar o led
#define F_CPU 16000000UL //F_CPU é uma constante especifica do compilador que serve para armazenar a frequencia do nosso cristal, 16000000 é os 16Mhz escritos em hz, e UL é uma indicação de que queremos o numero representado por uma variavel capaz de o armazenar para não termos problemas de overflow se o compilador usar uma variavel em que o numero não cabe
#include <util/delay.h> //Biblioteca que nos fornece o delay, tem de ser sempre declarada depois da declaração de F_CPU e não antes, se não o compilador não sabe qual é a frequencia do nosso cristal

int main(void){
while(1){ //Loop infinito{
PORTB |= (1<<PB5); //Acender led; vamos usar o led que está no pin13 do arduino
_delay_ms(1000); //esperar 1s; como são milisegundos, temos de esperar 1000ms para fazer 1s
PORTB &= ~(1<<PB5); //Apagar led;
_delay_ms(1000); //esperar 1s;
}
}

Por esta imagem podem relacionar os nomes que o arduino dá aos pinos com os nomes verdadeiros dos mesmos quando querem usar o AvrStudio.
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Farduino.cc%2Fen%2Fuploads%2FHacking%2FArduino-To-Atmega8-Pins.png&hash=cfb9d2229012ac309c09ae271ca73b18f5961dba)

Ainda falta uma coisa, tal como no arduino temos que definir se um pino é uma entrada ou uma saída, para isso usamos uma coisa chamada DDR, que é um registo que define se um pino é entrada ou saída, no caso do AVR 1 quer dizer saída e 0 quer dizer entrada, para isso vamos definir o nosso pino numero 5 do porto B como uma entrada assim:
Código: [Seleccione]
DDRB |= (1<<PB5);

E o nosso código final fica assim:
Código: [Seleccione]
#include <avr/io.h> //Definições dos portos do m328p, assim pudemos usar os portos para controlar o led
#define F_CPU 16000000UL //F_CPU é uma constante especifica do compilador que serve para armazenar a frequencia do nosso cristal, 16000000 é os 16Mhz escritos em hz, e UL é uma indicação de que queremos o numero representado por uma variavel capaz de o armazenar para não termos problemas de overflow se o compilador usar uma variavel em que o numero não cabe
#include <util/delay.h> //Biblioteca que nos fornece o delay, tem de ser sempre declarada depois da declaração de F_CPU e não antes, se não o compilador não sabe qual é a frequencia do nosso cristal

int main(void){

DDRB |= (1<<PB5); //Definir o pin digital 13/PORTB5 como saida para pudermos piscar o nosso led

while(1){ //Loop infinito{
PORTB |= (1<<PB5); //Acender led; vamos usar o led que está no pin13 do arduino
_delay_ms(1000); //esperar 1s; como são milisegundos, temos de esperar 1000ms para fazer 1s
PORTB &= ~(1<<PB5); //Apagar led;
_delay_ms(1000); //esperar 1s;
}
}

Basta copiar este código para a janela do IDE e carregar em F7 que é o botão para compilar e o código deve compilar sem qualquer erro e deve aparecer algo assim:
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg269.imageshack.us%2Fimg269%2F9563%2Finicio5.jpg&hash=9959431fb4cd4b281bac8ad94525d5628302acc9)

Se de erro é porque se enganaram a copiar alguma coisa, certifiquem-se que os comentários que aqui aparecem em várias linhas no IDE só estão numa linha, se andarem um pouco para cima na janela com o nome Build poderão ver que este código ocupa 172bytes de flash, muito mas muito menos que o blinky do arduino ocupa.

Sei que é neste momento que muitos de vós ficam com um nó no cérebro por causa de coisas como o "|=" o "&=" e até mesmo o "<<", isso meus caros colegas são operadores lógicos e o melhor mesmo é ler um tutorial sobre eles pois é um assunto um pouco extenso, se vocês de dão bem com inglês recomendo este aqui, pois aprendi a partir dele:
Citar
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=37871

Se mesmo assim não perceberem é só dizer que eu irei fazer o meu melhor para vos ensinar.
Agora vamos passar á parte de colocar o programa no arduino, como não estamos a usar o IDE do mesmo não temos o botão mágico para fazer o upload do programa para o atmega, mas em vez de termos um botão mágico vamos fazer nós mesmo o que o IDE do arduino não mostra, primeiro vamos começar por ir á barra Iniciar do

Windows, se estiverem a usar o Vista ou superior escrevam "Executar" e carreguem no icon azul que vos aparece com o nome "Executar":
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg228.imageshack.us%2Fimg228%2F2825%2Finicio6.jpg&hash=f7d968781e3c2fd3f4a3f77669ac0fddade6abe8)

Na janela que vos aparece escrevam cmd, que quer dizer linha de comandos do windows e cliquem em ok:
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg809.imageshack.us%2Fimg809%2F1560%2Finicio7.jpg&hash=b2c94bb33efa3bdf136eb19a585464715f1f5f36)

Ao clicar no ok vais-vos aparecer uma janela nova, a nossa querida linha de comandos, que vai ser a vossa nova companheira, pois é por ela que vão passar a fazer o upload dos programas para o arduino:
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg811.imageshack.us%2Fimg811%2F3760%2Finicio8.jpg&hash=7d1f2e23d2d1dd3f23020df3a90f620dc6ffd972)

E agora se usaram a mesma pasta que eu usei, ou seja AVR e se a meteram no mesmo sitio escrevam o seguinte na linha de comandos:
Código: [Seleccione]
cd/e carreguem no enter

Código: [Seleccione]
cd AVRE carreguem no enter

Código: [Seleccione]
cd blinkyE carreguem no enter

Código: [Seleccione]
cd defaultE carreguem no enter, eu sei que não criaram a pasta default mas é nessa pasta que está o nosso ficheiro .hex, ou seja o programa compilado pronto a enviar para o arduino, agora vem mais um pouco de magia, usando o ide do arduino vejam qual é a porta com a que o vosso arduino está associado quando o ligam ao pc.
O comando genérico para usar o avrdude é este:
Código: [Seleccione]
avrdude -p m328p -c avrisp -P com* -b 57600 -F -U flash:w:Nome do hex.hex
No meu caso a porta com é a com5 e neste exemplo o nosso programa chama-se blinky logo o hex tem o nome de blinky.hex, se não tiverem a certeza escrevam:
Código: [Seleccione]
dirE façam enter, como podem ver ali está o Blinky.hex, tenham atenção que o avrdude é sensível a letras maiúsculas e minúsculas:
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg29.imageshack.us%2Fimg29%2F7572%2Finicio9.jpg&hash=35089f0fb860be3a9c9477aab84df61748d6d820)

Então para enviar este programa para o nosso arduino escreve-mos o seguinte na linha de comandos:
Código: [Seleccione]
avrdude -p m328p -c avrisp -P com5 -b 57600 -F -U flash:w:Blinky.hexE carreguem no enter, a única diferença aqui é que terão de carregar no botão de reset do arduino para que o upload do programa seja feito, se não irão obter um erro de "out of sync" ou algo parecido, quando o upload do programa é bem sucedido eis a mensagem que vos é apresentada:
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg23.imageshack.us%2Fimg23%2F6255%2Finicio10.jpg&hash=1f7af3147b3df8b320da13964fe3655811192eb7)

Agora podem olhar para o vosso arduino a piscar o seu led todo contente da vida com uma tonelada de código a menos em cima, e espero que vocês tenham gostado de ler esta introdução ao avr-gcc usando o AvrStudio, se houver interesse irei continuar com uma série de tutoriais simplificados e muito explicados, estou aberto a sugestões pessoal!
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: amando96 em 28 de Setembro de 2010, 08:16
Bom post  ;D
Já agora esclareço aqui uma duvida,

porque
Código: [Seleccione]
DDRB |= (1<<PB5);
não dava para fazer assim?
Código: [Seleccione]
DDRB = 0x01;
tenho usado tanto de uma maneira como a outra, e ainda não vi diferenças, mas têm de haver, quais são elas?

Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: Cynary em 28 de Setembro de 2010, 09:28
Bom post  ;D
Já agora esclareço aqui uma duvida,

porque
Código: [Seleccione]
DDRB |= (1<<PB5);
não dava para fazer assim?
Código: [Seleccione]
DDRB = 0x01;
tenho usado tanto de uma maneira como a outra, e ainda não vi diferenças, mas têm de haver, quais são elas?

Primeiro, não é igual, visto que 0x01 = (1<<PB0).
Segundo, ao usares (1<<PB5), o teu código torna-se mais legível e, caso por alguma razão alterem qual o bit correspondente ao PB5 noutro AVR, tens menos trabalho a transportar o código de um AVR para outro.

Excelente tutorial senso ;)
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: senso em 28 de Setembro de 2010, 13:26
Tal como o cinary disse deves evitar numeros mágicos, se daqui a 6 meses olhares para o código provavelmente não te lembras por que razão está lá aquele 0x01, com 1<<PB5 sabes que é para definir o pino 5 do porto b como saida.
Já agora se vires no includes o que quer dizer o PB5 vais encontrar:
Código: [Seleccione]
#define PB0 0
#define PB1 1
#define PB2 2
#define PB3 3
#define PB4 4
#define PB5 5
#define PB6 6
#define PB7 7
Até podes usar decimais, mas ainda se torna mais confuso que usar as definições de PBx.

E como são variaveis conhecidas quando compilas o compilador optimiza tudo e não vai mandar o micro fazer shifts para calcular esse valor.
O que ainda é mais ou menos legivel é usares binário, assim tambem dá para ver visualmente qual é o pino, assim:
Código: [Seleccione]
DDRB |= 0b00100000;
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: Njay em 28 de Setembro de 2010, 14:05
porque
Código: [Seleccione]
DDRB |= (1<<PB5);
não dava para fazer assim?
Código: [Seleccione]
DDRB = 0x01;

Ambas são completamente diferentes.
A 1ª só afecta o bit 5 do porto B.
A 2ª afecta todos os bits!
Além de que convém sermpe usar nomes para as constantes, como já aqui foi referido.

Boa introdução!
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: senso em 28 de Setembro de 2010, 19:24
Tanta gente que quer dar o salto mas ninguem se chega á frente...
No pain no gain pessoal
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: amando96 em 28 de Setembro de 2010, 21:02
porque
Código: [Seleccione]
DDRB |= (1<<PB5);
não dava para fazer assim?
Código: [Seleccione]
DDRB = 0x01;

Ambas são completamente diferentes.
A 1ª só afecta o bit 5 do porto B.
A 2ª afecta todos os bits!
Além de que convém sermpe usar nomes para as constantes, como já aqui foi referido.

Boa introdução!

Sim mas, se fosse DDRB = 0x20, iria por o PB5 a 1, output, e o resto como input, se eles estão logo "default" como input, DDRB |= (1<<PB5) fará o mesmo, só que não declara o resto como input, mas como já o são, não faz falta?
Mas estou a ver que por referir logo o pino que está a ser declarado dá mais jeito para quando for rever o código, e caso o querer meter noutro AVR, passarei a fazer assim  :)
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: senso em 28 de Setembro de 2010, 21:06
Imagina que daqui a uns tempos te lembras e queres outro led no PB4, não te lembras que tens o DDR=0x20 e o que acontece é que o teu igual está a limpar qualquer assignação que faças antes de chegar a essa linha e isso é uma má prática de programação.
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: Njay em 28 de Setembro de 2010, 23:14
"assign" em Português é "atribuir".

Isto é um exemplo do meu tipico código de inicialização de pinos do AVR:

// Ports pin assignment.
enum {
    // Port A
    ePinStrobe  = _BV(PA0),
    ePinData    = _BV(PA1),
    ePinDClock  = _BV(PA2),
    ePinUartTx  = _BV(PA3),
    ePinUartRx  = _BV(PA4),    // input
    ePinEndX    = _BV(PA5),    // input w/ pull-up
    ePinEndY    = _BV(PA6),    // input w/ pull-up
    ePinEndZ    = _BV(PA7),    // input w/ pull-up
    // Port B
    ePinOE      = _BV(PB0),
    ePinDRLL    = _BV(PB1),
    ePinLEDa    = _BV(PB2),    // green
    ePinFTE     = _BV(PB3),
    ePinXTAL1   = _BV(PB4),    // (used by external crystal)
    ePinXTAL2   = _BV(PB5),    // (used by external crystal)
    ePinLEDb    = _BV(PB6),    // red
    ePinReset   = _BV(PB7),    // (used as the chip reset)
};

 (...)

DDRA = ePinStrobe | ePinData | ePinDClock | ePinUartTx;
PORTA = ePinEndX | ePinEndY | ePinEndX;   // pullups
DDRB = ePinOE | ePinDRLL | ePinLEDa | ePinFTE | ePinLEDb;
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: senso em 28 de Setembro de 2010, 23:44
Já agora, qual o uso do enum{ }; ?
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: Cynary em 28 de Setembro de 2010, 23:59
Já agora, qual o uso do enum{ }; ?

O enum neste caso funciona como um #define.
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: senso em 29 de Setembro de 2010, 00:07
Isso eu percebi, mas qual a diferença de um para o outro?
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: Njay em 29 de Setembro de 2010, 00:20
Eu pessoalmente uso enums sempre que possível apenas por uma razão. É que os enums ficam registados na "informação de debug" e quando estamos a correr o programa passo a passo (em simulador ou não), são mostrados os nomes dos enums em vez do seu valor e podemos atribuir a uma variável o nome do enum em vez do valor, o que é mais útil. Isto porque os enums são processados pelo compilador e não pelo pre-processador.
Por outro lado o enum é menos flexível, pois tem um tipo, int, e o define é apenas "texto" no código fonte.

Há aqui coisas que podem não fazer sentido para vocês, porque não sabem exactamente como funciona a corrente de ferramentas do c/c++ (toolchain). Mas assim por alto, é como se o código passasse por 4 programas distintos: 1º pelo pré-processador, depois pelo compilador, depois pelo assembler e depois pelo linker. O pré-processador entende quase apenas a linguagem de macros (o que começa por #, como #include, #define, etc) e faz as suas substituições no código fonte original. Depois, o código fonte já alterado (diz-se "expandido pelo pré-processador de C/C++) é que entra no 2º programa, o compilador. O compilador gera então um programa assembly. Depois o assembler transforma o programa assembly em binário e o linker junta vários binários gerados por diferentes ficheiros C/C++ num único programa, juntando-lhe código para fazer a inicialização de acordo com o sistema operativo ou plataforma que estão a usar.

No caso do GCC, o "gcc.exe" que vocês invocam é apenas um programa que vai invocando os programas necessários para compilar o vosso programa (confuso :)?). Na directoria dos binários do GCC vocês vão encontrar todos estes programas, o cpp é o "c pre-processor", etc. O gcc.exe apaga os ficheiros intermediários, como o output do preprocessador (o vosso ficheiro C/C++ expandido), o do compilador (o programa em assembly assembly), etc e vocês só vêm os finais; mas existem opções que podem dar ao gcc para ele não os apagar e assim poderem ser inspeccionados. E para que raio haveriam vocês de querer ver esses ficheiros, para além de curiosidade? Eu por exemplo uso muito o ficheiro em assembly, uso-o para ver que optimizações o compilador está a fazer (porque eu percebo o assembly) e assim poder "orientar" o compilador a fazer melhores optimizações, ou simplesmente para saber como é que se faz ou como é que ele faz X ou Y. Estes ficheiros em assembly podem ter em comentário o código C/C++ que deu origem às sequências de instruções assembly, portanto pode-se aprender imenso a ver este ficheiro.
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: Cynary em 29 de Setembro de 2010, 00:24
Isso eu percebi, mas qual a diferença de um para o outro?

com o enum, podes criar uma espécie de tipo personalizado, em que podes corresponder palavras a certos valores.
Imagina que queres um tipo que guarde os dias da semana, podes ter algo como:

Código: (c) [Seleccione]
enum dia { segunda, terca, quarta, quinta, sexta, sabado, domingo };
...
enum dia t = terca;
...

(a necessidade de enum antes do tipo de enum depende se usas C ou C++, da mesma forma que as struct).
No entanto, não tens de obrigatoriamente ter um tipo dia para dar o valor terca à variável, pode ser um int, um char, ...
No exemplo que dei, o enum dá automaticamente valores de inteiros aos dias (segunda, terca, ...). Podes também atribuir os teus próprios valores se quiseres.
Neste caso, não há diferença entre o enum e o define.
Na prática, a maior diferença é que com o enum só podes guardar inteiros, enquanto com o #define podes guardar tudo o que quiseres (strings, funções, qualquer tipo estranho de dados, ...).

Tenho uma questão: estive a fazer um programa que altera um número num LCD. Consegui pô-lo a funcionar perfeitamente. No entanto queria que fosse mais portátil caso decidisse alterar os pinos usados. Para isso, queria guardar em três arrays os seguintes dados para cada ligação: o register de direcção de dados, o register da porta e o bit (neste momento apenas guardo os bits, usando apenas um register DDR e PORT para todos).
Alguém sabe qual o tipo de dados que uso para guardar os registers PORT e DDR num array? (será que um void * a apontar para a variável funcionaria?)
Nos includes, só encontro que são o resultado de _SFR_IO8(valor), mas não faço a mínima ideia do que essa macro faça :S
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: senso em 29 de Setembro de 2010, 00:28
Estou a ver a coisa.
Já agora, quanto a isso do lcd dá uma vista de olhos nisto:
http://www.jump.to/fleury (http://www.jump.to/fleury)
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: Njay em 29 de Setembro de 2010, 00:52
Tenho uma questão: estive a fazer um programa que altera um número num LCD. Consegui pô-lo a funcionar perfeitamente. No entanto queria que fosse mais portátil caso decidisse alterar os pinos usados. Para isso, queria guardar em três arrays os seguintes dados para cada ligação: o register de direcção de dados, o register da porta e o bit (neste momento apenas guardo os bits, usando apenas um register DDR e PORT para todos).
Alguém sabe qual o tipo de dados que uso para guardar os registers PORT e DDR num array? (será que um void * a apontar para a variável funcionaria?)
Nos includes, só encontro que são o resultado de _SFR_IO8(valor), mas não faço a mínima ideia do que essa macro faça :S

Como 1ª aproximação eu colocava as definições dos portos e pinos num ficheiro .h à parte, e depois mudava esse ficheiro para cada aplicação. Esse ficheiro teria qq coisa do género

#define DATA0_AVRPORT  A
#define DATA0_BIT  3
#define DATA1_AVRPORT  A
#define DATA1_BIT  4
...


Depois na biblioteca do LCD criava macros para usar os valores definidos, tipo isto:

#define DATA0_PORT_DIR(port)  DDR # port
#define DATA0_PORT_VAL(port)  PORT # port
#define DATA1_PORT_DIR(port)  DDR # port
#define DATA1_PORT_VAL(port)  PORT # port
...


E para usar no código do LCD faria isto:

// inicialização
DATA0_PORT_DIR(DATA0_AVRPORT) |= _BV(DATA0_BIT);
DATA1_PORT_DIR(DATA1_AVRPORT) |= _BV(DATA1_BIT);
...

// utilização
DATA0_PORT_VAL(DATA0_AVRPORT) |= _BV(DATA0_BIT);


Podes simplificar se assumires que por exemplo todas as linhas de dados estão no mesmo porto. E em vez de criar o .h, também podes passar directamente os valores das macros na linha de compilação, tipo

gcc ... -DDATA0_AVRPORT=A -DDATA0_BIT=3 ...

É capaz de haver formas mais elegantes de fazer isto mas era o que eu começava por fazer.
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: senso em 29 de Setembro de 2010, 00:56
Mas que grande coding ninja que o senhor Njay se está a revelar!!
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: Cynary em 29 de Setembro de 2010, 09:08
Njay, a tua solução é válida, e é mais ou menos o que eu fiz.
No entanto, não era bem isso que que eu queria, pois queria ser capaz de percorrer os DDR e PORTs num loop, e essa aproximação não mo permite.
O que eu preciso é mesmo de saber que tipo usar para guardar os valores das PORT e DDR numa array. Para colocar num loop também tenho a opção de criar uma função que use um switch, mas não é exactamente a forma mais prática de o fazer :S
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: Njay em 29 de Setembro de 2010, 10:15
Acho que podes fazer isso guardando o endereço do registo. No entanto, queres isso assim para quê? Estás a pensar mudar o LCD de pinos dinamicamente, já com o programa a correr? Não me parece... além disso esse código vai-te ocupar espaço desnecessariamente e nunca vais realmente dar-lhe uso, porque o mapeamento de pinos nunca irá mudar durante a execução do programa.
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: Cynary em 29 de Setembro de 2010, 11:32
Acho que podes fazer isso guardando o endereço do registo. No entanto, queres isso assim para quê? Estás a pensar mudar o LCD de pinos dinamicamente, já com o programa a correr? Não me parece... além disso esse código vai-te ocupar espaço desnecessariamente e nunca vais realmente dar-lhe uso, porque o mapeamento de pinos nunca irá mudar durante a execução do programa.

Não estou a pensar mudar dinamicamente durante a execução do programa. O que quero é codificar de forma a que, caso mude de pinos usados, ou mesmo para um display que use um número diferente de pinos, seja fácil de mudar ...
E quanto ao espaço, em princípio o compilador otimiza de forma a ter o ratio de espaço/performance melhor que estiver programado para fazer (por exemplo, em vez de usar um loop for para iterar 7 pinos, provavelmente coloca as instruções separadamente, e elimina-me as arrays).
O meu objectivo é mesmo fazer um código fácil de alterar caso seja necessário.
Mas que tipo de estrutura de dados uso para guardar o endereço do registo?
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: senso em 29 de Setembro de 2010, 11:41
Uma estrutura?
Queres fazer algo á moda da inicizalização de um lcd no arduino, é isso?
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: Cynary em 29 de Setembro de 2010, 11:47
Uma estrutura?
Queres fazer algo á moda da inicizalização de um lcd no arduino, é isso?

Por estrutura de dados queria dizer tipo de dados (mas até pode ser uma estrutura, pois eu não faço a mínima ideia como é que os registers são representados num programa em C :/).
Queria por exemplo fazer uma coisa assim:

Código: (c) [Seleccione]
#define DPINS 7
tipo_register display_ports[DPINS] = {PORTB, PORTC, PORTD, PORTB, PORTB, PORTB, PORTB};
tipo_register display_ddrs[DPINS] = {DDRB, DDRC, DDRD, DDRB, DDRB, DDRB, DDRB};
Apenas me falta o tipo_register :S
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: senso em 29 de Setembro de 2010, 11:58
os registos são de 8bits, logo um uint8_t serve.
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: Njay em 29 de Setembro de 2010, 13:38
Continuo sem ver a vantagem do que queres fazer. Acho que vai ocupar mais espaço, e vai ficar mais complicado porque agora tens que lidar com os endereços dos registos em vez dos nomes, e esses endereços podem ser diferentes para cada chip -> lá se foi a tua portabilidade.

Qual é o problema que vês na solução com macros?
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: Cynary em 29 de Setembro de 2010, 14:06
A solução com macros funciona perfeitamente, e neste momento é a que estou a utilizar, e se não encontrar forma de fazer o que quero, fico com essa. O problema é mesmo não poder iterar pelos diferentes endereços se usar macros :S
Eu não quero ter de trabalhar com os endereços! Penso que percebeste mal essa parte. Eu quero é guardar o valor do nome (PORTB por exemplo) numa variável (ou num ponteiro que depois possa referenciar, de forma a usar o PORTB) -- se tiver de usar endereços directos dos registers, perco a portabilidade de chip para chip, e concodo que é melhor ter mais algum trabalho se precisar de modificar o código, do que perder essa portabilidade.
Um uint8_t não serve aparentemente :S dá-me este erro no compilador:

Código: [Seleccione]
error: initializer element is not constant(já tentei declarar a variável como const, e dá-me o erro de estar a escrever numa variável só de leitura)

Vou dar um exemplo do que quero fazer, a ver se fica mais claro (o que me falta é mesmo o tipo_register :S):

Código: (c) [Seleccione]
#include <avr/io.h>
#include <util/delay.h>

#define DPINS 7

unsigned char display[10][DPINS] = {{1,1,1,1,1,1,0}, {0,1,1,1,0,0,0}, {0,1,1,0,1,1,1}, {0,1,1,1,1,0,1}, {1,0,1,1,0,0,1}, {1,1,0,1,1,0,1}, {0,1,1,1,1,1,1}, {0,1,1,1,0,0,0}, {1,1,1,1,1,1,1}, {1,1,1,1,0,0,1}};
unsigned char display_bits[DPINS] = {PB0, PB1, PB2, PB3, PB4, PB5, PB6};
tipo_register display_ports[DPINS] = {PORTB, PORTB, PORTB, PORTB, PORTB, PORTB, PORTB};
tipo_register display_ddrs[DPINS] = {DDRB, DDRB, DDRB, DDRB, DDRB, DDRB, DDRB};

unsigned char n;

void putnumber() {
  unsigned char i;
  n %= 10;
  for(i = 0; i < DPINS; ++i) {
    display_ports[i] &= ~(1<<display_bits[i]);
    display_ports[i] |= (display[n][i]<<display_bits[i]); } }
(os DDR são inicializados no main neste caso; não se preocupem com a ordem dos 1s e dos 0s no exemplo, visto que depende da forma como o display está ligado)
Neste momento, como os registers de direcção e porta que uso são todos iguais, posso usar uma definição facilmente. Mas se quiser usar um display que usa mais ligações, de forma a que uma porta não seja suficiente, ou se tiver alguns dos pinos já ocupados, quero facilmente poder alterar alguns dos pinos da PORTB para PORTD por exemplo.
Usar macros funciona, e se usar sempre o mesmo número de pinos para o display, fica bastante portátil, mesmo que altere os pinos usados. Mas se mudar para displays que usem um número diferente de pinos, ou até se fizer, por exemplo, um contador binário com LEDs, com mais do que 7 pinos, tenho de alterar muito mais código.
Se desse para fazer uma macro em que se pudesse iterar, como por exemplo (este código não funciona, mas penso que demonstra o conceito):

Código: (c) [Seleccione]
#define DDR0 DDRA
#define DDR1 DDRB
...
#define DDR(n) DDR # n
...
for(i = 0; i < DPINS; ++i) {
  DDR(i) |= (1<<display_bits[i]); }
...

PS: Testei as macros que fizeste, e elas não funcionam com o avr-gcc :S só se estás a usar algum argumento diferente dos meus ...
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: senso em 29 de Setembro de 2010, 15:15
Vê o código da biblioteca para lcd's que eu meti ai em cima e podes ver como é que a pessoa que a fez usa as macros para isso.
Continuo a achar a tua solução muito arduinesca..
Acho que deves usar os portos na totalidade, porque assim não tens de andar a fazer ands e ors nos registos para só mexer um bit ou dois.
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: Cynary em 29 de Setembro de 2010, 18:10
Estive a ver o código da biblioteca, e ajudou-me a pensar um pouco.
Afinal tinhas razão senso, um uint8_t (neste caso usei unsigned char, mas dá no mesmo xD) funciona como ponteiro para o register, só me tinha esquecido de fazer o casting -.-.
O código ficou assim:

Código: (c) [Seleccione]
#include <avr/io.h>
#include <util/delay.h>

#define DPINS 7

unsigned char display[10][DPINS] = {{1,1,1,1,1,1,0}, {0,1,1,1,0,0,0}, {0,1,1,0,1,1,1}, {0,1,1,1,1,0,1}, {1,0,1,1,0,0,1}, {1,1,0,1,1,0,1}, {1,1,0,1,1,1,1}, {0,1,1,1,0,0,0}, {1,1,1,1,1,1,1}, {1,1,1,1,0,0,1}};
unsigned char display_bits[DPINS] = {PB0, PB1, PB2, PB3, PB4, PB5, PB6};
unsigned char *display_ports[DPINS] = {(unsigned char *)&PORTB, (unsigned char *)&PORTB, (unsigned char *)&PORTB, (unsigned char *)&PORTB, (unsigned char *)&PORTB, (unsigned char *)&PORTB, (unsigned char *)&PORTB};
unsigned char *display_ddrs[DPINS] = {(unsigned char *)&DDRB, (unsigned char *)&DDRB, (unsigned char *)&DDRB, (unsigned char *)&DDRB, (unsigned char *)&DDRB, (unsigned char *)&DDRB, (unsigned char *)&DDRB};

unsigned char n;

void putnumber() {
  unsigned char i;
  n %= 10;
  for(i = 0; i < DPINS; ++i) {
    *display_ports[i] &= ~(1<<display_bits[i]);
    *display_ports[i] |= (display[n][i]<<display_bits[i]); } }

Em termos de eficiência, não me preocupei muito com isso ao fazer este código, porque tinha como objectivo aprender e experimentar (por exemplo, queria saber como podia guardar uma referência a um register arbitrário numa variável, agora sei :P), em vez de fazer uma aplicação final ...
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: Njay em 30 de Setembro de 2010, 00:09
(...) tinha como objectivo aprender e experimentar (...)

É mesmo a única razão que vejo para fazeres as coisas dessa forma.
Título: Re: Introdução ao avr-gcc usando o AvrStudio
Enviado por: senso em 01 de Outubro de 2010, 00:18
   Boa noite a todos, hoje vou continuar o tutorial falando-vos sobre a usart.
Mas o que é a usart(ou uart), a usart é um módulo de hardware que está dentro dos nossos atmegas, que permite ao nosso chip comunicar com outros dispositivos usando um protocolo serial, isto quer dizer que com apenas dois fios podemos enviar e receber dados.
   Um dos maiores usos da usart é a comunicação serial com o nosso computador, e para isso temos de configurar a usart para ela fazer exactamente aquilo que queremos.

   Esqueci-me de referir no anterior tutorial, mas um dos vossos melhores amigos quando programam um micro-controlador é o seu datasheet, no caso do atmega328p podem fazer o download do mesmo aqui:
Citar
http://www.atmel.com/dyn/resources/prod_documents/doc8271.pdf

   Sempre que programa-mos pode-mos manter um esqueleto básico para iniciar a programação, a partir de agora vou usar sempre este esqueleto, que tem os includes e a declaração do main:
Código: [Seleccione]
#define F_CPU 16000000UL //permite usar os delays calibrados
#include <avr/io.h> //definições gerais de pinos e registos
#include <util/delay.h> //local onde estão as funções de delay

int main(void){ //inicio da função main
//O vosso programa fica entre as chavetas

return 0; //toda a função não void tem de ter um return
}

   Sempre que quiserem programar podem usar este esqueleto que são sempre uns caracteres a menos que têm de escrever.
   Falando agora especificamente de como activar e usar a nossa usart para falar com o computador, neste tutorial em vez de deixar um monte de linhas de código perdidas no meio no nosso main vou antes criar algumas funções, pois assim podem copiar as funções e usar noutros programas, até porque fica tudo mais limpinho e separado.
Em pseudo código eis o que temos de fazer:
Código: [Seleccione]
Iniciar e configurar a usart
Criar uma função para enviar um byte/caracter
Criar uma função para receber um byte/caracter
Fazer um simples eco na função main

   Como não faço ideia de como a usart funciona o que devo fazer é pegar no datasheet e começar a ler, e como as pessoas na atmel até fizeram um bom trabalho a fazer este datasheet está tudo muito bem organizado com um indice e marcadores e facilmente descobrimos que a secção sobre a usart começa na página 177- Secção 19, e temos até código em C e assembly para configurar a usart, quer então dizer que o nosso trabalho está facilitado, e pouco mais temos que fazer que ler e passar para o nosso programa o código dado.
Vamos começar pela inicialização da nossa usart, tal como está no datasheet:
Código: [Seleccione]
void USART_init(void){

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

   UBRR0H e UBRR0L são os registos onde colocamos um valor que depende do baud-rate e da frequência do oscilador que o nosso chip tem, temos duas opções para determinar este valor, ou vemos a tabela que está no datasheet ou usamos uma pequena fórmula que juntamos ao cabeçalho do nosso programa onde estão os outros includes e o compilador determina o valor BAUD_PRESCALLER baseado no baudrate que queremos e no valor de F_CPU, sendo esta formula a seguinte:
Código: [Seleccione]
#define BAUD 9600 //o baudrate que queremos usar
#define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1) //a formula que faz as contas para determinar o valor a colocar nos dois registos

   Isto não é magia nem nada que se pareça, no datasheet são dadas duas fórmulas, se juntarmos as duas é esta a formula com que ficamos.
   UCSR0B é o registo que nos permite activar os canais de recepção e transmissão de dados da usart assim como activar interrupts, mas isso não nos interessa por agora.
E em UCSR0C definimos que queremos 8 bits de dados, sem paridade e um stop bit, em vez de (3<<UCSZ00) podemos fazer ((1<<UCSZ00)|(1<<UCSZ01)) o resultado é precisamente o mesmo.

   Agora para inicializar-mos a nossa usart basta chamar a função USART_init no nosso main e temos a usart pronta a usar, mas ainda não somos capaz nem de enviar nem de receber dados.
Mais uma vez a datasheet tem uma solução funcional, que é a seguinte:
Código: [Seleccione]
void USART_send( unsigned char data){

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

}

   A primeira de linha de código pode parecer estranha, mas tudo tem a sua razão e a razão desta linha é que o atmega tem um buffer de 3 bytes em hardware e esta linha verifica se ainda existe espaço no buffer para colocar mais dados, se não espera que haja espaço, se sim, coloca os dados no buffer coisa que é tratada na segunda linha da função.
E com apenas duas linhas estamos prontos a enviar dados por serial!
Agora falta-nos apenas a função para receber dados, sendo esta mais uma função de duas linhas:
Código: [Seleccione]
unsigned char USART_receive(void){

while(!(UCSR0A & (1<<RXC0)));
return UDR0;

}
   Na primeira linha, usamos o while para esperar que existam dados recebidos no registo de recepção, quando esses dados chegam ao registo, simplesmenta returna-mos os dados e temos a nossa usart a ler dados por serial.
   Agora como extra, vou mostrar uma pequena função que permite enviar strings, pois muitas vezes queremos enviar mais que apenas um byte de informação de cada vez, a função para enviar strings tira partido do facto de em C uma string ser terminada por um caracter nulo(/null) e que é feita de muitos caracteres individuais, assim com um pequeno loop que é executado enquanto os dados a enviar são não nulos e vai enviando um caracter de cada vez.
Código: [Seleccione]
void USART_putstring(char* StringPtr){

while(*StringPtr != 0x00){ //Aqui fazemos a verificação de que não chegamos ao fim da string, verificando para isso se o caracter é um null
USART_send(*StringPtr); //Aqui usamos a nossa função de enviar um caracter para enviar um dos caracteres da string
StringPtr++;} //Aumentamos o indice do array de dados que contem a string

}

   O char* no inicio da função pode parecer estranho e chama-se um ponteiro, que é algo bastante util em C, mas por agora vamos simplificar as coisas, e imaginar as strings como arrays de caracteres, que é isso mesmo que elas são em C e que o ponteiro não é mais que o inicio desse mesmo array.

Agora, pegamos no nosso programa inicial em branco e juntamos tudo, ficando assim:
Código: [Seleccione]
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>

#define BAUDRATE 9600
#define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1)

int main(void){

return 0;
}

void USART_init(void){

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

unsigned char USART_receive(void){

while(!(UCSR0A & (1<<RXC0)));
return UDR0;

}

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 se tentar-mos usar as nossas funções o compilador vai dizer que elas não estão definidas e nós ficamos a olhar para ele com cara espantada, porque as nossas funções estão mesmo ali, por baixo do main, e é precisamente esse o problema, no arduino podemos declarar funções onde bem nos apetecer, e em C tambem, mas temos que declarar as funções, ou seja a primeira linha da função que tem o nome dela e que tipo de dados é o seu retorno e quais os seus argumentos têm de estar antes do main, para o compilador saber que as funções existem, ficando assim o nosso código com as declarações das funções antes do main:
Código: [Seleccione]
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>

#define BAUDRATE 9600
#define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1)

//declaração das nossas funções
void USART_init(void);
unsigned char USART_receive(void);
void USART_send( unsigned char data);
void USART_putstring(char* StringPtr);

int main(void){

return 0;
}

void USART_init(void){

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

unsigned char USART_receive(void){

while(!(UCSR0A & (1<<RXC0)));
return UDR0;

}

void USART_send( unsigned char data){

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

}

void USART_putstring(char* StringPtr){

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

}

   Já que temos acesso a comunicação terminal podemos fazer um "Olá mundo" mais completo que apenas um led a piscar, então vamos usar as nossas funções para escrver a frase "Olá mundo!!!", mas falta-nos umas coisa, não temos um terminal serial, ou seja um programa que receba os dados da porta serial e nos mostre no ecrã do computador o que recebeu.
No meu caso, recomendo usar este terminal:
Citar
http://www.smileymicros.com/download/term20040714.zip?&MMN_position=42:42


   Foi feito por um menbro do AvrFreaks e acho-o simples de usar, tambem vai do gosto e existem milhares de terminais pela internet fora, escolham o que mais gostarem. Neste caso basta fazer o download e executar o ficheiro, podem já fazer isso e deixar o terminal aberto que vamos usa-lo mais tarde.

Vamos lá pegar então no nosso programa e completa-lo para o nosso "Olá mundo":
Código: [Seleccione]
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>

#define BAUDRATE 9600
#define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1)

//declaração das nossas funções
void USART_init(void);
unsigned char USART_receive(void);
void USART_send( unsigned char data);
void USART_putstring(char* StringPtr);

char String[]="Olá mundo!!!"; //String[] que dizer que é um array, mas ao colocar-mos o texto entre "" indicamos ao compilador que é uma string e ele coloca automáticamente o terminador null e temos assim uma string de texto usavel

int main(void){
USART_init(); //Inicializar a usart

while(1){ //Loop infinito
USART_putstring(String); //Passamos a nossa string á função que a escreve via serial
_delay_ms(5000); //E a cada 5s re-enviamos o texto
}

return 0;
}

void USART_init(void){

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

unsigned char USART_receive(void){

while(!(UCSR0A & (1<<RXC0)));
return UDR0;

}

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 tão simples como isto temos o nosso atmega a enviar dados.
Agora que se acabou a teoria vamos abrir o nosso AvrStudio e criar um projecto novo com o nome USART, primeiro carregam em "New project" aqui:
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg408.imageshack.us%2Fimg408%2F4748%2Fusart1.jpg&hash=63337e2eef471ed69dd94aec31b7c983b7bb6fad)

   Depois na janela seguinte o AvrStudio já se lembra da nossa pasta chamada AVR onde guarda-mos os nossos projectos, preencham o "Project name" com USART e não se esqueçam de escolher o Avr-gcc como compilador e das caixas de verificação:
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg202.imageshack.us%2Fimg202%2F1034%2Fusart2.jpg&hash=39a8c5aa6b46b3b943cd277c037a98bbd573c008)

   Depois de carregarem em Next escolham o vosso chip, que se for como anteriormente é o Atmega328p:
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg818.imageshack.us%2Fimg818%2F9751%2Fusart3.jpg&hash=281983465c40099c88c06dd25227e970aa68adc0)

   Por fim carreguem em Finish e copiem o código completo para o editor de texto, e carreguem em F7 para compilar o vosso código, podem tambem carregar no botão que está marcado a verde nesta imagem:
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg822.imageshack.us%2Fimg822%2F4171%2Fusart4.jpg&hash=fa33824e24231eef74a3492ea689da8b7b669932)

Como podem ver o código compila sem erros e podemos iniciar a linha de comandos para fazer o upload do programa para o arduino, se não se lembrarem de como se faz, releiam o primeiro tutorial, vamos até á pasta que neste caso não se irá chamar blinky mas sim USART, e depois novamente até á pasta default que é onde estão os ficheiros gerados pelo compilador:
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg815.imageshack.us%2Fimg815%2F1522%2Fusart5.jpg&hash=1164aa8127e4c4354d2312e4a0ffbc477c3410dd)

E agora voltamos a usar o nosso caro avrdude para fazer o upload do ficheiro .hex para o arduino, mais uma vez saliento para o facto de terem atenção á porta COM usada:
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg411.imageshack.us%2Fimg411%2F8023%2Fusart6.jpg&hash=cdd86ad40cb204b187e3a735ea787b8e0e9a5e1e)

Com o upload do programa feito vamos agora abrir o nosso Terminal, e tal como com o avrdude têm de ter atenção á porta com que o vosso arduino usa, assim como ao baudrate escolhido, no caso deste exemplo é 9600 e a forma como a usart está configurada, no nosso caso, 8 bits de dados, 1 bit de stop e sem bit de paridade, nesta imagem mostro como configurar o terminal para receber dados do arduino:
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg22.imageshack.us%2Fimg22%2F1254%2Fusart7.jpg&hash=fa82c557d90e7f309c2e32768a875a2c519022a8)

Agora basta carregar em "Connect" e carregar no botão de reset do arduino para sincronizar o programa com arduino e deverão ver algo do género:
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg291.imageshack.us%2Fimg291%2F6127%2Fusart8.jpg&hash=2e723cf4d3c804bbb667de90047af9fa03dfacc7)

E está a comunicação serial a funcionar!
Agora deixo um desafio em aberto, usando o outro tutorial e este, desafio-vos a criarem um programa que acende o led do arduino quando recebe o caracter "a" e que o apague quando receber outro caracter qualquer, é algo bastate simples de se fazer, não precisam de nenhum hardware extre para além do arduino e assim aprendem como controlar algo usando dados via serial, para enviar um caracter usando ester terminal basta escrever o caracter na caixa marcada a verde da imagem de cima e carregar no enter.
Boa programação!!!
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: metRo_ em 01 de Outubro de 2010, 00:28
Só agora estive a ler este tópico com atenção e vir aqui só dar os parabens pelos tutoriais assim como a discussão que aqui vai até sinto que estou a fazer off-topic ;)
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: senso em 01 de Outubro de 2010, 00:31
E claro, se alguem reparar em algum erro digam para corrigir ;)
Obrigado.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: Cynary em 01 de Outubro de 2010, 01:02
Excelente tutorial :)
A única coisa que penso que poderia completá-lo seria uma pequena explicação de como comunicação serial funciona.
Duas pequenas observações: a maior parte dos registers 16 bits do AVR (até agora todos têm funcionado, não sei se há algum que não esteja configurado :P), apesar de serem divididos em dois, um H e um L, podem ser acedidos directamente como um único register em C (muitas vezes é melhor isso do que separadamente, visto que por vezes existem regras sobre qual o register que se tem de aceder primeiro). Neste caso, acedes separadamente o UBRR0L e o UBRR0H, quando podias aceder unicamente ao UBRR0.
Outro pormenor, de formatação: Fechaste mal a code tag quando estavas a explicar a função USART_Init ...
Se não te importas, vou plagiar os teus tutoriais, e começar a usar funções nos meus xD, infelizmente nunca me lembrei disso :/.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: senso em 01 de Outubro de 2010, 01:10
Obrigado pela chamada de atenção quanto ao erro.
Em alguns registos conheço isso, como é caso de ler a ADC usando só ADCW, mas para o caso deste não conhecia esse atalho por assim dizer.
Vai dar no mesmo e como é para iniciantes assim os registos que são usados no programa existem no datasheet.
Pois, eu estava para escrever uma pequena introdução sobre o que é em concreto a comunicação serial, mas primeiramente muita gente não iria entender grande coisa, e como só usamos serial até ao ftdi passa a usb e cada vez mais é assim, não vejo grandes vantagens em dizer como funciona, e no teu tutorial tu já escreves-te sobre isso.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: Cynary em 01 de Outubro de 2010, 01:58
Obrigado pela chamada de atenção quanto ao erro.
Em alguns registos conheço isso, como é caso de ler a ADC usando só ADCW, mas para o caso deste não conhecia esse atalho por assim dizer.
Vai dar no mesmo e como é para iniciantes assim os registos que são usados no programa existem no datasheet.
Pois, eu estava para escrever uma pequena introdução sobre o que é em concreto a comunicação serial, mas primeiramente muita gente não iria entender grande coisa, e como só usamos serial até ao ftdi passa a usb e cada vez mais é assim, não vejo grandes vantagens em dizer como funciona, e no teu tutorial tu já escreves-te sobre isso.

O que eu tenho no meu tutorial é mais a explicar o que cada register/bit faz.
Por curta descrição, estava a falar mais das especificações do standard.
Por exemplo, explicar como a paridade em serial funciona, a diferença entre modo síncrono e assíncrono, e qual a diferença entre 1 ou 2 stop bits (infelizmente ainda não percebi isto tudo bem xD era para ver este fim-de-semana :P).
Além disso, o USART tem outras funcionalidades que podem ser descritas, como os modos multi-processador/SPI (no entanto, sou da opinião que o SPI é suficiente para outra parte do tutorial, e o multi-processador é suficientemente semelhante ao I2C para ser descrito aí :P).
No entanto, também tens razão quando dizes que o que descreveste é suficiente para quase todas as aplicações, mas penso que uma curta descrição do serial seria positiva, mesmo que seja só para compreender mais facilmente como funcionam outras comunicações serial usadas como SPI/I2C.
Visto que já fizeste o tutorial de serial, em princípio irei trabalhar num de I2C para este fim-de-semana, e posso explicar isso aí ;)
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: senso em 01 de Outubro de 2010, 02:05
Quando vires o trabalho que dá meter o i2c a trabalhar certinho e direitinho, penso que no site do Fleury tem lá uma biblioteca de i2c e aquilo é massivo mesmo, tens montes de casos excepcionais, fazer o Start e o Stop.
Eu nunca usei a usart em modo spi nem nunca li a parte do multi processador, o modo síncrono segundo me parece é funcionar como uma porta spi, mas não tenho a certeza se é mesmo ou não.
1 stop bits ou dois é só mais um bit a comer tempo que em alta velocidade dá jeito, e mesmo em baixa, é menos um bit que a usart tem de gerar, eu dei comunicação serial em cadeiras e nunca ninguém disse o uso de 1 ou 2 stop bits, provavelmente teria uso nos antigos terminais mecânicos e os primeiros electrónicos, mas isto é especulação.
O bit de paridade é um mecanismo muito rudimentar de detecção de erros, mas só é capaz de os detectar, e não sei como é que o ftdi trata de uma falha de paridade, numa usart um erro de paridade das duas uma, ou mandava os dados do byte em questão fora ou ligava uma flag/excepção/interrupção consoante a importância dos dados.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: Cynary em 01 de Outubro de 2010, 02:25
O modo síncrono necessita do uso de um clock, e por isso a baud rate não interessa muito, a não ser que sejamos master. Basicamente funciona como nos modos SPI e I2C.
Como disse antes, esses pormenores são mais interessantes para perceber todo o potencial que o AVR nos dá do que propriamente para usá-los mesmo xD
Estive já a ver a biblioteca de I2C do fleury, e até não me pareceu muito complicado, apenas exige um bom conhecimento do protocolo.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: senso em 01 de Outubro de 2010, 03:02
Pois, e tendo em conta que está implementada em assembly melhor que aquilo eu sei que não faço, portanto se quero i2c pego nela e uso-a.
O modo sincrono é mesmo um segundo SPI.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: GnGz em 02 de Outubro de 2010, 16:47
Este foi o codigo que fiz para o desafio do senso .. com uma pequena ajudazita do senso:

Código: [Seleccione]
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>

#define BAUDRATE 9600
#define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1)

//declaração das nossas funções
void USART_init(void);
unsigned char USART_receive(void);
void USART_send( unsigned char data);
void USART_putstring(char* StringPtr);



int main(void){

USART_init();      //Inicializar a usart
DDRB |= (1<<PB5);

while(1){      //Loop infinito

if ( USART_receive() == "a" ) {   //faltava-te o () depois de USART_receive para o compilador saber que é um função, e o receive estava mal escrito
 PORTB |= (1<<PB5);
}
else {
  PORTB &= ~(1<<PB5);   //falta-te um ponto e virgula aqui para indicar fim de linha
}
}

return 0;
}

void USART_init(void){

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

unsigned char USART_receive(void){

   while(!(UCSR0A & (1<<RXC0)));
   return UDR0;

}

void USART_send( unsigned char data){

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

}

void USART_putstring(char* StringPtr){

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

}

Tou e com dificuldades e para fazer upload no meu arduino... :\
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: senso em 02 de Outubro de 2010, 17:27
Le a pm, deves estar a esquecer-te de fazer reset no botao do arduino, que o avrdude nao faz reset automatico.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: GnGz em 02 de Outubro de 2010, 18:16
Ja consegui mas parece que o codigo ai em cima faz o que deve fazer :S  :-[
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: Cynary em 02 de Outubro de 2010, 19:01
Ja consegui mas parece que o codigo ai em cima faz o que deve fazer :S  :-[

Tentei usar o teu código ...
Deu-me um erro, nem chegou a compilar :P (tinha o compilador configurado para mostrar todos os erros/avisos -- opção -Wall).
Deu erro na linha 24:

Código: (c) [Seleccione]
USART_receive() == "a"
Em C, "a" não é um carácter, logo não podes comparar com um carácter. Ao usares o símbolo ", estás a declarar uma string, que acabam em 0 (logo tens dois caracteres aí) -- apenas se usa esta declaração em C quando estás a atribuir o valor de uma string na sua declaração (fora da declaração, usas a função strcpy) ou quando passas como parâmetro para uma função (em C++ tens mais opções :P).
Para declarares como carácter, tens de usar o símbolo ':

Código: (c) [Seleccione]
USART_receive() == 'a'
Não vi o resto do código, nem testei, por isso não sei se pode ter algum problema de lógica, mas tens esse problema de programação :P
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: GnGz em 02 de Outubro de 2010, 19:30
Sim ... o senso ja tinha dito isso mas muito obrigado... mas nao correu bem o soft..
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: Cynary em 02 de Outubro de 2010, 20:58
Sim ... o senso ja tinha dito isso mas muito obrigado... mas nao correu bem o soft..

Estive a testar com maior pormenor, e o problema parece estar mesmo nas funções do USART :S
Agora não tenho tempo de testar ao pormenor, mas o código parece ficar bloqueado na função USART_init, e por alguma razão estranha, quando incluo o código para a função USART_putstring, o código aparenta dar problemas :S
Quando tiver mais tempo, vejo o que se passa com maior pormenor :P.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: GnGz em 02 de Outubro de 2010, 21:10
Mas se o USART tivesse a funcionar direito .. o codigo corria certo ?

P.S : tou sem acentos...
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: Njay em 03 de Outubro de 2010, 00:29
O código parece-me bem. GnGz, o que é que vês e o que é que estavas à espera de ver no teste que fazes (e que teste é que fazes?)?
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: Cynary em 03 de Outubro de 2010, 02:51
Njay -- a lógica está boa, mas o código não se comporta como deve de ser, visto que devia acender o LED do arduino quando recebe o caracter 'a', e não o está a fazer.
Queria pedir desculpa pelos erros que fiz na minha mensagem anterior -- o código do senso está a funcionar a 100%, e os problemas que estava a ter era por ter trocado as flags para o avrdude no meu IDE, visto que também tenho um atmega88, e não cheguei a trocar o argumento do avrdude de m88 para m328p.
O problema que faz com que o teu código não se comporte como deve de ser, são os tipos das variáveis.
Um 'a' é um signed char. No entanto, a função USART_receive() devolve um unsigned char.
Tens duas opções aí: ou mudas o tipo que a função devolve para char, ou fazes um casting para unsigned char do 'a'.
Aqui está como fica com o casting (linha 23):
Código: (c) [Seleccione]
if ( USART_receive() == (unsigned char)'a' ) {
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: GnGz em 03 de Outubro de 2010, 11:36
a outra forma era fazer return em char so?

P.S fez das duas maneiras e nenhuma funciona :S
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: Njay em 03 de Outubro de 2010, 11:46
O problema que faz com que o teu código não se comporte como deve de ser, são os tipos das variáveis.
Um 'a' é um signed char. No entanto, a função USART_receive() devolve um unsigned char.
Tens duas opções aí: ou mudas o tipo que a função devolve para char, ou fazes um casting para unsigned char do 'a'.
Aqui está como fica com o casting (linha 23):
Código: (c) [Seleccione]
if ( USART_receive() == (unsigned char)'a' ) {

Não me parece que isso seja problema. Ambos os tipos têm o mesmo tamanho (8 bits) e na comparação de igualdade o sinal é irrelevante.

Que tal fazer um "serial echo"? Isso tira logo as dúvidas relativas ao código UART estar ou não a funcionar:

Código: [Seleccione]
    while (1) {
        USART_send(USART_receive());
        PORTB ^= (1<<PB5);
        _delay_ms(80);
        PORTB ^= (1<<PB5);
        _delay_ms(80);
    }
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: GnGz em 03 de Outubro de 2010, 12:04
Feito ... O USART nao funciona...
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: Njay em 03 de Outubro de 2010, 12:29
Como estás a testar? Tens o teu terminal bem configurado?
Faz o LED piscar 2 ou 3 vezes no inicio do programa para teres a certeza que o micro está a correr.
O código que aí deixei faz o LED dar uma piscadela rápida a cada caracter que recebe; vês ele piscar?
Mostra aí o teu código completo e a linha de comando que usaste para o compilar.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: Cynary em 03 de Outubro de 2010, 12:31
O problema que faz com que o teu código não se comporte como deve de ser, são os tipos das variáveis.
Um 'a' é um signed char. No entanto, a função USART_receive() devolve um unsigned char.
Tens duas opções aí: ou mudas o tipo que a função devolve para char, ou fazes um casting para unsigned char do 'a'.
Aqui está como fica com o casting (linha 23):
Código: (c) [Seleccione]
if ( USART_receive() == (unsigned char)'a' ) {

Não me parece que isso seja problema. Ambos os tipos têm o mesmo tamanho (8 bits) e na comparação de igualdade o sinal é irrelevante.

Isso está errado ...
Para provar isto, convido-te a testar o seguinte código:
Código: (c++) [Seleccione]
#include <iostream>
using namespace std;

signed char i = (1<<7);
unsigned char j = (1<<7);

int main(void) {
  if(i == j)
    cout << "São iguais" << endl;
  else
    cout << "São diferentes" << endl; }

Em termos de bits são iguais ... mas visto que em termos de sinais são diferentes, a comparação dá diferente, visto estares a comparar um 128 com um -128 ... (esta é a principal diferença entre o operador lógico == e o operador bitwise xor ...)

No entanto, estive a testar melhor com o AVR, e é indiferente (o que acho estranho, pois nos primeiros testes fazia diferença ... devo ter feito alguma coisa mal :P)

GnGz: Como assim o USART não funciona? Que tipo de testes fizeste? O teste to Njay não funcionou? Se o teste do Njay funcionou, mas não o que usa um unsigned char, vê se o echo que recebes tem uma nova linha, pois o teu terminal pode estar a enviar o carácter '\n' logo após o 'a', o que faria com que o LED acendesse, apenas por um período de tempo muito curto (quase não se repara), e depois apagasse logo assim que recebesse o '\n'.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: GnGz em 03 de Outubro de 2010, 12:32
Afinal funciona ... mas o meu codigo anterior porque raio nao funciona...

Código: [Seleccione]
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>

#define BAUDRATE 9600
#define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1)

//declaração das nossas funções
void USART_init(void);
unsigned char USART_receive(void);
void USART_send( unsigned char data);
void USART_putstring(char* StringPtr);



int main(void){

USART_init();      //Inicializar a usart
DDRB |= (1<<PB5);

while(1){      //Loop infinito

if ( USART_receive() == 'a' ) {   //faltava-te o () depois de USART_receive para o compilador saber que é um função, e o receive estava mal escrito
 PORTB |= (1<<PB5);
 _delay_ms(5000);
}
else {
  PORTB &= ~(1<<PB5);   //falta-te um ponto e virgula aqui para indicar fim de linha
}
}

return 0;
}

void USART_init(void){

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

unsigned char USART_receive(void){

   while(!(UCSR0A & (1<<RXC0)));
   return UDR0;

}

void USART_send( unsigned char data){

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

}

void USART_putstring(char* StringPtr){

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

}
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: Cynary em 03 de Outubro de 2010, 12:37
Afinal funciona ... mas o meu codigo anterior porque raio nao funciona...

Código: [Seleccione]
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>

#define BAUDRATE 9600
#define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1)

//declaração das nossas funções
void USART_init(void);
unsigned char USART_receive(void);
void USART_send( unsigned char data);
void USART_putstring(char* StringPtr);



int main(void){

USART_init();      //Inicializar a usart
DDRB |= (1<<PB5);

while(1){      //Loop infinito

if ( USART_receive() == 'a' ) {   //faltava-te o () depois de USART_receive para o compilador saber que é um função, e o receive estava mal escrito
 PORTB |= (1<<PB5);
 _delay_ms(5000);
}
else {
  PORTB &= ~(1<<PB5);   //falta-te um ponto e virgula aqui para indicar fim de linha
}
}

return 0;
}

void USART_init(void){

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

unsigned char USART_receive(void){

   while(!(UCSR0A & (1<<RXC0)));
   return UDR0;

}

void USART_send( unsigned char data){

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

}

void USART_putstring(char* StringPtr){

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

}

Faz o seguinte: envia o a pelo terminal, e espera 5 segundos, e diz-nos se ao fim desses 5 segundos o LED desliga, sem enviares mais nada.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: Njay em 03 de Outubro de 2010, 12:44
(...) Ambos os tipos têm o mesmo tamanho (8 bits) e na comparação de igualdade o sinal é irrelevante.

Isso está errado ...
(...)

Sim, tens razão, parvoíce minha. Isso acontece apenas porque na expressão do if ambos os membros são promovidos a int, e como neste caso o int é de 16 bits, a diferença surge depois de promovidos. No caso do código original não havia problema (embora seja má prática) porque o 'a' é sempre positivo.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: GnGz em 03 de Outubro de 2010, 12:50
Aiai ... o erro foi meu... e de tar habituado ao arduino... e fazer mal os reset's... ja funciona ! o meu codigo e ma pratica?

P.S : passados 5 sec o led nao desliga
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: Njay em 03 de Outubro de 2010, 12:51
Má prática misturar tipos "com sinal" com "sem sinal" sem casts explícitos.

Fica um aviso: o simulador da versão do AVR studio que tenho (4.13, build 528, já é antiguinha) tem um bug que faz com que o valor escrito no UCSR0 apareça também no UBRR0H.

Ainda ninguém por aqui falou em como se usa o simulador, pois não? Se eu tivesse um tempinho...
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: GnGz em 03 de Outubro de 2010, 13:02
Má prática misturar tipos "com sinal" com "sem sinal" sem casts explícitos.

Fica um aviso: o simulador da versão do AVR studio que tenho (4.13, build 528, já é antiguinha) tem um bug que faz com que o valor escrito no UCSR0 apareça também no UBRR0H.

Ainda ninguém por aqui falou em como se usa o simulador, pois não? Se eu tivesse um tempinho...

Ainda precebo muito pouco... e nao tou nada habituado ao C.. O meu obrigado a todos...
Título: Re: Introdução ao avr-gcc usando o AvrStudio [segunda parte lançada]
Enviado por: senso em 05 de Outubro de 2010, 00:48
   Boa noite, hoje iremos falar de como usar os pinos do nosso micro-controlador como entradas e saídas digitais, este deveria ter sido até o primeiro tema a estudar nesta série de tutoriais pois apesar de relativamente simples é de grande uso.
Então, para começar vamos ver quais os registos que cada porto tem associado a ele. Muito basicamente um porto é um conjunto de 8 pinos do micro-controlador que associados aos mesmos registos de controlo e que são numerados alfabeticamente, PORTA, PORTB, PORTC, o numero de portos depende do numero de pinos que o encapsulamento do nosso micro-controlador tem, no caso do atmega328p temos os portos B,C e D, mas o unico que tem os 8 pinos disponível é o porto D, isto acontece porque cada pino pode ter variadas funcionalidades, por exemplo os pinos 6 e 7 do porto B também são os pinos onde se liga o oscilador(cristal), o pino 6 do porto C é onde fica o botão do reset, no porto C do pino 0 ao pino 5 temos as 6 entradas analógicas, e assim sucessivamente para os outros pinos, e isto variada de modelo para modelo pois cada um tem mais ou menos funcionalidades e mais ou menos pinos que permitem ter implementadas essas mesmas funcionalidades, porque no caso do atmega328p se o encapsulamento for TQFP(é um dos vários tipos de smd) temos mais dois pinos ligados ao ADC.
Nesta imagem podem ver a funcionalidade de cada pino e a que porto ele está associado, o chip em questão é o atmega328p usado no arduino:
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg837.imageshack.us%2Fimg837%2F2779%2Fio1c.jpg&hash=5190b15ee8bf4f34e2d3db9585c3aeb31d6185f6)

E a sua relação com os nomes dados aos pinos no arduino:
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Farduino.cc%2Fen%2Fuploads%2FHacking%2FAtmega168PinMap2.png&hash=d622b49a37cdfed618371099b0260c8cfb532a93)

   Então, como vamos nós interagir com os nossos pinos digitais?
Para começar temos um registo que define se um pino é uma entrada ou uma saída digital, esse registo é o DDRx, sendo x a letra da porta que queremos configurar, no nosso caso temos o DDRB, o DDRC e o DDRD.
Como qualquer valor lógico, cada bit do registo DDRx pode ser 0 ou 1, sendo que um bit a 1 neste registo configura um pino como saída e um 0 configura o pino como uma entrada, vou agora usar um exemplo para mostrar como é que configuramos o porto D em que os pinos 0,1,2,3 são entradas e os pinos 4,5,6,7 são saídas:
Código: [Seleccione]
DDRD = 0b11110000;
Se quisermos que sejam todos saidas:
Código: [Seleccione]
DDRD = 0b11111111;;

   E se forem todos entradas, e se eu quiser que o primeiro e o segundo sejam saídas e o resto entradas?Respondam vocês!
No caso do porto D temos que ter cuidado porque os pinos 0 e 1 são os pinos que fazem a comunicação serial, e não convém mudar os valores do registo DDRD se não podemos ter problemas de recepção ou de emissão de dados, dai usar o |= e o o &= e não o = apenas, assim declara-mos os nossos DDRD, fazemos a inicialização da USART e sabemos que vai funcionar.

   Agora que sabemos como configurar os pinos vamos ver como é que fazemos os pinos tomarem um valor definido por nós, para escrever nos pinos, ou seja usar os pinos como saídas, usamos um novo registo o registo PORTx, tal como o DDRx trocamos o x pelo porto que queremos usar.
Após configurar um pino como saída podemos escrever o que quisermos nesse mesmo pino, para isso, e partindo do pressuposto que todo o porto está configurado como saída, eis o método usado para escrever no porto:
Código: [Seleccione]
DDRD = 0b11111111; //Configura todos os pinos do porto D como saídas
PORTD = 0b11111111; //Coloca todos os pinos do porto D a 1(HIGH)

De notar que usamos = e não |=, isto poderá causar-nos problemas, como vou explicar mais á frente.
Se quisermos colocar todos os pinos a 0 fazemos assim:
Código: [Seleccione]
DDRD = 0b11111111; //Configura todos os pinos do porto D como saídas
PORTD = 0b00000000; //Coloca todos os pinos do porto D a 0(LOW)

E se for um padrão de ligado, desligado,lidago, desligado, até preencher os 8 bits?

   Agora só nos falta saber como ler um pino, afinal nós não queremos só passar dados para o exterior, também queremos recolher dados, e para os recolher usamos um terceiro registo, o registo PINx, que tal como o DDRx e PORTx trocamos o x pela letra do porto que queremos ler, primeiramente temos de configurar os pinos do porto escolhido como entradas e depois lê-mos essas mesmas entradas, assim:
Código: [Seleccione]
DDRD = 0b00000000; //Configura todos os pinos do porto D como entradas
char my_var=0; //Criamos uma variável com 8 bits para armazenar o que lemos do porto D
my_var = PIND; //E lê-mos os pinos e armazenamos esses dados na nossa variável

Mas nem sempre queremos ler um porto inteiro, podemos querer só a informação de um pino, por exemplo um botão, para isso podemos usar um método diferente:
Código: [Seleccione]
DDRD = 0b11111110; //Configura os 7 bits mais significativos do porto D como saídas e o bit menos significativo como saída;
if(PIND & (1<<PD0)){
//O código que colocar-mos aqui dentro é executado quando o pino 0 está a 1(HIGH)
}
else{
//Esta parte é executada quando o pino 0 está a 0(LOW)
}

[/code

Ou tambem podemos ler o valor de um pino para uma variavel, assim:
[code]DDRD = 0b11111110;
char my_var         //Variavel onde vamos guardar os dados
my_var = (PIND & (1<<PD0));      //E assim lemos o estado do pino 0 para a variavel my_var

Assim como temos o PD0 temos qualquer Px0..7 em que x é a letra do porto que queremos usar e o numero entre 0 e 7 é o numero do pino, usando o operador | podemos comparar vários pinos assim:

Código: [Seleccione]
DDRD = 0b1111110; //Configura os 7 bits mais significativos do porto D como saídas e o bit menos significativo como saída;
if(PIND & ((1<<PD0) | (1<<PD1))){
//o código aqui colocado será executado quando os pinos 0 e 1 estiverem a 1(HIGH)
}

Penso que já estão a perceber a ideia. ;)
Mas ainda podemos fazer mais umas coisas com o nosso pequeno chip, quando usamos um botão como já devem ter reparado, quando o botão é premido normalmente liga o pino á massa, o que provoca um 0(LOW), mas quando se deixa de premir o botão o pino fica a flutuar, ou seja o seu valor é indefinido e isso não é bom e pode gerar problemas, então o nosso atmega328p tem pull-ups internos, o que são pull-ups?
Pull-ups são resistências com um valor de resistência elevado, 10K ohms ou mais para permitir que o pino possa continuar a ser ligado a 0(LOW) sem causar um curto-circuito e queimar alguma coisa, mas que impede o pino de ficar a flutuar num estado indefinido, para isso primeiro temos de configurar os nosso pinos como entradas, e depois isto vai parecer estranho, nos pinos que escolhemos para entradas, vamos usar o registo PORTx para escrever o valor 1(HIGH) nos pinos que escolhemos como entradas e isso vai activar os pull-ups em que escrever-mos 1(HIGH) nos pinos que estão configurados como saídas, um exemplo para clarificar:
Código: [Seleccione]
DDRD = 0b00111100; //Os primeiros 2 e os últimos 2 bits estão configurados como entradas, os restantes são saídas
PORTD = 0b00000011; //Os primeiros 2 pinos continuam sem pull-ups mas os últimos 2 pinos agora têm os pull-ups activados
//Sendo os primeiros pinos os que ficam logo ao lado do 0b e os últimos estão ao lado do ponto e virgula

Agora, relembrando uma pergunta feita á algum tempo atrás, porque razão usar = e não o |= e &=~ me pode trazer problemas?
Porque como podemos ter um porto com pinos configurados como entradas e outros como saídas, e podemos ter pinos com pull-ups e outros sem como no exemplo acima, vou introduzir mais um conceito util na manipulação básica de registos, máscaras de bits.
E o que são, máscaras de bits são variáveis de 8 bits que têm um determinado padrão de 0's e 1's que estão lá para evitar por exemplo que desactivemos os pull-ups de um pino quando escrevemos para outros pinos que são saídas, vou aqui dar um pequeno exemplo usando o código de cima para exemplificar:
Código: [Seleccione]
char mascara = 0b00000011; //Esta mascara impede-me de mexer nos últimos dois bits quando utilizar operadores lógicos
DDRD = 0b00111100; //Os primeiros 2 e os últimos 2 bits estão configurados como entradas, os restantes são saídas
PORTD = 0b00000011; //Os primeiros 2 pinos continuam sem pull-ups mas os últimos 2 pinos agora têm os pull-ups activados
PORTD = 0b00101010; //Se fizer isto vou desligar o pull-up do pino 0
//Então em vez de usar o perigoso igual uso o |= para colocar os bits que quero a 1
PORTD &= 0b00101010; //Mas mesmo assim continuo a remover o pull-up do pino 0
PORTD &= (0b00101010 | mascara); //Assim já fica o pino 0 a 1(HIGH) e os outros bits continuam como eu quero

Assim já podem usar os vossos pinos como entradas e saídas digitais e tirar proveitos dos pull-ups que estão incluídos no atmega.
Sei que o assunto de máscaras e operadores lógicos com o AND( & ou &=) o OR(| ou |=) o NOT(~ ou ~=) e o XOR(^ ou ^=) são de difícil compreensão, mas qualquer duvida sobre os mesmos é só dizer, e volto a referir o tutorial alojado no AvrFreaks.com, mais concretamente aqui:
Citar
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=37871

Este tutorial foi algo mais teórico, por isso vamos lá criar um programinha que lê um pino e que acende e apaga o led do arduino:
Código: [Seleccione]
Main{
Configurar os pinos, neste exemplo será do porto D
Loop infinito{
ler o valor do botão
Se led estiver ligado e botão==1 desligar led
Se led estiver desligado e botão==0 ligar led
}
}

Antes de passar para C tenho de falar noutro assunto, e que nos pode trazer problemas se não o tivermos em atenção, que é o facto de o botão não criar uma transição limpa de 0(LOW) para 1(HIGH) nem de 1(HIGH) para 0(LOW), mas sim, transita muitas vezes de estado até parar num estado definitivo, isto chama-se oscilação e pode levar a que o led se ligue e desligue várias vezes apesar de só premir-mos o botão uma vez, para evitar isso temos de usar uma técnica chamada deboucing, e existem muitos meios para atingir esse mesmo deboucing, mas o que vou usar é um método muito muito simples que recorre a um delay, podem ler mais sobre as funções de delay aqui:
Citar
http://www.nongnu.org/avr-libc/user-manual/group__util__delay.html

O método é simples, lê-mos o estado do botão, se for 1(HIGH) quer dizer que não carregamos no mesmo(já mostro uma foto do circuito para perceberem melhor), se for 0(LOW) quer dizer que pressionei o botão, então vou esperar um determinado tempo, neste caso irei usar 25 mili-segundos, mas se acharem que o valor deve ser outro digam, e se o valor se mantiver 0(LOW) considero isso como uma leitura válida.
Esta lógica invertida deve-se ao facto de ter o pull-up activado, logo está a 1(HIGH) quando não estou a tocar no botão e fica 0(LOW) quando carrego no botão.
Código: [Seleccione]
#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>

uint8_t readButton(void);

int main(void){
DDRD &= ~(1<<PD2); //Configura o pino 2 do porto D como entrada
PORTD |= (1<<PD2); //Activa os pull-ups no pino 2 do porto D
DDRB |= (1<<PB5); //Configura o pino 5 do porto B como saida, é o digital 13 do arduino

while(1){ //Loop infinito
if(readButton()==1){ //Verifico o estado do botão
PORTB ^=(1<<PB5); //O operado ^ chama-se XOR e se um bit está a 0 ele coloca-o a 1 e se está a 1 coloca-o a 0
}
_delay_ms(250); //Delay entre toques no botão
}
}

uint8_t readButton(void){
if((PIND & (1<<PD2)) == 0){ //Se o botão foi premido
_delay_ms(25); } //Faço o debounce do mesmo
if((PIND & (1<<PD2)) == 0){ //Verifico o valor do botão
return 1; } //Se continuar a 0 é uma leitura válida
else{ //Se não continuar a 0, é uma leitura inválida
return 0; }

}

Penso que já não é necessário mostrar passo a passo como se usa o AvrStudio e o Avrdude para programar o atmega, mas relembro-vos de que têm de carregar no botão de reset sempre que querem fazer upload de um programa novo.
O circuito usado é o seguinte:
Pino 2 do porto D(digital 2 do arduino) ligado a uma das pernas do botão, e a outra perna do botão é ligada á massa(Um dos pinos chamados GND no arduino).
Aqui fica um pequeno vídeo do código a correr no arduino, apesar do pequeno debounce que implementei nem sempre funciona perfeitamente, isso pode-se dever ao facto de ter pontaria e carregar no botão enquanto ele está a fazer o delay na função main e algumas vezes pode-se ver que o led liga e desliga apesar de só carregar uma vez, isso acontece porque os botões não são todos iguais e um delay de 25ms pode não ser suficiente.
Button deboucing (http://www.youtube.com/watch?v=kuIz0Edlrn0#)

Ignorem o ruído de fundo, é apenas um filme qualquer que estava a passar na tv, mais uma vez, qualquer duvida digam e boa programação. ;)
Título: Re: Introdução ao avr-gcc usando o AvrStudio [terceira parte lançada]
Enviado por: senso em 05 de Outubro de 2010, 15:08
Está assim tão mau?  :-[
Título: Re: Introdução ao avr-gcc usando o AvrStudio [terceira parte lançada]
Enviado por: Cynary em 05 de Outubro de 2010, 18:39
Penso que o tutorial está excelente como sempre, mas tenho alguns reparos a fazer:


Citar
no porto C do pino 0 ao pino 5 temos as 5 entradas analógicas

São 6 entradas analógicas, pois inclui o pino 0.

Citar
no nosso caso tempos o DDRB, o DDRC

tempos = temos

E penso que fizeste alguma confusão em relação ao uso do |= ...

Código: [Seleccione]
PORTD |= 0b00101010; //Mas mesmo assim continuo a remover o pull-up do pino 0
Essa afirmação é false ... só removerias o pull-up do pino 0 se usasses o =. Como tens o pull-up anteriormente, ele fica lá, visto que 1|0 = 1.
Nesse caso, a bit mask era desnecessária :P
A bit mask, como a descreveste, seria necessária, se usasses o =, mas não o |=.
Ao longo do código, tens vários exemplos desta confusão, ao dizeres que pões pinos a LOW, ou desligas a pull-up, quando usas o |=. Isso apenas é verdade quando eles já estavam desligados! Se estivessem antes ligados, o |= não os muda!

Um conselho extra acerca do uso do delay (neste caso não faz diferença, mas pode fazer noutros :P): Nunca usem uma variável como argumento do delay! Isto é porque para fazer cálculos, essa função usa floats, e como o AVR não processa floats automaticamente, é necessário carregar mais 2kB em bibliotecas para os processar (mais a lentidão associada :P). Quando se usam constantes numéricas, o compilador optimiza o código, removendo a necessidade de floats, visto que as contas são todas feitas antes da compilação. Mas se se usar uma variável, cujo valor possa mudar, as contas passam a ter de ser feitas durante a execução, o que provoca a necessidade dos floats.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [terceira parte lançada]
Enviado por: senso em 05 de Outubro de 2010, 20:03
Isto de escrever depois de estudar análise não dá com nada, e devia ter lido o texto todo com olhos de ver antes de o postar, estava a fazer outras coisas ao mesmo tempo e fiquei com o |= na cabeça quando queria dizer &=, vou já corrigir.
Obrigado pelo aviso.
Sim, quanto ao delay é verdade, iria referir isso noutro tutorial, mas existem vários métodos para dar a volta a isso.
Penso que já está tudo corrigido, se encontrarem mais algum erro digam  ;)
Título: Re: Introdução ao avr-gcc usando o AvrStudio [terceira parte lançada]
Enviado por: Cynary em 05 de Outubro de 2010, 20:25
Isto de escrever depois de estudar análise não dá com nada, e devia ter lido o texto todo com olhos de ver antes de o postar, estava a fazer outras coisas ao mesmo tempo e fiquei com o |= na cabeça quando queria dizer &=, vou já corrigir.
Obrigado pelo aviso.
Sim, quanto ao delay é verdade, iria referir isso noutro tutorial, mas existem vários métodos para dar a volta a isso.
Penso que já está tudo corrigido, se encontrarem mais algum erro digam  ;)

Agora já está melhor :P
Continuas a fazer um pequeno erro com o |= no entanto ...
Quando fazes exemplo deste tipo:

Citar
DDRD |= 0b00000000;   //Configura todos os pinos do porto D como entradas

Pode ou não configurar como entradas ... esse pedaço de código não faz absolutamente nada, visto que os que fossem saídas, continuariam saídas (1|0 = 1), e os que fossem entradas, continuariam entradas (0|0 = 0).
Título: Re: Introdução ao avr-gcc usando o AvrStudio [terceira parte lançada]
Enviado por: senso em 05 de Outubro de 2010, 20:28
Pois, eu só olhei para as linhas que disseste, tenho que ir ler tudo, vamos ver se á terceira é de vez
Como tutorial de iniciação acho que é mais correcto usar o = e não o |= e o &= nos exemplos porque estamos a referir-nos a todo o porto, e não a um bit ou outro, portanto acho que como está até está mais legivel para os iniciantes, acho é que tenho que ir alterar parte do texto  :-\
Título: Re: Introdução ao avr-gcc usando o AvrStudio [terceira parte lançada]
Enviado por: Cynary em 05 de Outubro de 2010, 20:48
Pois, eu só olhei para as linhas que disseste, tenho que ir ler tudo, vamos ver se á terceira é de vez
Como tutorial de iniciação acho que é mais correcto usar o = e não o |= e o &= nos exemplos porque estamos a referir-nos a todo o porto, e não a um bit ou outro, portanto acho que como está até está mais legivel para os iniciantes, acho é que tenho que ir alterar parte do texto  :-\

Pelos comentários e explicações que fizeste, o = encaixa perfeitamente lá, e funciona para quem não percebe bem as operações bit-wise. A minha crítica foi que em certos locais onde o = é mais apropriado, tinhas o |=, o que, pelo que li agora, parece estar já corrigido :)
Título: Re: Introdução ao avr-gcc usando o AvrStudio [terceira parte lançada - corrigido]
Enviado por: senso em 05 de Outubro de 2010, 20:50
Sim, ás vezes o vicio/hábito acaba por complicar demasiado o que se quer simples.
Acho que agora ficou no ponto.
Talvez um tutorial sobre operadores lógicos fique bem depois disto.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [terceira parte lançada - corrigido]
Enviado por: Cynary em 05 de Outubro de 2010, 20:53
Sim, ás vezes o vicio/hábito acaba por complicar demasiado o que se quer simples.
Acho que agora ficou no ponto.
Talvez um tutorial sobre operadores lógicos fique bem depois disto.

Pois, convém para evitar dúvidas nisso, visto serem muito usados ao mexer-se nos registers do AVR.
Se quiseres usar alguma coisa do meu, estás à vontade ;)
Título: Re: Introdução ao avr-gcc usando o AvrStudio [terceira parte lançada - corrigido]
Enviado por: senso em 05 de Outubro de 2010, 20:55
Escreves-te sobre os operadores lógicos, nem reparei, tenho de ir dar uma olhadela.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [terceira parte lançada - corrigido]
Enviado por: senso em 11 de Outubro de 2010, 20:12
Já está no forno um sobre operadores logicos e outro sobre o conversor analógico digital, e funções para o mesmo para simplificar o seu uso.
Então pessoal que quer mudar para c, como é que vai isso?
Havia alguns interessados, mas parece que o interesse morreu :/
Título: Re: Introdução ao avr-gcc usando o AvrStudio [terceira parte lançada - corrigido]
Enviado por: Capeleiro em 11 de Outubro de 2010, 20:24
Então pessoal que quer mudar para c, como é que vai isso?
Havia alguns interessados, mas parece que o interesse morreu :/
Bom, interessados ainda há pelo menos um!
Parabéns ao senso pelos tutoriais (e já agora também ao Cynary), porque eles ajudam a deixar de estar tão dependente do Arduino, ganhar bases em C, e sabendo o básico depois é muito mais fácil aprender o resto por nossa conta.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [terceira parte lançada - corrigido]
Enviado por: senso em 11 de Outubro de 2010, 20:27
Então, diz ai coisas que gostavas de aprender a fazer usando C?
Tenta uns projectos simples, como enviar uma mensagem em código morse usando um led, e passas a mensagem por serial usando o terminal, ou uma mini calculadora que dizes que operação queres fazer e passas os numeros ao avr e depois ele manda-te o resultado, para te ambientares ao AvrStudio, apesar de ainda estar aqui pouca informação já podes fazer algumas coisas para conheceres melhor o compilador do Avr.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [terceira parte lançada - corrigido]
Enviado por: Capeleiro em 11 de Outubro de 2010, 20:34
O meu próximo projecto vai ser um robot para a Busca e Salvamento, no Festival de Robótica 2011.
Já fiz um o ano passado, mas este ano o desafio vai ser usar apenas funções criadas por mim em C.
A parte mais difícil penso que serão os Servos, porque vou precisar de controlá-los em qualquer pin e não estar limitado só aos que têm PWM, vamos lá ver se consigo, ou se vou ter de usar a biblioteca Arduino (vou tentar não usar!).
Título: Re: Introdução ao avr-gcc usando o AvrStudio [terceira parte lançada - corrigido]
Enviado por: senso em 11 de Outubro de 2010, 20:37
Aconselho-te talvez a usar um micro-controlador separado só para os servos, porque servos sem ser com pwm tem de ser com uma emulação de pwm feita em software e quanto menos coisas estiverem a correr e mais pequeno e limpo for o código para os servos melhores, porque a biblioteca servo do arduino tem problemas pois tem interrupções a disparar por causa do millis() e tem mesmo falhas a nivel de código que causam jitter nos servos, e falhas na posição dos mesmos.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [terceira parte lançada - corrigido]
Enviado por: Capeleiro em 11 de Outubro de 2010, 20:43
Nesse caso até se torna mais fácil, talvez...
Uso um AVR "dos pequenos", basta ter uns três PWMs pelo menos e se tiver UART facilita.
E pôr em prática o que aprendi em C  ;D
Título: Re: Introdução ao avr-gcc usando o AvrStudio [terceira parte lançada - corrigido]
Enviado por: Cynary em 11 de Outubro de 2010, 20:52
O meu próximo projecto vai ser um robot para a Busca e Salvamento, no Festival de Robótica 2011.
Já fiz um o ano passado, mas este ano o desafio vai ser usar apenas funções criadas por mim em C.
A parte mais difícil penso que serão os Servos, porque vou precisar de controlá-los em qualquer pin e não estar limitado só aos que têm PWM, vamos lá ver se consigo, ou se vou ter de usar a biblioteca Arduino (vou tentar não usar!).

Esse é o meu projecto para este ano também :D
Vais participar em rescue B? (labirinto, em vez de seguir linha)
No meu, vou usar em princípio três micro-controladores ... um para motores/virar, um para conduzir o robô, e um para um sensor especial secreto hehe (não posso revelar os segredos todos, senão deixo de ser competitivo xD depois da competição ponho aqui em princípio, se funcionar bem).
O condutor e o controlador dos motores, comunicarão por serial ou SPI (se aprender a usar SPI, e for simples o suficiente). O sensor especial ficará como um slave no BUS I²C.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [terceira parte lançada - corrigido]
Enviado por: Capeleiro em 11 de Outubro de 2010, 21:03
Vou participar no Rescue A, porque no ano passado já participei neste escalão e aprendi bastante, este ano vou aplicar tudo o que aprendi e o resultado final vai ser melhor, espero  ;D
A minha ideia inicial era usar um ATmega328 para tudo, ligado a 3 multiplexers, o que daria mais 24 entradas extra. Mas agora começo a mudar de ideias e talvez use dois ATtiny2313, um para controlar servos e ao mesmo tempo ler alguns sensores e outro para ler mais uns quantos sensores, e ainda um ATmega328 como "cérebro" a receber todas as leituras e enviar as ordens para os Attiny.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [terceira parte lançada - corrigido]
Enviado por: senso em 11 de Outubro de 2010, 22:22
Usar mais micros fica mais caro, mas liberta tempo de processamento ao cérebro ao mesmo tempo que oferece mais espaço para programas e mais possibilidades e recursos que se podem usar, eu usaria SPI, é bastante simples de usar e em 8 clocks do SPI mandas e recebes 8bits.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [terceira parte lançada - corrigido]
Enviado por: Capeleiro em 12 de Outubro de 2010, 19:38
(...) eu usaria SPI, é bastante simples de usar e em 8 clocks do SPI mandas e recebes 8bits.
Pois, realmente é muito mais rápido. É mesmo isso que vou usar!  ;D
Título: Re: Introdução ao avr-gcc usando o AvrStudio [terceira parte lançada - corrigido]
Enviado por: senso em 15 de Outubro de 2010, 02:10
   Boa noite a todos, hoje vamos falar de algo um pouco diferente do normal até aqui, o tema hoje abordado serão os operadores lógicos binários e de salientar que são binários(em inglês bitwise, pois eles operam bit a bit e não num numero como um todo, mas em cada bit que cria o numero), não são apenas úteis para o nosso atmega/arduino, mas para qualquer micro-controlador e até programas feitos para correr no nosso pc, pois saber dominar os operados lógicos pode-nos poupar trabalho a fazer variadas operações, sendo um dos exemplos as divisões e as multiplicações.

   Para começar vamos introduzir os operadores e os respectivos símbolos/comandos que os invocam:
Código: [Seleccione]
AND &
OR |
NOT ~
XOR ^
Shift left <<
Shift right >>

   Para ajudar á sua compreensão vou descrever as tabelas da verdade para cada um dos operadores, as tabelas da verdade servem para mostrar qual é o resultado de um dado operador conforme as entradas variam, vamos definir também que a partir daqui, o valor lógico HIGH será representado por '1' e o valor lógico LOW será representado por '0'.
Código: [Seleccione]
Tabela da verdade do AND:
0 AND 0 = 0
0 AND 1 = 0
1 AND 0 = 0
1 AND 1 = 1

Tabela da verdade do OR:
0 OR 0 = 0
0 OR 1 = 1
1 OR 0 = 1
1 OR 1 = 1

Tabela da verdade do XOR:
0 XOR 0 = 0
0 XOR 1 = 1
1 XOR 0 = 1
1 XOR 1 = 0

Tabela da verdade do NOT:
NOT 0 = 1
NOT 1 = 0

Tabela da verdade para o Shift left aplicado a uma variável de 8 bits:
0x01<<0 = 0b00000001
0x01<<1 = 0b00000010
0x01<<2 = 0b00000100
0x01<<3 = 0b00001000
0x01<<4 = 0b00010000
0x01<<5 = 0b00100000
0x01<<6 = 0b01000000
0x01<<7 = 0b10000000

Tabela da verdade para o Shift right aplicado a uma variável de 8 bits:
0x80>>0 = 0b10000000
0x80>>1 = 0b01000000
0x80>>2 = 0b00100000
0x80>>3 = 0b00010000
0x80>>4 = 0b00001000
0x80>>5 = 0b00000100
0x80>>6 = 0b00000010
0x80>>7 = 0b00000001

Tabela para converter de binário para hexadecimal:
Binário    Hexadecimal Decimal
0000 = 0     =    0
0001 = 1     =    1
0010 = 2     =    2
0011 = 3     =    3
0100 = 4     =    4
0101 = 5     =    5
0110 = 6     =    6
0111 = 7     =    7
1000 = 8     =    8
1001 = 9     =    9
1010 = A     =    10
1011 = B     =    11
1100 = C     =    12
1101 = D     =    13
1110 = E     =    14
1111 = F     =    15
Numeração dos bits numa variável de 8bits:
0b00000000
  ||||||||
  |||||||bit0
  ||||||bit1
  |||||bit2
  ||||bit3
  |||bit4
  ||bit5
  |bit6
  bit7

   Depois, para montarmos um numero de 8 bits em binário para decimal é simples, fazemos assim:
   0b10101001 dividimos este numero em duas partes e removemos o 0b que só está lá para dizer ao nosso compilador que é um numero binário, e vamos ficar com 1010 e 1001, depois é só olhar para a tabela de cima e escrever 0x que diz ao compilador que é um numero em hexadecimal e juntamos a tradução dos 4 bits no numero ou letra que lhe corresponde e no caso do exemplo que estou a dar fica 0xA9.
   De notar que nas tabelas de verdade para o shift right e o shift left apenas usei um valor em cada uma, mas posso colocar lá outro valor(no caso do shift left não tem de ser sempre 0x01 e no caso do shift right não tem de ser sempre 0x80, usei esses números pois são o que me dão um bit sozinho á direita ou á esquerda respectivamente).

   Então vamos aprofundar os nosso conhecimentos sobre os nossos operadores lógicos, se repararem e por vezes para simplificar o nosso raciocinio e até foi assim que eu memorizei os operadores, é que podemos olhar para o AND como uma multiplicação entre os dois valores, mas atenção que não podemos usar o AND como alternativa ao * porque como referi em cima, este operadores operam individualmente sobre cada bit, o OR pode ser encarado como uma adição, o XOR como um detector de diferenças, pois se reparar-mos na tabela de verdade do XOR vê-mos que quando os bits são diferentes ele tem 1 como resultado e quando são iguais o seu resultado é 0, os shifts esses sim podem ser usados para multiplicar, usando um Shift Left, e para dividir usando um Shift Right, mas temos de ter em atenção que eles só dividem e multiplicam em potencias de dois, no caso de uma variável de 8 bits só podemos usa-los para multiplicar ou dividir por 2,4,8,16,32,64,128 e 256, e no caso da divisão não ficamos com números fraccionários nem com resto nem nada.
   As aplicações dos operadores lógicos binários são mais que muitas, e vou agora listar algumas das mais úteis para os nosso micro-controladores.
   
Código: [Seleccione]
unsigned char variavel = 0; //Aqui está a nossa variável inicializada a 0
   Para colocar o bit0 a 1 usamos o OR do seguinte modo:
   
Código: [Seleccione]
variavel = variavel | 0x01;   O 0x01 é a chamada de Máscara(em inglês MASK ou BITMASK ) e como podemos constatar pela tabela da verdade do OR o valor que irá ser guardado na nossa variável será 0x01, e agora podemos tirar partido dos operados compostos(na verdade não sei bem o nome disto em português, em ingles chamam-se compound assignments) e podemos escrever algo mais compacto e rápido de se escrever e que tem precisamente o mesmo efeito:
   
Código: [Seleccione]
variavel |= 0x01;    Isto não tem qualquer diferença a nível de código gerado, é apenas mais rápido de escrever-mos, podem usar o modo que quiserem.
   Agora, se quiser-mos voltar a colocar o bit0 a 0 não usamos o OR mas sim o AND:
   
Código: [Seleccione]
variavel =  variavel & ~0x01;    Como podem ver agora estamos a usar não apenas o AND mas também o NOT,mas não era suposto termos de usar só o AND?
   Sim, era e é, mas nós gostamos de olhar para o nosso código e perceber em que bit vamos mexer e assim é facil de reconhecer que vamos mexer no bit0, mas como esta máscara só serve para colocar bits a 1, temos de fazer o NOT da mesma e depois fazer o AND do resultado com a nossa variável.
   Vou mostrar-vos como é que isto funciona:
   
Código: [Seleccione]
variavel tem o valor 0x01 = 0b00000001
a mascara é 0b00000001
~0b00000001 = 0b11111110 -> a mascara fica assim após o NOT
variavel AND mascara =
0b00000001
       &0b11111110
------------------
0b00000000 -> e colocamos o bit0 a 0
   É claro que também podíamos fazer:
   
Código: [Seleccione]
variavel = variavel & 0xFE;   Mas não seria de todo tão perceptível á primeira vista reparar que íamos colocar o bit0 a 0, tal como o OR podemos usar o operador composto e o código fica assim:
   
Código: [Seleccione]
variavel &= ~0x01;   Também podemos usar o AND para saber se um bit está a 0 ou a 1 por exemplo para um if(), e de que modo?
   
Código: [Seleccione]
if(variavel & 0x04){
// Se o bit 2 estiver a 1 o if é executado
}
else{
//Se o bit 2 estiver a 0 o else é executado
}
   Atenção numa coisa, se o bit2 for 0, a condição do if é zero, mas se o bit2 for 1 o valor é DIFERENTE de 0, mas não é necessariamente 1, vai ser um valor igual ou superior a 1, se fizerem:
   
Código: [Seleccione]
if((variavel & 0x04) == 1)   Não se admirem que o vosso código não funciona como querem, porque ele vai funcionar tal e qual lhe estão a pedir, têm de ter atenção nestes pormenores, o primeiro exemplo funciona perfeitamente, não precisam de colocar nenhum ==.

   Agora o operador XOR, para que será que o queremos usar?
   Bem, é útil para fazer um bit mudar de 0 para 1 e de 1 para 0, quando nós não sabemos e/ou não queremos sequer saber de qual o valor do bit que queremos fazer mudar de estado, por exemplo para fazer o led do pino 13 do arduino piscar, basta usar o XOR, e não uma cadeia de if/else's ou coisas ainda mais estranhas.
   Vamos supor que temos um ciclo infinito(loop) em que queremos mudar o estado do bit0 a cada iteração que fazemos nesse loop, basta-nos usar:
   
Código: [Seleccione]
variavel = variavel ^0x01;   Ou mais uma vez a versão reduzida:
   
Código: [Seleccione]
variavel ^= 0x01;

   Ao longo de um programa, podemos acabar por ter mascaras de bits algo complexas que não queremos estar a re-escrever sempre que as queremos usar, por isso, podemos usar um #define, e em vez de usar-mos 0x01 sempre que queremos usar a nossa máscara escrevemos MASK, exemplo:
   
Código: [Seleccione]
#define MASK 0x01
//O nosso main começa aqui, temos umas quantas linhas de código até que precisamos da nossa máscara
variavel |= MAKS
   E agora os Shifts, que podem ser usados para várias coisas, por exemplo queremos colocar o bit5 a 1, em vez de vermos como se escreve isso em binário, usamos um shift left para criar a nossa máscara, deste modo:
   
Código: [Seleccione]
variavel |= (1<<5);   Para o AND, o XOR e o NOT o funcionamento é precisamente o mesmo, usamos (1<<x), em que x varia de 0 até 7 consoante o bit que queremos alterar ou comparar usando um if, pode-se usar o Shift left á vontade que tudo continua a funcionar certinho, e se quiser-mos fazer por exemplo uma multiplicação por dois, podemos usar o shift left para a fazer, eis um exemplo:
   
Código: [Seleccione]
2<<1 = 4 mas como?
2 em binário é 0b00000010
0b00000010 << 2 = 0b00000100 que é 4
   Não esquecer que estamos em base 2, logo fazemos um shift left para multiplicar por 2, dois shifts para multiplicar por 4, ou seja estamos a multiplicar por 2^(numero de shifts left), neste caso o ^ não quer dizer XOR mas sim elevado a. Atenção que qualquer valor que passe para a "frente" do bit7 é perdido, e a isso chama-se overflow, podemos por exemplo prevenir isto usando um int que tem 16 bits em vez de um char/unsigned char/uint8_t que tem apenas 8.
   
   A divisão é igual, apenas podemos dividir por potencias de dois e se o resultado devia dar algum numero com virgula o mesmo não é criado, ficamos apenas com o resultado inteiro e não o fraccionário, por exemplo 7/2:
   
Código: [Seleccione]
7 em binário é 0b00000111
0b00000111>>1 = 0b00000011
0b00000011 em decimal é 3
   Mas nós sabemos que 7/2 = 3.5 mas como já referi anteriormente, qualquer numero que passe para "trás" do bit 0 é perdido e a isto chama-se underflow e não é possível recuperar o bit perdido, podemos encarar os Shift Rights como divisões por 2^(numero de shifts right).

   Por agora é tudo, e espero que tenham ficado a compreender um pouco melhor toda esta confusão de operadores bitwise e de todo o seu poder apesar da sua aparente simplicidade podem tornar o vosso código mais rápido e eficiente e depois de estudarem isto um bocadinho e fazer as contas no papel recorrendo ás tabelas de verdade quando têm duvidas ajuda muito.
   Bom estudo pessoal ;)
   
   Tiago Ângelo
   15/10/2010
Título: Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
Enviado por: senso em 16 de Outubro de 2010, 14:02
Então, sugestões?
Título: Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
Enviado por: GnGz em 16 de Outubro de 2010, 14:34
Até aprendemos aqui C...
Título: Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
Enviado por: CBX em 19 de Outubro de 2010, 14:07
boas

excelente tutorial, têm-me ajudado bastante

tenho uma duvida: se eu usar _delay_ms(1000); o delay não deveria ser de 1s? é que assim o blink dura 10s...

estou a usar um atmega16L a 8mhz com um usbasp...

cumps
Título: Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
Enviado por: senso em 19 de Outubro de 2010, 14:10
Fizeste
#define F_CPU 8000000UL
O fuse CKDIV8 está desligado?
Imagino que estejas a correr com o oscilador interno, e se não mexeste nos fuses ele vem de origem com com o CKDIV8 ligado, que divide o clock por 8 e ficas efectivamente com um clock de 1Mhz.
Outra coisa,  é que com clocks muito elevados, ou mesmo delays muito longos não são possiveis, tenho ideia que a 16Mhz a máximo são de 600ms, para evitar isso podes fazer esta pequena função:

Código: [Seleccione]
void my_delay(int delay){
while(delay){
   _delay_ms(1);
   delay--; }

E na declaração das funções antes do main metes:
Código: [Seleccione]
void my_delay(int delay);
Título: Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
Enviado por: CBX em 19 de Outubro de 2010, 14:14
sim fiz #define F_CPU 8000000UL, não mexi foi nos fuses, possivelmente é isso...

como é que mexo nos fuses?

cumps e obrigado
Título: Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
Enviado por: senso em 19 de Outubro de 2010, 14:19
Para alterar os fuses, primeiro aconselho usar isto:
http://www.engbedded.com/fusecalc/ (http://www.engbedded.com/fusecalc/)

Depois, terás de usar o avrdude para os mudar, mas antes de os mudares mostra aqui que fuses queres alterar para vermos se não estás a desligar o spi ou coisas assim e ficas com o chip bloquado.
Um tutorial sobre fuses:
http://electrons.psychogenic.com/modules/arms/art/14/AVRFusesHOWTOGuide.php (http://electrons.psychogenic.com/modules/arms/art/14/AVRFusesHOWTOGuide.php)

Mais uma coisa, um fuse ligado é represantado por 0 e um desligado é representado por 1, isto por vezes causa muitos problemas e leva a que pessoas bloqueiem os seus chips pois desligam a programação via ISP, e coisas assim.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
Enviado por: CBX em 19 de Outubro de 2010, 14:26
obrigado, vou ler o tutorial depois digo alguma coisa
Título: Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
Enviado por: CBX em 19 de Outubro de 2010, 15:08
bem segundo percebi tenho que alterar nos fuse low bits os CKSEL de 3 a 0 que por default estão a 0001, que segundo a datasheet (pág. 25) para utilizar um cristal externo o valor será 1010 certo?

nos fuse high bits tenho de mexer no CKOPT de 1 para 0, mas aqui fiquei na duvida...

então:

low default 1110 0001 é alterado para 1110 1010
high defaul 1001 1001 é alterado para 1000 1001

que na calculadora dá: 0xEA 0x89

é isto?


informação correcta na pág. seguinte
Título: Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
Enviado por: senso em 19 de Outubro de 2010, 16:26
O 1010 é mesmo para cristal externo, ou oscilador externo? são coisas diferentes.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
Enviado por: CBX em 19 de Outubro de 2010, 16:28
segundo a datasheet: External Crystal/Ceramic Resonator 1111 - 1010
Título: Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
Enviado por: senso em 19 de Outubro de 2010, 16:51
Sim, parece-me ser isso, mas nunca mexi nos fuses não te posso dizer que é com 100% de certeza, irás usar que frequencia de cristal?
É que se for 16Mhz tens de escolher Crystal/ressonator High frequency.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
Enviado por: CBX em 19 de Outubro de 2010, 16:54
8mhz pois é o máximo do mega16l

Crystal/ressonator High frequency é aplicável a partir de quantos mhz?
Título: Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
Enviado por: senso em 19 de Outubro de 2010, 17:01
Está no datasheet, mas tenho ideia que é entre 8 e 16Mhz.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
Enviado por: CBX em 19 de Outubro de 2010, 18:37
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
Título: Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
Enviado por: senso em 19 de Outubro de 2010, 18:40
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?
Título: Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
Enviado por: CBX em 19 de Outubro de 2010, 19:15
parece-me bem como tens planeado, também não posso falar muito porque pouco sei  ;D

cumps
Título: Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
Enviado por: GnGz em 22 de Novembro de 2010, 19:47
Boas , e como se pode programar atmeis em ambiente linux?
Título: Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
Enviado por: senso 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.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
Enviado por: Cynary 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 (http://avr-eclipse.sourceforge.net/wiki/index.php/The_AVR_Eclipse_Plugin)
Título: Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
Enviado por: senso 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:
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg140.imageshack.us%2Fimg140%2F7140%2Fadc1.png&hash=b67d5907cd05677f00f8469cd19f025161c510fb)
   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.

(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg560.imageshack.us%2Fimg560%2F2607%2Fadc2.png&hash=235a2483f9f73d63268bd327287f98f086c7d57a)

   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).

(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg408.imageshack.us%2Fimg408%2F5953%2Fadc3.png&hash=3777071703f2b2cbb63a76f0da1ce51a4f0c515a)


   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
Título: Re: Introdução ao avr-gcc usando o AvrStudio [quarta parte - operadores lógicos]
Enviado por: senso 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/ (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:

(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg211.imageshack.us%2Fimg211%2F4734%2Fadc4.png&hash=4060c1c0c88f3e484889f3faf10fa3188af35bac)

   Após isso abram o terminal(existe uma nova versão, aqui https://sites.google.com/site/terminalbpp/ (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.

(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg42.imageshack.us%2Fimg42%2F7761%2Fadc5.png&hash=0a7efd7066e2e569e736bb6c978f5f915c783fce)

   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
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Quinta parte - Conversor ADC]
Enviado por: Capeleiro 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?
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Quinta parte - Conversor ADC]
Enviado por: senso 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).
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Quinta parte - Conversor ADC]
Enviado por: Capeleiro em 01 de Janeiro de 2011, 20:47
Obrigado  ;)
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Quinta parte - Conversor ADC]
Enviado por: SJD22 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.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Quinta parte - Conversor ADC]
Enviado por: Cynary 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.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Quinta parte - Conversor ADC]
Enviado por: SJD22 em 06 de Fevereiro de 2011, 20:10
Desculpa a ignorância mas o que é uma ISR?
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Quinta parte - Conversor ADC]
Enviado por: Cynary em 06 de Fevereiro de 2011, 20:17
Desculpa a ignorância mas o que é uma ISR?

Uma ISR é uma interrupt service routine.
Basicamente é uma função que é accionada quando uma interrupção ocorre.
Se não sabes o que é uma ISR, vou assumir que não sabes ainda trabalhar com interrupções ...
Por isso anexo aqui um guia em PDF que escrevi (tenho isto num servidor, mas neste momento esse servidor está em baixo :/ deve voltar amanhã ou depois).
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Quinta parte - Conversor ADC]
Enviado por: senso em 06 de Fevereiro de 2011, 22:30
É isso mesmo, podes sempre procurar no google que felizmente coisa que não falta são bons tutoriais sobre AVR e programação dos mesmos.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Quinta parte - Conversor ADC]
Enviado por: SJD22 em 06 de Fevereiro de 2011, 22:53
Obrigado.

Já agora, a função que o senso colocou no tutorial dos USART recebe apenas um char. Ou seja, para receber uma string por exemplo teria de invocar mais vezes a função receive?

Basicamente eu quero receber tudo o que o outro dispositivo tem para me dar...
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Quinta parte - Conversor ADC]
Enviado por: senso em 06 de Fevereiro de 2011, 23:04
Recebes sempre char a char, ou seja 8 bits de cada vez, a melhor maneira de fazer isso é com um buffer circular, digamos de 64 caracteres, ou 32 conforme o teu main loop, e meter a recepção por interrupção tambem, assim não estás a fazer polling á recepção.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Quinta parte - Conversor ADC]
Enviado por: souza_neto em 27 de Janeiro de 2012, 06:37
Cumprimento a todos,

Gostaria de lhes falar o seguinte. A algum tempo aqui no Brasil, antes de conhecer o LR eu comprei um livro: "Tecnicas de Projetos Eletronicos com os Microcontroladores AVR" Autor: Charles Borges de Lima. E me decepcionei com o livro. Para que tenham uma ideia, o livro na verdade deveria ter outro titulo: Exercicios Avancados Sobre AVR, porque digo isso. O autor nao usa de didatica, o livro é indicado para quem ja tem um bom e muito bom conhecimento tanto da linguagem C como do microAVR. No livro autor escreve varias macros usando operacoes logicas Bitwise, nao explica ou ensina como entende-las, isso é o minimo. Quando vai abordar um determinado assunto por exemplo, como fazer um bubo de led, um painel de mensagem eletronicas, protocolos de comunicacao etc, ele fala sobre o que é, quase nao tem exemplo e quando tem é bem generico e nao faz sentido pra quem esta aprendendo (iniciante) mas isso no prefacio nao foi dito. Em seguinda é pedido atraves de exercicios para executar varias tarefas que acredito que nem mesmo o auto consiga realiza-las. Ai depois eu encontrei o LR e a coisa mudou, os tutoriais do Senso realmente ensinam, quem nao tem nenhum conhecimento, se ler com calma ler novamente realmente aprende, principalmente sobre operacoes logicas Bitwise. Se os devidos autores dos tutoriais juntassem  todos esses tutoriais de forma bem organizada e distribuida daria um excelente bestseler, principalmente aqui para nos brasileiros que temos muita carencia em literatura sobre AVR em lingua portuguesa.

Fica ai o meu alerta sobre o livrinho, nao indico a ninguem.

E quanto aos tutoriais aqui do LR se estudar por eles aprende com certeza.

Att,

Ate uma proxima.

 
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Quinta parte - Conversor ADC]
Enviado por: senso em 27 de Janeiro de 2012, 14:23
Obrigado pelo comentário, ver se para a semana escrevo mais um ou dois tutoriais que sito ficou assim meio parado no tempo.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Quinta parte - Conversor ADC]
Enviado por: senso em 31 de Janeiro de 2012, 02:12
Boas noites caros colegas, hoje vamos iniciar o primeiro de três tutoriais sobre timers e o que podemos fazer com eles. Os timers têm diversos modos de operação, os 4 principais podem-se considerar como o modo normal em que funcionam como um simples contador que pode gerar interrupções, CTC(clear timer on compare match), fast pwm e phase correct pwm. No atmega328p e em quase todos os atmegas mais comuns temos acesso a 3 timers, 2 deles são de 8 bits e 1 é de 16 bits, e têm sub-modos/opções de funcionamento extra que os distinguem uns dos outros. Todos os timers têm associados a si 3 registos, TOP, BOTTOM e MAX, que podemos usar por exemplo para alterar o duty cycle quando estamos em modo pwm. TOP define o valor máximo até onde o timer pode contar, pode ser o máximo suportado pelo timer ( 255 para um timer de 8 bits ou 65535 para um timer de 16 bits) ou um valor definido pelo utilizador, BOTTOM é o valor mínimo que tipicamente é zero, existem aplicações em que é vantajoso alterar o BOTTOM mas são raras e é algo que não vou usar em nenhum destes timers e MAX é sempre o valor máximo até onde o timer pode contar.

Nesta primeira parte vamos falar apenas em usar o timer no modo normal e no modo CTC, a segunda parte será sobe um dos modos de pwm e controlar o brilho de uns quantos leds e terceiro será sobre phase correct pwm e controlo de motores.

Vamos então começar pelo modo normal e fazer mais um blinky deste vez baseado em timers e não com delays como o _delay_ms(), como estamos a usar timers temos primeiro de fazer algumas contas para fazer o setup dos mesmo para que contem o tempo que queremos. Este primeiro exemplo nem sequer vai usar interrupções, vamos simplesmente fazer um polling aos registos dos timers para sabermos quando atingimos o tempo que programa-mos inicialmente e fazemos um toogle ao led, e depois repetimos isto infinitamente.

Neste exemplo vamos fazer toogle do led a 2Hz, isto quer dizer que o led vai estar ligado 500ms e desligado 500ms, como o nosso clock é 16Mhz e o nosso maior timer só pode contar até 65535, isto quer dizer que colocando o timer a contar a 16Mhz só podemos contar até:
Código: [Seleccione]
1/16Mhz = 0,0000000625s //esta é a resolução do nosso timer por contagem
0,0000000625s * 65535 = 0,0040959375s // pouco mais que 4ms

Como podem ver o sinal de 16Mhz do clock é demasiado rápido para o que queremos teremos de usar prescalers para reduzir o sinal de relógio que é fornecido ao(s) timer(s), vamos fazer uma pequena tabela para ver-mos quais são as possíveis resoluções que obtemos dos timers usando os prescalers disponíveis no atmega328p:
Código: [Seleccione]
Resolução do timer = (Prescaler / Clock)
Os valores dos prescalers podem ser 1, 8, 64, 256 ou 1024
O clock é 16Mhz.

Valor prescaler  | Resolução do timer
1 | 0,0000000625s = 62,5 nS
8 | 0,0000005s = 0,5uS
64 | 0,000004s = 4uS
256 | 0,000016s = 16us
1024 | 0,000064s = 64uS

Como podem ver usando os prescalers temos uma grande variedade de frequências/resoluções disponíveis, por isso vamos agora fazer mais umas contas para vermos que valor temos de usar no timer para termos um delay exacto de 500ms(2Hz) para fazer o nosso novo blinky. Para o fazer-mos vamos usar uma pequena fórmula que pode ser facilmente derivada das formulas dadas na datasheet:
Código: [Seleccione]
Target Timer Count = (((Input Frequency / Prescaler) / Target Frequency) - 1)

Input frequency é 16Mhz
Target frequency é 2Hz

Prescaler value | Target timer count
1 | 7999999
8 | 999999
64 | 124999
256 | 31249
1024 | 7811,5

Como o valor máximo de um timer de 16 bits é de apenas 65535 os primeiros três valores podem ser excluídos pois o timer não conta até tão alto, o ultimo valor é inferior a 65535 mas é fracionário, pode ser usado mas vai acumular erros ao longo do tempo o que vai acabar por estragar a contagem e dar valores erróneos, ficamos assim com o quarto valor que nos vai dar um resultado de 500ms certinhos(mais ou menos o erro do nosso cristal, mas ai não temos controlo sobre isso), vamos então começar com pseudo-código:
Código: [Seleccione]
#include <avr/io.h>

int main(void){

//Definir pino do led como saida
//Configurar o timer

for(;;){
//Ler o valor do timer e conforme o mesmo fazer ou não o toogle do led
}

return 0;


Como temos estado a fazer todas as contas a pensar num timer de 16bits, temos de usar o timer 1 do Atmega, toda a informação sobre este timer começa na página 114 da datasheet do atmega328p.
Como vamos usar o timer no modo normal não temos muito que configurar, basta selecionar o prescaler que queremos, isso coloca automaticamente o timer a contar, depois é fazer poling ao registo que tem o valor da contagem e está basicamente o nosso novo blinky feito.
Código: [Seleccione]
#include <avr/io.h>

int main(void){

DDRB = (1<<PB5); //Pino PB5 configurado como saida

//Configuração do timer
TCCR1B = (1<<CS12); //1:256 prescaller

for(;;){
//Ler o valor do timer e actuar conforme
if(TCNT1 >= 31249){ //31249 é o valor que queremos para o delay de 500ms
PORTB ^= (1<<PB5); //Fazer toogle ao led
TCNT1 = 0; //Fazer reset ao timer
}
}

return 0;
}

Basta compilar e fazer o upload deste pequeno pedacinho de código para o vosso Arduino e verão o led que ele tem a piscar, como podem ver usar um timer no modo normal é muito muito simples a inicialização do timer custa-nos uma linha de código e depois basta um if para comparar o valor actual do timer com o nosso valor alvo.

No caso do timer 0 os bits do prescaler são configurados no registo TCCR1B e têm o nome de CS12, CS11, CS10 e representam os seguintes valores de prescaler:
Código: [Seleccione]

  CS12    CS11    CS10      Prescaler
    0       0       0       Timer desligado
    0       0       1       1:1 prescaler/é o mesmo que não ter um prescaler
    0       1       0       1:8 prescaler
    0       1       1       1:64 prescaler
    1       0       0       1:256 prescaler
    1       0       1       1:1024 prescaler
[code]

Como o nosso prescaler escolhido foi o de 1:256 basta colocar o bit CS12 a 1 e deixar todos os outros intocados. Um pequeno pormenor, o nosso micro-controlador é de 8 bits, ou seja o seu barramento de dados tem apenas 8 bits e como tal não podemos ler,escrever ou alterar uma variavel de 16 bits de uma só vez, e como podem ver no cógido não estou a usar variaveis de 8 bits para ler ou escrever valores no registo de contagem do timer, mas sim o TCNT1, que não é um registo verdadeiro mas um alias que o avr-gcc nos oferece para ler e escrever nos registos TCNT1L e TCNT1H sem termos de andar a fazer shifts e operações lógicas no código para colocar os valores de 16 bits nos dois registos de 8 bits, isto é feito pelo compilador e permite-nos criar um código mais limpo e legivel e tambem minimizar erros de programação, é um atalho identico ao ADCW que nos permite ler valores de 10 bits do ADC de uma só vez no nosso código em vez de ler o ACDH e ADCL e depois juntar o valor com operações lógicas num valor maior, mais uma vez para evitar erros e limpeza no código fonte.

Vamos agora usar o timer num outro modo, vamos usar interrupções em vez de estar a fazer constantemente polling á contagem do timer para decidir-mos se fazemos ou não toogle ao led. As interrupções permitem-nos libertar o micro-controlador para fazer outras tarefas enquanto o timer não atinge o valor esperado, e quando o timer atinge o valor programado, o processador pára o que estava a fazer, serve a interrupçõa e volta a fazer o que estava a fazer antes da interrupção, isto pode ser visto de uma maneira muito rudimentar de multi-tasking pois permite ao nosso micro-controlador correr um main pesado, complexo e cheio de cálculos e ao mesmo tempo ter um timer que gera uma interrupção por segundo por exemplo para actualizar um lcd, ou para fazer multiplexing a um array de leds com um tempo de refresh constante sem recorrer a delays, e para muitas outras coisas.

A interrupção é servida/atendida numa função especial chamada ISR(interrupt service routine), esta função não é chamada normalmente como uma qualquer outra função, mas sim por sinais que são gerados no micro-controlador, que são as requisições para interrupção, e tambem não é uma função criada do mesmo modo que as outras funções, é criada de uma maneira diferente que é para o nosso compilador saber que aquilo é uma interrupção. Mas existem algumas limitações em usar interrupções que devem ficar já a conhecer, NUNCA se usam delays em interrupções e devem evitar ao máximo chamar funções dentro de uma interrupção, delays dentro de uma interrupção vão praticamente contra o princpio de usar a interrupção em primeiro lugar, e chamar funções dentro de uma interrupção coloca um over-head bastante grande na entrada e saida da interrupção pois é necessário salva-guardar todos os registos do micro-controlador. O ponto a reter é que as interrupções são supostas de serem rápidas. Se quiserem por exemplo executar uma função complexa de 10 em 10ms, podem usar uma interrupção e nessa interrupção não chamam a função, mas sim uma flag que colocam a um sempre que passam os 10ms e no loop principal usam um if para ver se a flag foi ou não colocada a 1, e por ultimo, toda e qualquer variavel que é para ser partilhada entre o main/funções e uma interrupção tem de ser declarada volatile, o volatile diz ao compilador que esta variavel pode ser alterada numa interrupção e por isso sempre que é usada temos a garantia que é lida da ram e não armazenada num registo caso possivel para poupar um acesso á ram, o facto de não se declarar uma variavel como volatile pode causar um comportamento errático e por vezes dificil de se descobrir nos nossos programas, eis como delcaram uma variavel como volatile:
[code]
int variavel1 = 0; //isto é uma variavel normal
volatile int variavel2 = 0; //Isto é uma variavel declarada como volatile

Para criar-mos uma função de serviço para uma interrupção temos de o fazer de uma maneira diferente de quando criamos uma função, são criadas usando uma keyword especial que tem como argumento o nome do vector de interrupção, esses nomes podem ser encontrados na página 58 do datasheet do atmega328p, a esses nomes, substitui-mos os espaços por _ e adiciona-se _vect no fim deles, deixo a seguir um exemplo de um dos possiveis vectores de interrupção para o timer1. Se quiserem partilhar informação/variaveis entre o main e uma interrupção têm de usar variaveis declaradas como volatile porque as rotinas não têm nem argumentos de chamada nem têm um return.
Código: [Seleccione]
ISR(TIMER1_COMPA_vect){ //Esta é a rotina de serviço á interrupção para o vector TIMER1 COMPA
//código fica aqui
}

Para finalizar este mini crash-course sobre interrupções, não esquecer que como todos os outros periféricos presentes no micro-controlador, temos de configurar as interrupções para que se comportem do modo que queremos pois praticamente tudo o que está dentro do micro pode gerar interrupções, desde os 3 timers, ADC, UART, SPI, I2C, interrupções externas e mais, todos podem gerar interrupções. Temos ainda diga-mos que um interruptor mestre que activa ou desactiva as interrupções a nivel global, e que se nos esquecer-mos de o ligar nunca nenhuma interrupção será gerada, é uma unica linha de código que nos pode deixar a pensar por que razão algo não funciona como devia.

Vamos então passar á segunda parte deste tutorial e usar o timer no modo CTC, neste modo o timer conta até a um valor pré-programado, quando atinge esse valor gera uma interrupção e recomeça a contagem desde 0 (recomeça desde o BOTTOM e como este é 0 por defeito recomeça do 0, mas se alterarem o BOTTOM ele recomeça desde esse valor), tudo isto é feito em hardware, o que nos liberta o processador enquanto o timer conta até ao valor pré-programado. Mais uma vez vamos fazer o blinky com uma frequência de 2hz, vamos lá ao pseudo-código.
Código: [Seleccione]
#include <avr/io.h>
#include <avr/interrupt.h>

int main(void){

//Configurar o pino do led como saida
//Configurar o timer no modo CTC
//Ligar as interrupções globais, para a nossa ISR seja chamada
//Activar as interrupções do timer

for(;;){
//O nosso loop infinito, que vai ficar vazio, tudo é feito na ISR
}

return 0;
}

ISR(){ //Esta vai ser a nossa rotina de serviço á interrupção

//Fazer toogle ao led

}

Para começar podem notar que existe um novo include, que serve precisamente para incluir a tabela de vectores associados ás interrupções e assim como as macros ISR() que servem para criar as funções de serviço a interrupção, entre outras coisas. E agora um pouco mais sobre o modo CTC dos timers já que se configuram todos do mesmo modo, o modo especifico de funcionamento de cada timer é definido por 3 ou 4 bits, neste exemplo vamos continuar a usar o timer 1 que tem 16 bits, portanto teremos 4 bits para configurar qual o modo de funcionamento especifico de cada timer, são eles os bits WGM13, WGM12, WGM11 e WGM10 que estão presentes nos registos TCCR1A e TCCR1B, a tabela com todos os modos está localizada na página 137 do datasheet, como queremos o modo CTC podemos ver por essa tabela que basta-nos colocar o bit WGM12 a 1 no registo TCCR1B e colocar o nosso valor algo num dos dois registos de comparação disponveis no timer, estes registos aceitam valores de 16 bits, pois estamos a usar um timer de 16 bits e são os registos OCR1A e OCR1B, e é tambem através destes registos que se define o duty cycle quando usamos o timer no modo de pwm. Temos tambem de activar as interrupções do timer, neste exemplo basta-nos activar uma delas chamada OCIE1A (output compare interrupt enable A), isto diz ao nosso micro que queremos uma interrupção sempre que o valor de contagem do timer atinge o valor colocado em OCR1A, este bit encontra-se no registo TIMSK1 (timer 1 interrupt mask register).
Código: [Seleccione]
#include <avr/io.h>
#include <avr/interrupt.h>

int main(void){

DDRB = (1<<PB5); //Configurar o pino do led como saida


//Configurar o timer no modo CTC
TCCR1B = (1<<WGM12)|(1<<CS12); //Activar o modo CTC com um prescaler de 1:256
TIMSK1 = (1<<OCIE1A); //Activar as interrupções do timer

//Ligar as interrupções globais, para a nossa ISR seja chamada

for(;;){
//O nosso loop infinito, que vai ficar vazio, tudo é feito na ISR
}

return 0;
}


ISR(){ //Esta vai ser a nossa rotina de serviço á interrupção

PORTB ^= (1<<PB5); //Fazer toogle ao led

}

Mais uma vez, o nosso valor alvo vai ser colocado no registo OCR1A, podem comprovar isso mesmo pela tabela da página 137 no caso de duvida, e como estamos a usar esse registo a interrupção será tambem relacionada com ele, e o seu nome é TIMER1_COMPA_vect, COMPA para o OCR1A e COMPB para o caso de usarem o registo OCR1B.
Para activar as interrupções a nivel global temos de chamar uma pequena função chamada sei(), e para as desactivar-mos chama-mos uma função chamada cli(), os nomes derivam de sei -> SEt Interrups e cli -> CLear Interrupts, vamos então completar o nosso código:
Código: [Seleccione]
#include <avr/io.h>
#include <avr/interrupt.h>

int main(void){

DDRB = (1<<PB5); //Configurar o pino do led como saida


//Configurar o timer no modo CTC
TCCR1B = (1<<WGM12)|(1<<CS12); //Activar o modo CTC com um prescaler de 1:256
OCR1A = 31249; //O nosso valor alvo para termos interrupções a 2Hz
TIMSK1 = (1<<OCIE1A); //Activar as interrupções do timer

sei(); //Ligar as interrupções globais, para a nossa ISR seja chamada

for(;;){
//O nosso loop infinito, que vai ficar vazio, tudo é feito na ISR
}
return 0;
}


ISR(TIMER1_COMPA_vect){ //Esta vai ser a nossa rotina de serviço á interrupção

PORTB ^= (1<<PB5); //Fazer toogle ao led

}

Mais uma vez, o código deve compilar sem qualquer erro ou warning e após upload deverão ver o led a piscar, e sem qualquer código no loop infinito!

E já que estamos a brincar com timers que tal fazer ainda outro blinky! Mas desta vez criando algo de util, vamos re-criar a função millis() do Arduino que é bastante util para delays não bloqueantes e sem ser preciso andar a mexer com os timers sempre que querem adicionar mais uma função com uma temporização especial/rigida basta usar um if no main e uma chamada á millis() e bang, funções chamadas a intervalos regulares. Neste examplo vamos utilizar um timer de 8 bits, pois serve perfeitamente para o que queremos e deve-se sempre começar a usar os timers de 8 bits, pois o timer de 16 bits é um timer com mais funcionalidades e é melhor por exemplo para fazer controlo de motores via pwm pois oferece pwm de 10 bits ao invés do pwm de 8 bits que um timer de 8 bits oferece. Vamos então calcular qual o valor alvo e o prescaler que temos de usar para gerar-mos interrupções a um ciclo de 1000Hz ou de 1 em 1ms para incrementar a variavel que vai guardar os mili-segundos.
Código: [Seleccione]
Target Timer Count = (((Input Frequency / Prescaler) / Target Frequency) - 1)

Input frequency é 16Mhz
Target frequency é 1000Hz ou 1Khz

Prescaler value | Target timer count
1 | 15999
8 | 1999
64 | 249
256 | 61,5
1024 | 14,625

Como apenas o prescaler de 1:64 nos dá um valor inteiro inferior a 255(que é o valor máximo do timer de 8bits) vamos usar esse valor como o valor de comparação(compare) pois vamos usar novamente o timer no modo CTC, devido á simplicidade de uso, está presente em todos os timers e requer apenas uma configuração inicial, vamos então á datasheet ver como coloca-mos o timer 0 no modo CTC.
A secção do timer 0 é a numero 14 e começa na página 98, aconselho a leitura da datasheet para os bravos e para os curiosos mas é muito provavel que fiquem a coçar a cabeça, por vezes a datasheet é confusa e precisa de ser lida várias vezes até se entender, até porque o ingles que lá está nem sempre é o melhor, portanto vamos saltar directamente para a subsecção 9 que é onde está a descrição bit a bit de cada registo envolvido no controlo do timer 0. Tal como o timer 1 existe uma tabela com os diferentes modos de funcionamento, mas neste timer é muito mais pequena até porque este timer tem apenas 8 bits, mas mais uma vez basta colocar um bit a 1 para ligar o modo CTC, neste caso é o bit WGM01 no registo TCCR0A, e como o prescaler será de 1:64 temos de colocar os bits CS01 e o CS00 no registo TCCR0B a um, e o nosso valor alvo de 249 será colocado no registo OCR0A, mais uma vez temos de ir á tabela de interrupções ver o nome do vector de interrupção que como devem imaginar é muito parecido ao usado no timer 1, sendo ele TIMER0_COMPA_vect, e temos tambem de activar as interrupções do timer colocando o registo OCIE0A a um no registo TIMSK0, como podem ver muitos dos registos dos timers diferem apenas no numero do timer que usamos.
Deste vez não há pseudo-código pois não estou a introduzir nada de novo espero eu e o tutorial já vai longo e penso que está tudo explicado neste texto.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Quinta parte - Conversor ADC]
Enviado por: senso em 31 de Janeiro de 2012, 02:13
Código: [Seleccione]
#include <avr/io.h>
#include <avr/interrupt.h>

void timer0_init(void); //Protótipo da função

volatile unsigned long milis = 0; //Esta é a variavel volatile que vai ser partilhada pela função milis e pela interrupção
unsigned long now = 0;

int main(void){

DDRB = (1<<PB5); //Coloca o pino do led como saida
timer0_init();

for(;;){
if(milis-now >= 500){
now = milis;
PORTB ^= (1<<PB5); //Fazer toogle do led a cada 500ms/2Hz
}
}
return 0;
}


void timer0_init(void){
TCCR0A = (1<<WGM01); //Timer no modo CTC
TCCR0B = ((1<<CS01)|(1<<CS00)); //1:64 prescaler
OCR0A = 249; //Valor alvo para termos uma interrupção a cada 1ms
TIMSK0 = (1<<OCIE0A); //Activar interrupções do timer
sei(); //Activar interrupções globais
}

ISR(TIMER0_COMPA_vect){

milis++; //Aumentar a variavel milis em 1, para dizer que 1ms passou
}
Este código é igual ao exemplo Blinky without delay que se encontra no IDE do Arduino, mas feito bare-metal, a incialização do timer está numa função, pois se quiserem usar o código é mais simples de copiar.
Qualquer duvida deixem um post, e boas programações.

PS.: Tiver de dividir em duas partes pois excedia o limite de 20 mil caracteres por mensagem, dava jeito alterar esse limite para 25 mil, que os próximos tutoriais tambem são algo extensos.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Sexta parte- timers]
Enviado por: msr em 31 de Janeiro de 2012, 04:10
senso, já fazias era um .pdf com todo este material. Com o LyX (http://www.lyx.org/) era só despejar texto e imagens :)
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Sexta parte- timers]
Enviado por: metRo_ em 31 de Janeiro de 2012, 11:07
senso não há aí uma confusão? Em vez de 2Hz não querias dizer 1Hz?

De resto, estive a ler e está excelente :)
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Sexta parte- timers]
Enviado por: tr3s em 31 de Janeiro de 2012, 11:27
Está muita bom Senso!  ;D
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Sexta parte- timers]
Enviado por: zordlyon em 31 de Janeiro de 2012, 11:41
5*... parabéns e obrigado pelo trabalho que tives-te a disponibilizar isto...  ;)

Cumprimentos,
André Carvalho.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Sexta parte- timers]
Enviado por: tr3s em 31 de Janeiro de 2012, 11:43
Na secção de tutoriais já conseguem encontrar esta entrada!  ;)
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Sexta parte- timers]
Enviado por: senso em 31 de Janeiro de 2012, 14:33
Eu penso que não, porque 1Hz é 1s, e 2Hz são 0.5s, o que pode levantar confusão é que é feito um toogle a 2Hz, e isso divide a frequência para 1Hz, mas não digo que não estou a pensar mal
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Sexta parte- timers]
Enviado por: metRo_ em 31 de Janeiro de 2012, 21:09
Eu penso que não, porque 1Hz é 1s, e 2Hz são 0.5s, o que pode levantar confusão é que é feito um toogle a 2Hz, e isso divide a frequência para 1Hz, mas não digo que não estou a pensar mal

Eu li mal, realmente está correcto pois falas em toogle(não sei é se dizer que o toogle é feito a 2Hz e depois só o fazer a cada 500ms está muito correcto mas se calhar sim e estou a confundir-me) e eu estava a pensar em piscar à freq de 2Hz. Li logo que querias que ele esteja 500ms ligado e 500ms desligado, sendo assim um período é ele estar 500ms ligado e 500ms desligado, periodo=1s, f=1Hz, isto é, ele pisca a freq de 1Hz.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Sexta parte- timers]
Enviado por: senso em 01 de Fevereiro de 2012, 19:59
Boas noites, hoje vamos falar um bocadinho sobre como gerar PWM usando os timers do nosso Atmega. PWM é um sigla que quer dizer pulse width modulation, ou em português modulação da largura do pulso, o que isto quer dizer é que recorrendo ao hardware do nosso timer( ou via software com algumas limitações, mas este tutorial só se foca em pwm usando o hardware dos timers) podemos controlar o tempo que um sinal está a 1(HIGH) e a 0(LOW), ao fazer-mos isto a voltagem/energia média que este sinal fornece pode ser variada e assim podemos controlar o brilho de um led, misturar cores num led RGB, controlar a velocidade de um motor DC, e até gerar áudio, podemos ainda usar sinais PWM para controlar os típicos servos tão úteis na robótica caseira, e usando um pequeno filtro RC podemos ainda converter o sinal PWM num sinal analógico com uma variação suave de voltagem e não apenas entre 0's e 1's, na imagem abaixo podem ver como se parece uma onda PWM com diferentes duty cycles, duty cycle é o nome que damos á relação de tempo que o sinal está a 1 ou a 0.

(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fdesmond.imageshack.us%2FHimg194%2Fscaled.php%3Fserver%3D194%26amp%3Bfilename%3Dpwm1.png%26amp%3Bres%3Dmedium&hash=f0fcb6815cab7cc4c2f0a8d6a4ad4215c55df547)

O nosso micro oferece-nos vários tipos de pwm, Fast PWM, Phase correct PWM e Phase and Frequency correct PWM, neste tutorial vou apenas abordar o Fast PWM, deixando o Phase correct PWM para o próximo. Temos também possibilidade de gerar pwm com 8, 9 ou 10 bits de resolução, mas mais de 8 bits precisamos de recorrer ao(s) timer(s) de 16 bits como é óbvio. Fast PWM é um modo bastante simples de se configurar e que permite frequências de pwm superiores á dos outros dois modos de operação, este modo não é aconselhado para fazer o controlo de motores, mas sim o phase correct PWM, podem ver na imagem abaixo a diferença em fast pwm e phase correct pwm.

(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fdesmond.imageshack.us%2FHimg96%2Fscaled.php%3Fserver%3D96%26amp%3Bfilename%3Dpwm2.gif%26amp%3Bres%3Dmedium&hash=6150df95ff38ed339e9a80277373be1b7077c27d)

Os dois sinais de cima são gerados por fast pwm e os dois de baixo são gerados usando phase correct pwm. A diferença principal entre os dois é que em fast pwm a parte a 1(HIHG) do sinal começa sempre no mesmo sitio,mas a transição para 0(LOW) é arbitrária(regira pelo valor definida por nós nos registos do pwm), isto leva a que o centro da onda enquanto a 1 não esteja por assim dizer centrado com o resto do sinal a 0, o que pode causar alguns problemas quando usada para controlar motores dc/pontes H.

(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fdesmond.imageshack.us%2FHimg688%2Fscaled.php%3Fserver%3D688%26amp%3Bfilename%3Dpwm3.gif%26amp%3Bres%3Dmedium&hash=a7691c866546eb05656d02edf849a5fc4630a2d1)

Esta imagem acima mostra como o modo phase correct pwm funciona, o timer conta de BOTTOM para TOP, e depois de TOP para BOTTOM, e a onda de pwm é gerada comparando o valor dessa mesma contagem com o nosso duty cycle programado, ao contrário do fast pwm que conta de BOTTOM até TOP e depois recomeça do BOTTOM. Adicionalmente ainda temos o phase and frequency correct pwm que nos permite variar a frequência do pwm, este modo está apenas disponivel no timer 1 de 16 bits.

Deixando toda este teoria para trás vamos passar a um exemplo, neste vamos utilizar pwm para gradualmente ligar e desligar um led, algo simples que mostra perfeitamente o intuito do pwm.
Código: [Seleccione]
#include <avr/io.h>

int main(void){

//Colocar pino do led como saida
//Configurar o timer

for(;;){
//definir o novo valor do duty cycle
//pequeno delay se não vai parecer instanteneo
//pois o nosso micro está a correr a 16Mhz
}

return 0;
}

Neste exemplo vamos usar o timer 0, simplesmente por para fazer o fade-in/out do led não precisa-mos do timer de 16 bits, a primeira coisa que vamos aprender é como configurar/escolher os diferentes modos de funcionamento de um timer, esses modos estão descritos numa tabela chamada Waveform generation modes, que no caso do timer0 do Atmega328p estão na página 109 da datasheet.
Os bits que afectam os diferentes modos são o WGM02, WGM01 e WGM00 que se encontra nos registos TCCR0A e TCCR0B.

(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fdesmond.imageshack.us%2FHimg843%2Fscaled.php%3Fserver%3D843%26amp%3Bfilename%3Dpwm4.png%26amp%3Bres%3Dmedium&hash=ba3142028a037509de616cbfa3acd2795c14fa49)

Para usar-mos Fast PWM temos dois modos á escolha, modo 3 e o modo 7, a diferença entre eles é que no modo 3 o TOP está fixo em 0xFF ou seja o timer conta até 255, enquanto que no modo 7 o TOP pode ser definido e assim pode-se alterar a frequência para além do duty-cycle, até agora numa precisei de o fazer, mas se precisarem de alterar a frequência já sabem que é possivel, para este exemplo não precisamos de o fazer por isso vamos antes escolher o modo 3, e como tal temos de colocar os bits WGM01 e WGM00 a 1 no registo TCCR0A.

Como queremos controlar os pinos do nosso micro através do timer para gerar o PWM temos tambem que configurar o modo como esses mesmos pinos reagem aos comandos do timer, como isto é feito em hardware não podemos escolher os pinos que queremos, por exemplo os pinos em que é gerado o pwm do timer 0 são os pinos PD6 e PD5. Vamos usar o pino PD6, e é neste pino que teremos de ligar o led, e teremos então de o configurar, os vários modos de funcionamento do pino quando usado no modo Fast PWM estão descritos na tabela 14-3 da página 106, que para simplificar fica aqui uma imagem da mesma:

(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fdesmond.imageshack.us%2FHimg836%2Fscaled.php%3Fserver%3D836%26amp%3Bfilename%3Dpwm5.png%26amp%3Bres%3Dmedium&hash=5cd5fbbd5ab5ac734d3783b683a8a3762c8f5071)

Como podem ver pela tabela, podemos usar o pino normalmente como um pino digital, o pino pode fazer um toogle, ou pode ser usado como saida de pwm com o sinal invertido ou não, vamos usar o sinal normal pois, o cátodo do led vai ligar á massa e o anodo vai ser ligado ao pino PD6, se se usar pwm invertido o efeito de fade in fica trocado com o de fade out, mas o funcionamento em si é igual.
Neste caso vamos escolher o modo 3, ou seja teremos de colocar ambos os bits COM0A1 a 1.

para definir-mos um determinado duty cycle temos dois registos, um para cada pino em que cada timer pode gerar pwm, são esses registos os OCR0A e OCR0B para o timer 0, OCR é um sigla para Output Compare Register, o pwm é gerado comparando o valor que colocamos neste registo com a sua actual contagem.
Por ultimo, falta-nos escolher um prescaler para fornecer um sinal de relógio ao timer, não existe de todo uma regra para que frequência devem fazer pwm, mas tipicamente quanto mais elevada melhor, deixo-vos as diferentes frequências de PWM que são possíveis de obter com um timer de 8 bits:
Código: [Seleccione]
Fclk=16Mhz //Cristal do Atmega/Arduino

A formula:
 Fpwm = Fclk/(Prescaler*256)

 Prescaler Fout
     1 62.5Khz
     8 7812.5Hz
     64 976Hz
     256 244.141hz
     1024 61.03Hz

Como podem ver as frequências de pwm não são muuito elevadas, e se forem usadas por exemplo para controlo de motores, recomendo que usem apenas o prescaler de 1:1 pois todas as outras frequências vão ouvir os motores a "cantar", para o caso dos led's diria que o prescaler de 1:1024 também não deve ser usado pois podem notar flicker dos mesmos, vamos escolher um valor intermédio e usar o prescaler de 1:64, e vamos então juntar tudo isto num pedacinho de código.
Código: [Seleccione]
#include <avr/io.h>
#define F_CPU 16000000UL
#include <avr/delay.h>

int main(void){

unsigned char i=0; //Variavel para ser usada no for()

DDRD = (1<<PD6); //Coloca o pino do led/pwm como saida
//O PD6 é o digital 6 do Arduino

//Configuração do timer
TCCR0A = ((1<<COM0A1)|(1<<WGM01)|(1<<WGM00)); //Activar o pwm no pino PD e coloca o timer em modo Fast PWM
TCCR0B = ((1<<CS01)|(1<<CS00)); //Activar o timer com um prescaler de 1:64


for(;;){

//Fade up
for(i=0; i<255;i++){
OCR0A = i; //Define o novo valor do duty cycle
_delay_ms(50); //Um pequeno delay
}

//Fade down
for(i=255; i>0;i--){
OCR0A = i; //Define o novo valor do duty cycle
_delay_ms(50); //Um pequeno delay
}
}

return 0;
}


É só ligar um led, com o cátodo(pata mais comprida) á massa e a outra pata ligada através de uma resistência com 330 ou 380ohm liga ao pino PD6/Digital 6 do Arduino.
Já cá venho deixar um filme do que deverão ver, e de um mini projecto, uma mood light.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 02 de Fevereiro de 2012, 21:27
E para variar um bocadinho do típico tutorial com muito texto e pouca ação, vamos juntar o que já sabemos e usar isso para montar uma mood light, para que não sabe é basicamente um/vários led's RGB que vão percorrendo lentamente todas as cores do espectro visível, para isso vamos usar timers para gerar sinais pwm para poder-mos controlar a intensidade da cada cor e assim gerar-mos todas as cores do arco-íris e mais umas quantas, e para aproveitar juntar mais um pouco de funcionalidade vamos adicionar dois potenciómetros, um para controlar a intensidade do(s) led(s) e outro para controlar a velocidade a que os mesmos mudam de cor, e em vez de delays no nosso código vamos também tirar partido do nosso millis, assim já se pode dizer que temos um projeto com alguma complexidade, com um efeito prático bonito(toda a gente gosta de leds e muitas cores =) ).
Provavelmente a parte deste projeto que será mais estranha é o método como vamos gerar as cores, existem algumas fórmulas por ai que nos permitem gerar basicamente todas as cores do espectro visível,mas não oferecem um método simples de se controlar a intensidade da cor,por isso eu vou fazer isto de outra maneira, vou usar uma função que converte cores em HSV para RGB. HSV é um método de representar cores de um método diferente de RGB, basicamente RGB tem um valor em 0 e 255 (se pensar-mos uma resolução de 8 bits por cor) para cada um dos canais R (vermelho), G (verde) e B (azul), enquanto que o HSV representa as cores num cilindro, sendo H (hue) a cor que queremos escolher e que pode ser um valor entre 0 e 360º, S (saturação) é o quão intensas serão as cores, ou seja, quanto maior o valor da saturação mais intensa é a cor, e quanto menor é este valor mais branca é a cor, quando a saturação atinge o seu mínimo não importa o hue e obtemos sempre uma cor branca, e V (value) é a intensidade da cor, e é este parâmetro que nos permite variar a intensidade/luminosidade da nossa mood light, para converter de HSV para RGB's existem alguns métodos, eu vou usar uma fórmula que se encontra facilmente na internet, e se quiserem ler um pouco mais sobre este e outros espaços de cores, nada melhor como a Wikipédia:
http://en.wikipedia.org/wiki/HSL_and_HSV (http://en.wikipedia.org/wiki/HSL_and_HSV)

Uma outra "novidade" será o uso de ponteiros, os meus tutoriais não são de todos um tutorial sobre programação C, e o uso de ponteiros é o que dá ao C a sua flexibilidade e poder, posso esclarecer duvidas sobre os mesmos nos comentários, e deixo aqui novamente um link para a Wikipédia sobre eles, mas podem sempre pesquisar mais sobre o assunto pois um tutorial sobre ponteiros seria algo extensivo e fora do intuito deste mini-projecto.
http://en.wikipedia.org/wiki/Pointer_%28computing%29 (http://en.wikipedia.org/wiki/Pointer_%28computing%29)

Como vimos no anterior tutorial cada timer só nos oferece 2 canais de pwm, e para um led RGB precisamos de 3, por isso iremos usar o timer 0 e o timer 2 que são ambos de 8 bits, usando 2 canais do timer 0 e um canal do timer 2 para gerar os três sinais de pwm necessários para um led RGB, se não tiverem um led RGB podem sempre usar 3 leds separados, um vermelho, um verde e um azul, a vantagem dos RGB é que as cores ficam mais bem misturadas e se forem leds foscos nem é preciso colocar um papel por cima por exemplo para misturar as cores numa só cor e não em spots das 3 cores fundamentais.
Iremos também usar o timer 1 como base de tempo para o millis, assim como dois pinos para ler os dois potenciómetros, no meu caso vou usar potenciómetros de 10Kohm's, podem usar de outros valores, mas entre 5 e 50Khom é o recomendado para usar com o ADC do micro-controlador, valores muito maiores podem causar oscilações nos valores lidos.

Vamos começar com um bocadinho de pseudo-código.

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

int main(void){

//Definir pinos dos leds como saida
//Configurar timer0 para gerar dois sinais pwm
//Configurar timer2 para gerar um sinal pwm
//Configurar timer1 para ser usado como base de tempo para o milis
//Configurar o ADC

for(;;){
//Ler o valor dos dois potenciometros
if(lastTime-milis() > timePotValue){ //Se tempo que passou > tempo definido pelo potenciometro, então
//Fazer update aos 3 duty-cycles para gerar a nova cor
}
}

return 0;
}

O código para inicialização e leitura do ADC basta simplesmente ir buscar as funções criadas no tutorial sobre o mesmo, e a configuração dos dois timers que vão gerar o pwm vai ser em tudo identica á feita no tutorial anterior, com o mesmo prescaler, apenas ativando os dois OCR no caso do timer0 e no caso do timer 2 apenas muda o nome dos registos que de resto é em tudo igual ao que foi feito no tutorial anterior, para o timer 2 irei usar o pino PD3, que corresponde ao OC2B.

Vamos então fazer umas continhas para que o timer 1  gere a base de tempo para o milis, o timer 1 como já referi é um timer especial, pois para além de ser de 16 bits ao invés dos outros dois timers, tem alguns extras, por exemplo pode ser alimentado por um sinal de relógio externo, pode ser usado para medir a frequência de um sinal externo ao micro-controlador com recurso a muito pouco código, tem um debouncer que lhe permite ler um botão sem necessitar de código para o debounce, pode gerar pwm até 10 bits e mais alguns extras.
Para o milis queremos gerar interrupções a 1000Hz, ou seja uma interrupção por cada mili-segundo, usando novamente formulas disponíveis na datasheet vamos calcular qual o valor que precisamos de colocar no registo de comparação que irá por sua vez gerar a interrupção.

Código: [Seleccione]
Target Timer Count = (((Input Frequency / Prescaler) / Target Frequency) - 1)
Input Frequency = 16Mhz
Target frequency = 1000Hz

Prescaler value | Target timer count
1 | 15999
8 | 1999
64 | 249
256 | 61.5
1024 | 14.625

Neste caso temos uma larga escolha de valores que podemos usar, tirando apenas os dois últimos que são fraccionários e que por isso vão acabar por introduzir erros na nossa contagem dos mili-segundos, podemos usar o primeiro valor pois cabe perfeitamente no timer de 16 bits, visto que o valor máximo que ele suporta é 65535, vamos então juntar todas estas coisas ao pseudo-código para o transformar num mood light funcional.

Código: [Seleccione]
#include <avr/io.h>
#include <avr/interrupt.h>
#include <math.h> //Necessário devido ao uso de floats/numeros de virgula flutuante

#define INTENSITY 0 //Potenciometro que controla a intensidade está ligado ao pino 0 do ADC,ou seja PC0
#define TIME 1 //Potencioetro que controla o delay entre transições está ligado no pino 1 do ADC, ou seja PC1

//Protótipos das funções
void timer0_init(void);
void timer1_init(void);
void timer2_init(void);
void adc_init(void);
void set_pwm(uint8_t red, uint8_t green, uint8_t blue);
void Hsv2Rgb( double *r, double *g, double *b, double h, double s, double v );
unsigned long milis(void);
uint16_t read_adc(uint8_t channel);

//Variaveis globais
volatile unsigned long milis_count = 0; //Variavel partilhada entre a interrupção do timer 1 e a função milis

int main(void){

//Variaveis locais
unsigned int delayTime = 10; //Variavel usada para guardar o valor de delay lido do potenciometro
uint16_t intensity = 0; //Variavel usada para guardar o valor de intensidade lido do potenciometro
unsigned long lastTime = 0; //Variavel usada para guardar o ultimo valor do milis
double red = 0.0; //Valor actual do vermelho, retornado pela função HSV->RGB, entre 0 e 1
double green = 0.0; //Valor actual do verde, retornado pela função HSV->RGB, entre 0 e 1
double blue = 0.0; //Valor actual do azul, retornado pela função HSV->RGB, entre 0 e 1
double hue = 0.0; //Valor actual do hue, ou seja define a cor actual
double saturation = 100; //Valor da saturação, está definido como 100(máximo), mas podem alterar
double value = 1.0; //Valor de intensidade, convertido para ser passado á função HSV->RGB

timer0_init(); //Chama inicialização do timer 0
timer1_init(); //Chama inicialização do timer 1
timer2_init(); //Chama inicialização do timer 2
adc_init(); //Chama inicialização do timer 0

sei(); //Activa as interrupções a nivel global, para que milis funcione
DDRB = (1<<PB5); //Pino PB5 como saida (led do Arduino), usado para ver-mos que o programa está a correr

for(;;){

//Leitura dos potenciometros que definem o delay e a intensidade
delayTime = read_adc(TIME) * 2; //Multipliquei a leitura por 2 para que o valor máximo entre cor seja de 2s
//mas podem alterar isto a vosso gosto
intensity = read_adc(INTENSITY); //Leitura do valor da intensidade

if(delayTime <= 10){ //Usado para que o valor do delay entre cores não seja menor que 10ms
delayTime = 10;
}

value = ((double)(intensity/1024.0)*255.0); //Isto é basicamente um map() do arduino, para garantir que o valor
//do value não passa de 255
if(value >= 255.0){ //Usado como teste de sanidade para garantir que o valor não passa de 255
value = 255.0; //devido a arredondamentos de floats
}

if(value <= 0.1){ //Para garantir que o value vai mesmo a 0, e desliga o led
value = 0.0;
}


if( (milis() - lastTime) >= delayTime){ //Se tempo que passou > tempo definido pelo potenciometro, então
//Fazer update aos 3 duty-cycles para gerar a nova cor

PORTB ^= (1<<PB5); //Faz toogle ao led, funciona como uma indicação de que o programa não bloqueou
lastTime = milis();


//Chama a função HSV->RGB e faz o update do duty-cycles

Hsv2Rgb(&red, &green, &blue, hue, saturation, value);

red = red * 255.0; //Os valores retornados pela função variam entre 0 e 1, então multiplicam-se
green = green * 255.0; //por 255 para os converter em valores 0-255
blue = blue * 255.0;

hue = hue + 0.5; //Este é o passo entre cores, podem tambem alterar este valor

if(hue > 360){ //Quando chega-mos ao limite das cores, volta-mos ao inicio
hue = 0;
}

//Aqui faz-se uma conversão de float para inteiro ao usar o (uint8_t), ou seja um cast para um valor de 8 bits
//Se tiverem leds de cátodo comum usem este código
OCR0A = (uint8_t)red; //Led vermelho está conectado no pino PD6/digital 6
OCR0B = (uint8_t)green; //Led verde está conectado no pino PD5/digital 5
OCR2B = (uint8_t)blue; //Led azul está conectado no pino PD3/digital pin 3

//Com leds de anodo comum é necessário inverter os valores, se tiverem leds de anodo comum
//comentem para fora as 3 linhas de cima e descomentem estas:
//OCR0A = 255-(uint8_t)red; //Led vermelho está conectado no pino PD6/digital 6
//OCR0B = 255-(uint8_t)green; //Led verde está conectado no pino PD5/digital 5
//OCR2B = 255-(uint8_t)blue; //Led azul está conectado no pino PD3/digital pin 3

}
}

return 0;
}

void adc_init(void){
ADCSRA |= ((1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)); //16Mhz/128 = 125Khz como clock de referencia para o ADC
ADMUX |= (1<<REFS0); //Referencia de voltagem é 5V
ADCSRA |= (1<<ADEN); //Ligar o ADC
ADCSRA |= (1<<ADSC); //Fazer uma conversão, pois esta é que demora mais tempo
}

uint16_t read_adc(uint8_t channel){
ADMUX &= 0xF0; //Limpa o canal anteriormente lido
ADMUX |= channel; //Define o novo canal a ser lido
ADCSRA |= (1<<ADSC); //Inicia uma nova conversão
while(ADCSRA & (1<<ADSC)); //Espera que a conversão seja concluida
return ADCW; //Retorna o valor de 10 bits lido pelo ADC
}

void timer0_init(void){

DDRD = ((1<<PD5)|(1<<PD6)); //Define os pinos associados ao pwm do timer 0 como saidas
TCCR0A = ((1<<COM0A1)|(1<<WGM01)|(1<<WGM00)); //Activa o pwm no pino PD6 e coloca o timer em Fast pwm
TCCR0A |= (1<<COM0B1); //Activa o pwm no pino PD5
TCCR0B = ((1<<CS01)|(1<<CS00)); //Define prescaler para 64, timer activado
}

void timer2_init(void){

DDRD |= (1<<PD3); //Define o pino PD3/OC2B como saida
TCCR2A = ((1<<COM2B1)|(1<<WGM21)|(1<<WGM20)); ///Activa o pwm no pino PD3 e coloca o timer em Fast pwm
TCCR2B = ((1<<CS21)|(1<<CS20)); //Define prescaler para 64, timer activado
}


void timer1_init(void){

OCR1A = 15999; //Valor necessário para gerar interrupções ao ritmo de 1 a cada 1ms
TIMSK1 = (1<<OCIE1A); //Activa a interrupção quando a contagem do timer = OCR1A
TCCR1B = ((1<<WGM12)|(1<<CS10)); //Timer no modo CTC, prescaler de 1, timer activado
}

unsigned long milis(void){

cli(); //Desactivar as interrupções para que se faça uma cópia atómica da variavel
//Isto é para garantir que por exemplo estamos a ler o valor do milis, acontece outra
//interrupção e o resto do valor que vamos ler já não é utilizavel
unsigned long milis_value = milis_count; //Copia o valor de milis para uma variavel auxiliar para poder ser retornado
sei(); //Activar as interrupções de novo, se não o milis pára
return milis_value;

}

ISR(TIMER1_COMPA_vect){ //Interrupção associada ao timer 1
milis_count++; //Adiciona 1 á variavel para indicar que passo 1ms
}

void Hsv2Rgb( double *r, double *g, double *b, double h, double s, double v ) {
int i;
double f, p, q, t;

s/=100.0;
v/=255.0;

if( s == 0 ) {
// achromatic (grey)
*r = *g = *b = v;
return;
}

h /= 60;
i = (int)floor( h );
f = h - i;
p = v * ( 1.0 - s );
q = v * ( 1.0 - s * f );
t = v * ( 1.0 - s * ( 1 - f ) );

switch( i ) {
case 0:
*r = v;
*g = t;
*b = p;
break;
case 1:
*r = q;
*g = v;
*b = p;
break;
case 2:
*r = p;
*g = v;
*b = t;
break;
case 3:
*r = p;
*g = q;
*b = v;
break;
case 4:
*r = t;
*g = p;
*b = v;
break;
default:
*r = v;
*g = p;
*b = q;
break;
}
}


Como podem ver o programa é um pouco mais extenso que o normal, mas faz algo mais que piscar um led, e como está todo comentado espero que seja de facil compreensão. Como estamos a utilizar numeros em virgula flutuante(floats ou doubles para o compilador do avr são iguais), temos de dizer ao IDE para usar uma biblioteca de matemática feita especialmente para eles, pois a que standart do C gera código demasiado grande e lento, para isso precisam de ir primeiro menu Configuration Menu, desponivel na tab Project Manager, por exemplo neste pequeno exemplo se não incluir-mos a libm o código compilado tem cerca de 6.1Kbytes, enquanto que com a libm esse valor baixa para 3Kbytes sensivelmente.

(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fdesmond.imageshack.us%2FHimg269%2Fscaled.php%3Fserver%3D269%26amp%3Bfilename%3Dpwm6.png%26amp%3Bres%3Dmedium&hash=357a23de5b64458c95058ddf243badfcb7e19b78)

Escolham a secção Libraries, escolham a libm.a e pressionem Add Library e deverá aparecer a libm.a na listagem Link with these Objects:

(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fdesmond.imageshack.us%2FHimg838%2Fscaled.php%3Fserver%3D838%26amp%3Bfilename%3Dpwm7.png%26amp%3Bres%3Dmedium&hash=3d4763facc3f326cda31c9af385d59395532fe33)

Por fim, montem numa breadboard os potenciometros e o led conforme este esquema, façam o upload do código e tudo deverá funcionar sem problemas:

(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fdesmond.imageshack.us%2FHimg802%2Fscaled.php%3Fserver%3D802%26amp%3Bfilename%3Dpwm8.png%26amp%3Bres%3Dmedium&hash=0a66c6ce68a700d2a61b7227a89ada8ad6755e13)

E aqui fica um filme a mostra o funcionamento tipico:

http://youtu.be/oRybp0Bb2LE (http://youtu.be/oRybp0Bb2LE)

Qualquer duvida que tenham ou erro que encontrem, deixem um post  ;D

Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: metRo_ em 03 de Fevereiro de 2012, 00:21
Muito bom, não conhecia a libm :)
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 03 de Fevereiro de 2012, 00:26
E tens lá as bibliotecas optimizadas para usar printf's com floats, assim como os sprintf's que até é preciso adicionar mais umas coisas ao script do linker, ficam para um outro tutorial quando for preciso, para a semana é controlo de motores.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: souza_neto em 14 de Fevereiro de 2012, 05:40
Cumprimentos,

Senso parabens pelos tutoriais eles tem me ensinado muito, como te falei antes eu sou iniciante no mundo dos microcontroladores em especial os AVR, na verdade eu tenho um contato maior com os velhos e bons 8051's, pisco alguns leds com PIC's mas estou muito interessado nos micros AVR. Sempre que posso estou aqui no LR e leio bastante sobre AVR's e afins.

Até a próxima.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: pmj_pedro em 25 de Julho de 2012, 23:07
percisava de uma pequena ajuda para configurar o pwm do arduino, a uma frequenica superior a 20khz,e estava a tentar fazer com pwm com phase correction, mas nao estou a conseguir, nao percebo mt bem o que configurar, podias ver o que falta

Código: [Seleccione]
void setup(){
  TCCR0 = (1<<CS00);                            //set presscaler to 1
  TCNT0 = 0;                                    // initialize counter
  TCCR0A = (1<<COM0A1);                          // pwm non-inverted mode
  TCCR0A |= (1<<WGM00);                          // phase correct
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 25 de Julho de 2012, 23:08
Tens de meter todos os registos a zero, porque o Arduino usa os timers, e se lhe mexeres podes perder algumas funcionalidades como por exemplo o milis().
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: pmj_pedro em 25 de Julho de 2012, 23:29
eu estou a tentar fazer o codigo segundo o datasheet, e sao mt os registo para configurar, mas quais sao os registos que tu dizes para por a zero, eu tinha a intençao de por dois pwm sobre o msm timer, ja vi que é possivel, o Pin5 e Pin6, mas nao estou a perceber mt das configuraçoes
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: pmj_pedro em 26 de Julho de 2012, 18:32
Decidi usar o timer2 por enquanto pk vou precisar de usar a funçao delay()

Código: [Seleccione]
void init_myPWM(){
  TCCR2A = (1<<COM1A1) | (1<<COM2B1) | (1<<WGM1) | (1<<WGM0); //non inverting pwm & fast pwm
  TCCR2B = (1<<WGM2) | (1<<CS21);  // variable TOP & prescaler 8
  OCRA =  100; // TOP
  TCNT2 = 0;  //clean counter
  OCR2A = 0;  //OC2A
  OCR2B = 0;  //0C2B 
  DDRD = (1<<DDD3);
  DDRB = (1<<DDB3);
}

mas o arduino ide esta a dar erro no OCRA

edit: quero limitir o contador ate 100, pk fazendo as contas assim fico com a frequencia do pwm a 20khz
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 26 de Julho de 2012, 20:12
Mas assim o limite do pwm é 100, usa o timer só com prescaler e com a resolução completa é que assim nem 7 bits tens.
Dá erro porque é OCR2A e não OCRA.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: pmj_pedro em 26 de Julho de 2012, 20:50
Mas assim o limite do pwm é 100, usa o timer só com prescaler e com a resolução completa é que assim nem 7 bits tens.
Dá erro porque é OCR2A e não OCRA.

Acho que nao entendeste o que queria, eu quero que o top seja 100, assim fico com o pwm de 0 ate 99
Com isto ja vou conseguir ter um pwm com frequencia de 20khz, que é para usar no motor e ser inaudivel

foc=fclk/(N*TOP)

Mas se dizes que o top é o meu OCR2A, entao qual é o valor para fazer varias o pwm, e qual para definir o top?
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 26 de Julho de 2012, 20:54
Tens de ler a datasheet com atenção, quando defines o top, só o timer1 tem um registo separado para isso, que é o ICR, nos outros, o TOP é definido se não me engano pelo OCRxA e ficas só com variação de duty-cycle do pwm no OCRxB, e só tens um canal de pwm e não dois.
Por isso é que não uso Arduino e o seu IDE limitado, queres usar o timer1 e não podes, mas não precisas de ter uma frequencia certa, é um motor, não é audio, mete simplesmente o prescaler a 8 e não inventes com o TOP.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: pmj_pedro em 26 de Julho de 2012, 21:37
Tens de ler a datasheet com atenção, quando defines o top, só o timer1 tem um registo separado para isso, que é o ICR, nos outros, o TOP é definido se não me engano pelo OCRxA e ficas só com variação de duty-cycle do pwm no OCRxB, e só tens um canal de pwm e não dois.
Por isso é que não uso Arduino e o seu IDE limitado, queres usar o timer1 e não podes, mas não precisas de ter uma frequencia certa, é um motor, não é audio, mete simplesmente o prescaler a 8 e não inventes com o TOP.

eu acho que nao gosto é do avr, tive umas aulas de pic na univ, e o datasheet parace me mais facil de perceber
mas tb estou a tentar abandonar o arduino, conprei um atmega1284p, ja consegui por o bootoloader, mas so consigo fazer upload dos programas pelo arduino, quando tento pelo avrdude,falha
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 26 de Julho de 2012, 22:51
Se for o tipico pic16f é normal que a datasheet seja mais simples, o micro é muito mais antigo e com periféricos muito mais simplificados e com menos utilidade.
E depois estás a viver na pele o resultado de usar a simplicidade do Arduino e mal queres fazer alguma coisa diferente ficas a patinar por não estás habituado a usar o micro directamente, espero que não leves isto a mal, mas sim como um incentivo.

Que bootloader tens no atmega1284p?
Se for o bootloader do duemilanove usas uma linha de comando assim:
avrdude -p m328p -c avrisp -P com3 -b 57600 -U flash:w:N.hex

Se for bootloader Uno:
avrdude -p m328p -c arduino-P com3 -b 115200 -U flash:w:N.hex
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: dropes em 27 de Julho de 2012, 00:11
Há versões diferentes de micros que o avrdude não reconhece por a assinatura não ser igual ao do micro selecionado.
Então é possível gravar no micro ignorando a assinatura acrescentando "-F" na linha de comandos do avrdude mas na verificação já dá erro.
Neste caso tem de se editar o ficheiro "avrdude.conf" e alterar onde diz "signature=" do respectivo micro e colocar a assinatura que estava a ser mal reconhecida.

obs: convém fazer uma cópia deste ficheiro antes de qualquer alteração nele

@Senso, estás a fazer um excelente trabalho neste tutorial, parabéns  :)
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: pmj_pedro em 27 de Julho de 2012, 02:35
Há versões diferentes de micros que o avrdude não reconhece por a assinatura não ser igual ao do micro selecionado.
Então é possível gravar no micro ignorando a assinatura acrescentando "-F" na linha de comandos do avrdude mas na verificação já dá erro.
Neste caso tem de se editar o ficheiro "avrdude.conf" e alterar onde diz "signature=" do respectivo micro e colocar a assinatura que estava a ser mal reconhecida.

obs: convém fazer uma cópia deste ficheiro antes de qualquer alteração nele

@Senso, estás a fazer um excelente trabalho neste tutorial, parabéns  :)

alterei agr o bootloader e ja consigo fazer o upload com o arduino e com o avrdude se premir o botao reset, mas vou fazer esta pergunta noutro sitio para nao misturar temas
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: souza_neto em 30 de Julho de 2012, 18:11
Senso,

Excelente tutorial, estou estudando com calma, para assimilar melhor.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: Pedrock em 06 de Setembro de 2012, 13:37
Comecei a ver os tutoriais, estão muito bons. Estás de parabéns!
Acho que devias era por na primeira página os links para os outros que estão nas outras páginas. Por pouco não me passavam despercebidos. Se não me faltou algum, são estes:
Citar
http://lusorobotica.com/index.php?topic=2838.msg29175#msg29175
http://lusorobotica.com/index.php?topic=2838.msg29367#msg29367 (http://lusorobotica.com/index.php?topic=2838.msg29367#msg29367)
http://lusorobotica.com/index.php?topic=2838.msg29942#msg29942 (http://lusorobotica.com/index.php?topic=2838.msg29942#msg29942)
http://lusorobotica.com/index.php?topic=2838.msg33556#msg33556 (http://lusorobotica.com/index.php?topic=2838.msg33556#msg33556)
http://lusorobotica.com/index.php?topic=2838.msg33557#msg33557 (http://lusorobotica.com/index.php?topic=2838.msg33557#msg33557)
http://lusorobotica.com/index.php?topic=2838.msg47774#msg47774 (http://lusorobotica.com/index.php?topic=2838.msg47774#msg47774)
http://lusorobotica.com/index.php?topic=2838.msg47775#msg47775 (http://lusorobotica.com/index.php?topic=2838.msg47775#msg47775)
http://lusorobotica.com/index.php?topic=2838.msg47846#msg47846 (http://lusorobotica.com/index.php?topic=2838.msg47846#msg47846)
http://lusorobotica.com/index.php?topic=2838.msg47903#msg47903 (http://lusorobotica.com/index.php?topic=2838.msg47903#msg47903)

Estou a usar o AtmelStudio 6, tem a IDE igual ao VisualStudio, com IntelliSense e tudo! :D
Existem também uns tutoriais que ensinam a copiar as funções do Arduino para este IDE, caso alguém esteja interessado. Não estou a pensar fazer isso, porque as funções do arduino reduzem a performance. Por exemplo, o digitalWrite demora muito mais tempo do que alterar os portos diretamente. Isto para não falar do espaço ocupado na flash. Fiz o blink para o Attiny85, e no AtmelStudio ficou em 134 bytes vs. os 824 do Arduino. Mas é sempre bom ter essa opção.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: rglove em 06 de Setembro de 2012, 14:23
Também acho que os tutoriais estão muito bons! Estou na parte dos timers, está-me a custar um bocadinho, mas com tempo lá vou percebendo tudo :p

Pedrock, vi num outro tópico que mandaste vir samples da atmel, quanto tempo demoraram a chegar?

Já agora deixo aqui um link sobre timers que me ajudou bastante também:
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=50106 (http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=50106)

Cumps
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: Pedrock em 06 de Setembro de 2012, 18:10
Pedrock, vi num outro tópico que mandaste vir samples da atmel, quanto tempo demoraram a chegar?
Eu fiz o pedido no sábado, enviaram segunda, quinta estavam a entregar! (não estava em casa, então ligaram-me e entregaram na sexta). 8)
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 06 de Setembro de 2012, 18:12
Como é que conseguiste pedir samples á Atmel, já cheguei a pedir quando lançaram os XMegas e nem me responderam ao pedido lol..
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: Pedrock em 06 de Setembro de 2012, 18:18
Como é que conseguiste pedir samples á Atmel, já cheguei a pedir quando lançaram os XMegas e nem me responderam ao pedido lol..
Eles perguntam para que projeto é. Eu disse que eram para uma espécie de Arduino com 1284P e dei alguns detalhes para tornar a história mais credível. ::)
E como na National Semiconductors não aceitavam hotmail, criei um no site aeiou.pt que usei lá e foi aceite. Então usei esse email também na Atmel. Pode ser disso...
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: rglove em 06 de Setembro de 2012, 18:27
Eu também pedi samples ontem xD Tinha visto um tipo qualquer que pediu 30 atmegas328p, então decidi também pedir, mas pedi 20 atmegas328p e mais 10 atmegas1284p. E... já foram enviados eheh

Também me perguntaram para que projecto era e etc, inventei uma história qualquer, parece que colou.
Quanto ao email, como não tenho mail de escola ou universidade fiz uma pesquisa e descobri que os mails da rocketmail (yahoo) são permitidos. Criei um e pedi as samples.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 06 de Setembro de 2012, 18:29
30 atmegas, de certeza?
É que eu com email da universidade e nunca enviaram nada, ou mudaram de ideias ou não tenho sorte nenhuma lol.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: Pedrock em 06 de Setembro de 2012, 18:31
Eu também pedi samples ontem xD Tinha visto um tipo qualquer que pediu 30 atmegas328p, então decidi também pedir, mas pedi 20 atmegas328p e mais 10 atmegas1284p. E... já foram enviados eheh

Também me perguntaram para que projecto era e etc, inventei uma história qualquer, parece que colou.
Quanto ao email, como não tenho mail de escola ou universidade fiz uma pesquisa e descobri que os mails da rocketmail (yahoo) são permitidos. Criei um e pedi as samples.
Recebeste um email a dizer "Shipment Notification"?
Eu vi na net um que pediu 2 atmegas1284p e por isso pedi também. Até estava para pedir um 328p também, mas decidi não arriscar, achei que fosse demais. LOL
Agora 20+10!? Quando isso chegar diz!
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: rglove em 06 de Setembro de 2012, 18:38
Pois, se calhar tive sorte :p

A prova:
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fimg594.imageshack.us%2Fimg594%2F3972%2Fatmegas.png&hash=9511f2708dadd90f5018a3980cae2a6481b2a2fe)

Sim, já foi enviado, até já me ligaram da DHL a pedir NIF e nome completo :p
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: CBX em 06 de Setembro de 2012, 18:50
a atmel voltou a enviar samples à uns meses, mas com o pessoal a abusar não deve durar muito
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: rglove em 06 de Setembro de 2012, 21:27
Pois, eu não sabia bem a quantidade que devia pedir, como vi que um tipo qualquer tinha pedido 30 atmegas decidi pedir também... Sempre podiam ter recusado, ou então se são prejudicados por dar tantas samples porque é que não fazem como a TI e a microchip e colocam limites?
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 06 de Setembro de 2012, 22:43
Porque espera-se bom senso por parte das pessoas..
E samples tipicamente são pedidos por pessoas que trabalham em empresas que futuramente vão comprar milhares ou milhões de unidades, tu com 30 atmegas com jeito não é nos anos mais chegados que compras um micro.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: xiserre em 06 de Setembro de 2012, 23:03
e pensar que paguei mais de 20€ por 2 atmegas...
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: Pedrock em 06 de Setembro de 2012, 23:05
e pensar que paguei mais de 20€ por 2 atmegas...
Foram caros...
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: rglove em 07 de Setembro de 2012, 12:03
Já cá chegou o material  :D
20 atmegas328p e 10 atmegas1284p.
Se calhar exagerei um bocado nas quantidades, mas já não tenho mais necessidades durante muitoooo tempo xD
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: dio123 em 07 de Setembro de 2012, 22:38
Tou com um pequeno problema.

Instalei winavr e depois avr studio 6.

Acontece que apaguei o arduino atmega328 com o tutorial BitBang Mode.
Até aqui tudo ok.

Depois no avrstudio criei uma External tools asiim:

comando: C:\WinAVR\bin\avrdude.exe
Agrumento avrdude -p m328p -c avrisp -P com11 -b 57600 -F -U flash:w:"$(ProjectDir)Debug\$(ItemFileName).hex":i

erro avrdude.exe: stk500_getsync(): not in sync: resp=0x00

avrdude.exe done.  Thank you.

Cliquei no reset no inicio a meio do upload e durante todo o upload e da o mesmo erro em todas as tentativas

Onde esta o problema

Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: Pedrock em 07 de Setembro de 2012, 22:44
comando: C:\WinAVR\bin\avrdude.exe
Agrumento avrdude -p m328p -c avrisp -P com11 -b 57600 -F -U flash:w:"$(ProjectDir)Debug\$(ItemFileName).hex":i
Deverá ser algo como:
Comando: C:\WinAVR\bin\avrdude.exe
Argumento: -p atmega328p -c arduino -P COM11 -b 115200 -D -U flash:w:"$(ProjectDir)Debug\$(ItemFileName).hex":i

Se não der, vê este tutorial:
Tutorial Arduino + Atmel Studio 6 (Parte 1) (http://www.youtube.com/watch?v=F74khQnh9pM#ws)
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 07 de Setembro de 2012, 23:03
O que é que apagaste efectivamente?
Se foi o bootloader, ele é preciso, e o AvrStudio6 já tem integrado o compilador não precisas de instalar o WinAvr.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: Pedrock em 07 de Setembro de 2012, 23:34
O que é que apagaste efectivamente?
Se foi o bootloader, ele é preciso, e o AvrStudio6 já tem integrado o compilador não precisas de instalar o WinAvr.
Pelo que vi é preciso para fazer o upload, porque o AtmelStudio6 só é compatível com os programadores da Atmel.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 07 de Setembro de 2012, 23:36
Nada disso, o avrdude funciona á parte do Studio6, era só não mexeres no Arduino e usar o bootloader do mesmo, agora tens de o voltar a programar, porque o Arduino não tem programador nenhum.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: dio123 em 08 de Setembro de 2012, 14:19
Pedrock - A primeira vez foi assim que fiz, mas não usei as bibliotecas do arduino, segui o tutorial até a parte do upload arduino. Depois usei o codigo da pagina inicial.  e funcionou.

Senso - Ai está a minha duvida se é ou não necessário bootloader.

Fiz  a seguinte experiencia com o programa no tutorial BitBang Mode daqui do forum.

No Avr studio fiz o projecto o led pisca-pisca com o codigo da pagina inicial e compilei,

no avrdude-GUI , apaguei o chip - erase chip depois verifiquei se os fuses estavam iguais, sim estão.
Depois no flash em vez de por o bootloader, como é suposto, pus logo hex  do pisca pisca. O que está na pasta do projecto avr studio

Gravei e depois desliguei e liguei o cabo usb e o arduino fica com o led a piscar.

Agora estou na duvida.

Se faz bem ao atmega ou se  vou acabar por estragar o atmega.
Pois sem o bootloader poupa-se 5,40  KB de memoria.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 08 de Setembro de 2012, 14:52
O bootloader tem no máximo 2Kb, isto no atmega328p.
Não percebo é como é que estás a programar o atmega.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: dio123 em 08 de Setembro de 2012, 15:18
Estou a programar como o video Pedrock postou.

Só que exprimentei seguir este post: http://lusorobotica.com/index.php?topic=427.msg42035#msg42035 (http://lusorobotica.com/index.php?topic=427.msg42035#msg42035), mas em vez de usar o bootloader usar logo o hex gerado pelo avr studio do programa led pisca pisca.

E quero saber se há problemas em usar este metodo pois não usei nenhum bootloader.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: CBX em 08 de Setembro de 2012, 15:22
não há problema nenhum , o bootloader simplesmente permite que a mesma memória flash seja programada sem recurso a um programador, ter ou não ter apenas depende da aplicação, não afeta o funcionamento ou tempo de vida do microcontrolador
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: rglove em 13 de Setembro de 2012, 22:07
Boas, se calhar a minha dúvida vai ser estúpida, mas está a fazer-me um bocado de confusão...

Estive a fazer uns testes com pwm com um led, mas quando supostamente está a 0% duty cycle o led acende, claro que com menos intensidade, mas acende. Não era suposto a tensão no pino ser 0?

Cumps


EDIT: Coloquei o bit COM0A0 a 1 para experimentar no modo inverso e ao escrever 255 no registo OCR0A de facto o led apaga-se completamente. Alguém sabe o porquê de não acontecer no modo normal?
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: dio123 em 18 de Setembro de 2012, 19:58
Estou com graves problemas.

Então fui http://www.jump.to/fleury (http://www.jump.to/fleury) baixei o LCD library for HD44870 based LCD's.
copiei o lcd.c lcd.h para a pasta do projecto
Depois abrir o atmel  studio 6 criei um projecto escolhi atmega328p.
No Solution Explorer adicionei os 2 ficheiros lcd.h lcd.c.

Coloco o codigo  e ao fazer debug dá-me 9 erros:
'PORTA' was not declared in this scope

Alguém sabe onde está o problema.
Testei vários codigos mas dá-me sempre o mesmo erro.
Citar
#define F_CPU 16000000

#include <avr/io.h>
#include <util/delay.h>
#include "lcd.h"

int main(void)
{
   lcd_init(LCD_DISP_ON);                /* initialize display, cursor off */
   while (1)                         /* loop forever */
   {
      lcd_clrscr();                      /* clear the screen*/
      lcd_puts("test program");             /* displays the string on lcd*/
      _delay_ms(50);
   }
}

Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 18 de Setembro de 2012, 20:23
Tens de editar o lcd.h para que tenha nas definições os pinos que estás a utilizar e é normal que dê esse erro, o atmega328p não tem nenhum PORTA.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: dio123 em 19 de Setembro de 2012, 16:59
devia ser algum conflito com a biblioteca lcd, pois alterava e acusava PORTA há mesma.
Resolvido e o lcd já funciona.

Agora queria fazer reproduzir o exemplo do arduino liquid crystal - hello world e usei um codigo que apareceu aqui, mas nao está a funcionar. Ao fim de 2 segundos aparece 0.


Citar
#define F_CPU 16000000UL
   #include <avr/interrupt.h>
#include <avr/io.h>
#include <util/delay.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "lcd.h"
#include "lcd.c"


void timer0_init(void);              //Function prototype

volatile unsigned long milis = 0;   //This is our shared volatile variable that will be used as the milis count
unsigned long now = 0;

//Global values
uint16_t adc_data;


//
// Main
//

int main(void)
{   
   int i;
   char buffer [33];
   timer0_init();
    lcd_init(LCD_DISP_ON);
   lcd_clrscr();
_delay_ms(2000);
   i == milis;
    itoa (i,buffer,10);
   lcd_puts(buffer);
}

   
   void timer0_init(void){

      TCCR0A = (1<<WGM01);             //Timer in CTC mode
      TCCR0B = ((1<<CS01)|(1<<CS00));    //1:64 prescaler
      OCR0A = 249;                     //Value to have an compare at every 1ms
      TIMSK0 = (1<<OCIE0A);             //Enable timer interrupts
      sei();                         //Enable global interrupts
   }

   ISR(TIMER0_COMPA_vect){

      milis++;    //Increase milis count by one millisecond
   }
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: dio123 em 20 de Dezembro de 2012, 20:16
Boa noite,

Estou aqui com um problema na lcd library de Peter Fleury, e  nem estou a ver o que se está a passar. E as uns tempos a tras fiz umas brincadeiras.

Importei os 2 ficheiros lcd.c  e o lcd.h para o avr studio.
Abri  o lcd.h  e fiz as seguintes alterações.
Quando ligo o atmega não mostra nada, mas se colocar o exemplo hello world do arduino já aparece, apenas tenho de por RW em ground.
O que estou a fazer mal? 

Citar
#define XTAL 16000000
#define LCD_PORT         PORTD       
#define LCD_DATA0_PORT   LCD_PORT   
#define LCD_DATA1_PORT   LCD_PORT   
#define LCD_DATA2_PORT   LCD_PORT   
#define LCD_DATA3_PORT   LCD_PORT 
#define LCD_DATA0_PIN    0         
#define LCD_DATA1_PIN    1   
#define LCD_DATA2_PIN    2     
#define LCD_DATA3_PIN    3     
#define LCD_RS_PORT      LCD_PORT   
#define LCD_RS_PIN       5           
#define LCD_RW_PORT      LCD_PORT   
#define LCD_RW_PIN       6     
#define LCD_E_PORT       LCD_PORT 
#define LCD_E_PIN        4     
 
O codigo para o atmega é:
Citar
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include "lcd.h"
#include "lcd.c"

int main(void)
{
   lcd_init(LCD_DISP_ON);                /* initialize display, cursor off */
   lcd_clrscr();                      /* clear the screen*/
   lcd_puts("test program");
}


Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 20 de Dezembro de 2012, 20:24
Não se faz include de um .c
Isso não está a dar erros?
Estás a dizer ao IDE onde é que tens a biblioteca lcd?
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: dio123 em 20 de Dezembro de 2012, 20:50
Se tirar #include "lcd.c" tenho  3 erros.
[quote undefined reference to `lcd_init(unsigned char)'
 undefined reference to `lcd_clrscr()'
 undefined reference to `lcd_puts(char const*)'[/quote]


Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 20 de Dezembro de 2012, 20:50
Mas não se faz includes de .c's!
Isso é o IDE/compilador que não sabe onde tens os ficheiros.

Vais a project options, em include directories, adicionas a pasta onde tens isso e em principio funciona.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: dio123 em 20 de Dezembro de 2012, 21:33
Mesmo assim continua a não aparecer nada, no lcd.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 20 de Dezembro de 2012, 21:36
E isso está a compilar sem warnings?
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: dio123 em 20 de Dezembro de 2012, 21:46
sim o projecto está a  compilar sem warmings. 
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: dio123 em 01 de Janeiro de 2013, 13:10
Bom dia,
ainda não há muito tempo consegui fazer o " hello world " do arduino no avrstudio,  mas hoje fui tentar fazer novamente e nao consigo fazer.  Ainda por cima perdi o ficheiro onde tinha feito.

Quem puder dar uma vista de olhos agradeço.

codigo: http://pastebin.com/aB3FKr1n (http://pastebin.com/aB3FKr1n)
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: dio123 em 02 de Janeiro de 2013, 00:04
já consegui fazer milis , ler portas analogicas, ligar lcd , porta serial e juntar isto tudo.
Devagarinho lá se vai aprendendo.

Tenho uma pequena questão mas pelo que apanhei no google nao funcionou.

no arduino tenho   tmpPass += keypressed; que acrescenta ao tmpPass  as teclas pressionadas e que da a senha colocada.

Agora no avr studio não é assim, e nao consigo encontrar como se faz, só aparece mas para o visual studio?


Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 02 de Janeiro de 2013, 00:12
Isso é a usar strings?
Usas um array, que é o que isso faz por trás das cenas.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: dio123 em 03 de Janeiro de 2013, 13:38
o tmpPass e password estam defenidos como string 
keypressed está defenido como int
apanho os 4 numeros   tmpPass += keypressed;
depois no final verifico assim    if (tmpPass == password && count == 4 ){ lcd.println("autorizado")}

é que o programa do alarme não cabe no atmega8, por pouco, mas se fizer no avrstudio já deve caber.
e guardava o   atmega328  que faz sempre falta.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: c3dr1c em 18 de Janeiro de 2013, 21:36
Senso, consigo gravar programas com o arduino mas nao consigo com o avrdude. Da o erro stk500_getsync<>:not in sync resp: 0x00

Quando carrego no botao de reset? Ja tentei mal faço enter para carregar o programa, a meio, etc...

Eu ja consegui fazer isto, mas nao sei porque agora nao da...

Configurei o baud rate para 57600 no windows. Tenho é um atmega328 e nao 328p...

Cumps
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 18 de Janeiro de 2013, 21:53
Cola aqui o comando que estás a usar, porque se usares como nome do programador arduino ele faz auto-reset.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: c3dr1c em 18 de Janeiro de 2013, 22:00
Faz reset mas nao da na mesma :/

(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fi48.tinypic.com%2F8z0j7b.png&hash=a19c7f319935e11b5b0616044b5b3c8903ccb79b)

Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 18 de Janeiro de 2013, 22:13
Tens o auto-reset do arduino desligado?
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: c3dr1c em 18 de Janeiro de 2013, 22:20
O condensador ligado ao pino 1 e ao DTR? Tá ligado, ele faz reset que eu vejo o led 13 a piscar.

Eu quando conseguia fazer upload usava um bootloader que ao fazer auto reset o led do sistema piscava varias vezes . Agora com este bootloader pisca so 3 vezes. Pode ser do bootloader?

Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 18 de Janeiro de 2013, 22:22
Que bootloader é?
Se for mais recente o baud-rate é 115200 e não 57600.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: c3dr1c em 18 de Janeiro de 2013, 22:35
Boa senso era mesmo isso  ;D Muito obrigado, agora vou começar o teu tutorial.

Cumps
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: c3dr1c em 19 de Janeiro de 2013, 19:00
Boas senso.
Estou aqui com um problema basico. Como posso ver o printf do codigo?
Eu compilei e depois fiz build and run (penso que seja isso), mas acontece isto:
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fi46.tinypic.com%2Fj6l6wk.png&hash=e53d13674cdddff6c727513806bbab0b15a182f5)
http://i46.tinypic.com/2lly5b7.jpg (http://i46.tinypic.com/2lly5b7.jpg)

Ja agora encontrei um tutorial teu, do Cynary e do Njay na net muito bom!!!

Este teu tutorial bem como o que encontrei na net deviam estar fixos no inicio do subforum "Arduino/AVR" (bem como outros importantes para a malta poder estudar). Ou ter um topico fixo no inicio do subforum com todos os links dos tutoriais. Mas isto é so uma sugestão minha para o forum.
Não tinha reparado no subforum "Tutoriais"  :P

Cumps

Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: vdr em 20 de Janeiro de 2013, 12:49
Tens de escolher para onde queres que o printf mande o que queres imprimir, podes usar a USART.
Tens aqui um exemplo, http://www.appelsiini.net/2011/simple-usart-with-avr-libc (http://www.appelsiini.net/2011/simple-usart-with-avr-libc)
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 20 de Janeiro de 2013, 12:59
Eu nunca uso o printf, uso o sprintf, e depois faço o uartPuts para imprimir o buffer.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: c3dr1c em 20 de Janeiro de 2013, 15:42
Vou ver isso.
O que é o uartPuts? Isso faz-se dentro do avrstudio?

Thanks
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: vdr em 20 de Janeiro de 2013, 16:49
Vou ver isso.
O que é o uartPuts? Isso faz-se dentro do avrstudio?

Thanks

Penso que o uartPuts que o senso referiu era a função que ele colocou num dos primeiros tutoriais neste tópico "USART_send(unsigned char data)"

Basicamente, fazes a formatação através do sprintf e colocas a string num array de chars, depois é so ir mandando para a USART char a char.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: DLM em 06 de Março de 2013, 10:43
Se eu estiver a usar o Arduino como ISP, qual opção seleciono no menu "Debug plataform"?
O Atmega em questão é um atmega8A.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: migvalsan em 25 de Setembro de 2014, 14:31
Boas :D

tenho estado a seguir este topico e tambem eu quero largar a IDE do arduino. estou na parte do USART fiz aquele desafio do senso e correu bem, agora o que queria tentar fazer era em vez de ligar ou desligar o led com '1' ou '0', queria fazer o mesmo efeito mas por exemplo com palavras do genero ligava o led quando eu enviasse  a palavra 'Ligar' e desligava quando enviava a palavra 'desligar'.
como posso fazer isto?

cumps

Miguel
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 25 de Setembro de 2014, 23:38
Isso é manipulação (básica) de strings, idealmente devias passar a usar a UART por interrupção, depois vais metendo os caracteres que recebes num buffer, remover os /n e /r que podem por lá andar e fazer um strcmp entre a palavra ligar/desligar e a string que recebeste pela UART.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: migvalsan em 26 de Setembro de 2014, 20:58
Isso é manipulação (básica) de strings, idealmente devias passar a usar a UART por interrupção, depois vais metendo os caracteres que recebes num buffer, remover os /n e /r que podem por lá andar e fazer um strcmp entre a palavra ligar/desligar e a string que recebeste pela UART.

ok percebi essa parte mas antes disso tenho de criar uma função para chamar cada vez que quero receber uma string, andei a pesquisar na internet e achei esta função:

Código: [Seleccione]
char * UART_GetString(char *st)

// string is returned when <enter> key is pressed
{
char c;
int count = 0;
while ((c = USART_receive()) != '\r')
{
//UART_Write(c); // echo char back to console
st[count++] = c; // add char to string
}
st[count]='\0'; // add NULL termination
USART_send('c');
return st;
}

eu entendo o que ela faz, não sei é como interagir com ela, por exemplo quero chamar esta função e guardar a string numa variavel na funão que a chamou.
como é que posso fazer isso?

Cumps
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: jm_araujo em 26 de Setembro de 2014, 21:47
Crias um array de char (em C a única maneira standard de definir uma string) e passas como parâmetro para essa função.
Se queres largar a IDE do Arduino precisas de aprender C no mínimo. Tens muita informação e não é difícil encontrar o Kernighan&Ritchie ("The C Programming Language"), a bíblia do C escrita pelos autores da linguagem.

Essa função tem uma falha grave, culpada da maior parte dos hacks, não está protegida para um buffer overrrun, se tiveres reservado 10 bytes de memória  para a string, e enviares mais para a UART vai continuar a escrever a memória com efeitos nefastos. Devia tem um parâmetro extra com o numero máximo de caracteres a ler, e dentro uma condição para limitar o count a esse valor.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: artur36 em 26 de Setembro de 2014, 21:49
Basicamente tens de chamar a função "para dentro de uma variável".
Qualquer coisa do género:

Variável= função();

Na função que deste em cima a última linha vai retornar o valor de st para a variável que definiste ao chamar a função.

Ou então usas na função uma variável que declaras-te como geral e depois no restante código podes testar directamente essa variável (funciona mas não é o correcto/ideal).

Isto é C geral, não sei se é exactamente assim que o avr-gcc funciona, mas deve ser minimamente parecido.

Edit: Parece que dei calinada, fica rasurado para não induzir em erro, sigam as indicaçãoes de quem tem mais "calo".
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: jm_araujo em 26 de Setembro de 2014, 21:55
Basicamente tens de chamar a função "para dentro de uma variável".
Qualquer coisa do género:

Variável= função();

Na função que deste em cima a última linha vai retornar o valor de st para a variável que definiste ao chamar a função.

Ou então usas na função uma variável que declaras-te como geral e depois no restante código podes testar directamente essa variável (funciona mas não é o correcto/ideal).

Isto é C geral, não sei se é exactamente assim que o avr-gcc funciona, mas deve ser minimamente parecido.
Errado, isso não vai funcionar porque não estás a passar o apontador da string como parâmetro para a função.

Com o código mostrado precisas de ter um array de chars definido:
Código: [Seleccione]
char s[10];E para atribuir o valor basta usar o nome do array como parâmetro:
Código: [Seleccione]
UART_GetString(s);
Trabalhar com apontadores é um bocado diferente, estudem sobre o assunto.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: artur36 em 26 de Setembro de 2014, 21:57



Se queres largar a IDE do Arduino precisas de aprender C no mínimo. Tens muita informação e não é difícil encontrar o Kernighan&Ritchie ("The C Programming Language"), a bíblia do C escrita pelos autores da linguagem.


Se te safares em inglês existe  um livro chamado beginning C for Arduino. Se quiseres em português tens linguagem C de Luís damas, ambos disponíveis nas "lojas" do costume.

Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: migvalsan em 26 de Setembro de 2014, 21:59
Basicamente tens de chamar a função "para dentro de uma variável".
Qualquer coisa do género:

Variável= função();

Na função que deste em cima a última linha vai retornar o valor de st para a variável que definiste ao chamar a função.

Ou então usas na função uma variável que declaras-te como geral e depois no restante código podes testar directamente essa variável (funciona mas não é o correcto/ideal).

Isto é C geral, não sei se é exactamente assim que o avr-gcc funciona, mas deve ser minimamente parecido.
Errado, isso não vai funcionar porque não estás a passar o apontador da string como parâmetro para a função.

Com o código mostrado precisas de ter um array de chars definido:
Código: [Seleccione]
char s[10];E para atribuir o valor basta usar o nome do array como parâmetro:
Código: [Seleccione]
UART_GetString(s);
Trabalhar com apontadores é um bocado diferente, estudem sobre o assunto.

Código: [Seleccione]
char * UART_GetString(char *st)

// string is returned when <enter> key is pressed
{
 char c;
 byte count=0;
 while ((count<80) && ((c = UART_Read()) != '\r'))
 {
 UART_Write(c); // echo char back to console
 st[count++] = c; // add char to string
 }
 st[count]='\0'; // add NULL termination
 return st;
}

neste caso o maximo da string vai ser 80 caracteres certo?
cumps
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: migvalsan em 26 de Setembro de 2014, 22:01



Se queres largar a IDE do Arduino precisas de aprender C no mínimo. Tens muita informação e não é difícil encontrar o Kernighan&Ritchie ("The C Programming Language"), a bíblia do C escrita pelos autores da linguagem.


Se te safares em inglês existe  um livro chamado beginning C for Arduino. Se quiseres em português tens linguagem C de Luís damas, ambos disponíveis nas "lojas" do costume.

sim até me safo em inglês :D
vou pesquisar isso obrigado ;)
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: artur36 em 26 de Setembro de 2014, 22:02
Jm-Araújo peço desculpa se disse alguma asneira, o meu C é muito básico e orientado para PIC.
Mas era a maneira que eu ia usar se fosse para um int por exemplo (arrays nunca trabalhei). Penso que a solução que apresentas-te é para enviar uma string e ele quer receber, acho eu.


Edit: rasurei as dicas que dei, aparentemente estão erradas e não quero induzir em erro ninguém.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: jm_araujo em 26 de Setembro de 2014, 22:19
Código: [Seleccione]
char * UART_GetString(char *st)

// string is returned when <enter> key is pressed
{
 char c;
 byte count=0;
 while ((count<80) && ((c = UART_Read()) != '\r'))
 {
 UART_Write(c); // echo char back to console
 st[count++] = c; // add char to string
 }
 st[count]='\0'; // add NULL termination
 return st;
}

neste caso o maximo da string vai ser 80 caracteres certo?
cumps
Nesse cado precisas de passar um array de 81 caracteres, porque a contagem começa a count=0, o loop sai com count=80 e essa posição é escrita com o NULL.

Jm-Araújo peço desculpa se disse alguma asneira, o meu C é muito básico e orientado para PIC.
Mas era a maneira que eu ia usar se fosse para um int por exemplo (arrays nunca trabalhei). Penso que a solução que apresentas-te é para enviar uma string e ele quer receber, acho eu.

Não, está certo como mostrei. Para iniciantes a cena toda de arrays e apontadores é um bocado confusa, e não tenho muito jeito para professor.
Mete o seguinte exemplo neste site: http://www.compileonline.com/compile_c_online.php (http://www.compileonline.com/compile_c_online.php)

Código: [Seleccione]
#include <stdio.h>

char* teste(char* str)
{
    int i;
    for (i=0;i<5;i++)
        str[i]='a'+i;
    str[i]='\0';
    return str;
}


main()
{
    char s[16]="Lusorobotica";
    printf("Valor Inicial: %s\n",s);

    teste(s);
    printf("Valor Final: %s\n",s);
}
(feito à pressa, podia estar mais bonito)

Como podes ver chamo a função "teste" da mesma maneira que disse atrás, e a string é alterada. O que estás a passar como parâmetro é um apontador (tem o asterisco) para a posição de memória onde está o array. Em C não consegues alterar uma variável passada como parâmetro para uma função, mas se for um apontador, nada impede que alteres a memória para onde aponta. Como disse, é um pouco confuso e os livros fazem melhor o trabalho que eu ;)

Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: artur36 em 26 de Setembro de 2014, 22:38
Como podes ver chamo a função "teste" da mesma maneira que disse atrás, e a string é alterada. O que estás a passar como parâmetro é um apontador (tem o asterisco) para a posição de memória onde está o array. Em C não consegues alterar uma variável passada como parâmetro para uma função, mas se for um apontador, nada impede que alteres a memória para onde aponta. Como disse, é um pouco confuso e os livros fazem melhor o trabalho que eu ;)

Agora já liguei o PC (estava a ver através do tapatalk).

Vi o teu exemplo e percebi (gostei da abordagem do a+i para avançar para o caracter seguinte, nunca me tinha lembrado de tal coisa).
No entanto pelo que eu percebi da pergunta original o  migvalsan quer receber caracteres da uart, no exemplo de código que ele apresentou é lida a uart e gravado na string ST que é retornada no fim da função certo?

Se ele chamar a função assim:

Código: [Seleccione]
char valor[];
valor[]= UART_GetString();

Não vai ficar com o valor de ST na string valor?

P.S.: É provável que esteja errado, mas isto era o que eu faria, também quero aprender a forma correcta.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: jm_araujo em 26 de Setembro de 2014, 22:52
Código: [Seleccione]
char valor[];
valor[]= UART_GetString();
Não vai ficar com o valor de ST na string valor?
Nope. A função retorna um apontador para st, que é o mesmo do parâmetro inicial. Essa atribuição dá erro, não é possível manipular arrays dessa maneira em C. Só podes atribuir valor aos seus membros (tens de ter número entre os parênteses retos) e não alterar o array (que funciona como um apontador estático).

Tem de ser:
Código: [Seleccione]
char valor[16]; //ou o tamanho do buffer que quiser
UART_GetString(valor);

Usa a meu exemplo para testares e aprenderes. Faz de conta que a função "teste" é a UART_GetString, fazem a mesma coisa: alteram uma string.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: migvalsan em 26 de Setembro de 2014, 23:03
Código: [Seleccione]
char * UART_GetString(char *st)

// string is returned when <enter> key is pressed
{
 char c;
 byte count=0;
 while ((count<80) && ((c = UART_Read()) != '\r'))
 {
 UART_Write(c); // echo char back to console
 st[count++] = c; // add char to string
 }
 st[count]='\0'; // add NULL termination
 return st;
}

neste caso o maximo da string vai ser 80 caracteres certo?
cumps
Nesse cado precisas de passar um array de 81 caracteres, porque a contagem começa a count=0, o loop sai com count=80 e essa posição é escrita com o NULL.

Jm-Araújo peço desculpa se disse alguma asneira, o meu C é muito básico e orientado para PIC.
Mas era a maneira que eu ia usar se fosse para um int por exemplo (arrays nunca trabalhei). Penso que a solução que apresentas-te é para enviar uma string e ele quer receber, acho eu.

Não, está certo como mostrei. Para iniciantes a cena toda de arrays e apontadores é um bocado confusa, e não tenho muito jeito para professor.
Mete o seguinte exemplo neste site: http://www.compileonline.com/compile_c_online.php (http://www.compileonline.com/compile_c_online.php)

Código: [Seleccione]
#include <stdio.h>

char* teste(char* str)
{
    int i;
    for (i=0;i<5;i++)
        str[i]='a'+i;
    str[i]='\0';
    return str;
}


main()
{
    char s[16]="Lusorobotica";
    printf("Valor Inicial: %s\n",s);

    teste(s);
    printf("Valor Final: %s\n",s);
}
(feito à pressa, podia estar mais bonito)

Como podes ver chamo a função "teste" da mesma maneira que disse atrás, e a string é alterada. O que estás a passar como parâmetro é um apontador (tem o asterisco) para a posição de memória onde está o array. Em C não consegues alterar uma variável passada como parâmetro para uma função, mas se for um apontador, nada impede que alteres a memória para onde aponta. Como disse, é um pouco confuso e os livros fazem melhor o trabalho que eu ;)


fixe, testei o teu exemplo e já percebi e já consegui por o meu codigo a funcionar.

mas como está sou obrigado a enviar uma string com 81 caracteres se não não sai do ciclo while... como é que posso por o codigo de maneira a poder enviar uma string do tamanho que entender e o programa guardar apenas o que envio na string?

cumps
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: jm_araujo em 26 de Setembro de 2014, 23:34
Nesse código a string acaba com um enter (o " != '\r' ")
Podes definir outro carater como fim da string a ler, como por exemplo um espaço ou ponto e virgula...
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: migvalsan em 27 de Setembro de 2014, 00:13
Mas como está caso eu não introduza 81 caracteres não sai do ciclo while..
Mas já tentei tirar a parte "(count<80)&& "  e não funciona também..
O que queria era Não ter de ser obrigado a introduzir um certo número de caracteres.

Cumprimentos
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: jm_araujo em 27 de Setembro de 2014, 00:56
Mostra aí o teu código completo.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: migvalsan em 27 de Setembro de 2014, 01:10
Mostra aí o teu código completo.

Código: [Seleccione]
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>

#define BAUDRATE 9600
#define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1)

//declaração das nossas funções
void USART_init(void);
unsigned char USART_receive(void);
void USART_send( unsigned char data);
void USART_putstring(char* StringPtr);
char * UART_GetString(char *st);

char String[]="Olá mundo!!!"; //String[] que dizer que é um array, mas ao colocar-mos o texto entre "" indicamos ao compilador que é uma string e ele coloca automáticamente o terminador null e temos assim uma string de texto usavel

int main(void){
USART_init();  //Inicializar a usart
    char strr[200];
while(1){  //Loop infinito



USART_putstring(String); //Passamos a nossa string á função que a escreve via serial

UART_GetString(strr);

USART_putstring(strr);

_delay_ms(1000);  //E a cada 5s re-enviamos o texto
}

return 0;
}

void USART_init(void){

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

unsigned char USART_receive(void){

while(!(UCSR0A & (1<<RXC0)));
return UDR0;

}

void USART_send( unsigned char data){

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

}

void USART_putstring(char* StringPtr){

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

}

char * UART_GetString(char *st)

// string is returned when <enter> key is pressed
{
char c;
int count = 0;
while ((count<80) && ((c = USART_receive()) != '\r'))
{
//UART_Write(c); // echo char back to console
st[count++] = c; // add char to string
}
st[count]='\0'; // add NULL termination

return st;
}
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: migvalsan em 27 de Setembro de 2014, 02:16
já descobri qual era o problema... o terminal que estava a usar não enviava o "enter" por isso é que a função ficava presa no while  >:(
bem, obrigado senso jm_araujo e artur36 pela ajuda :)

ja agora podem me indicar um bom terminal?

cumprimentos

Miguel
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: jm_araujo em 27 de Setembro de 2014, 09:42
Eu gosto do Bray Terminal: https://sites.google.com/site/terminalbpp/
Mas a última versão no site tem alguns problemas se enviares muita coisa de rajada e tiver a vista Hex, dec e/ou bin ligada.
A última versão "escondida" já se porta melhorzinho: https://sites.google.com/site/terminalbpp/Terminal20140703.zip?attredirects=0


Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: Kristey em 20 de Outubro de 2015, 16:30
Agora que voltei aqui, vi que algumas das imagens estão down.
Queres ajuda para voltar a por tudo direitinho?
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 20 de Outubro de 2015, 16:59
Devem é estar todas down, já vi no primeiro post.
E agora está ultra desactualizado, que as imagens eram do AvrStudio 4.14 ou algo assim, já vai no 7 e quando foi o salto para o 5 passou a ser com Visual Studio que não tem nada a ver a nivel de UI.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: dio123 em 21 de Outubro de 2015, 10:47
a única coisa que sei é imageshack.us de uma volta ao servidor que uma grande parte das imagens foram ao ar, deixando pela net montes de tutoriais sem imagens.

Como o meu nivel é super básico ainda agora quando instalei windows 10 coloquei o avr studio 4 com sp3 e é só acrescentar um ficheiro e esta a bombar.  Porque as novas versões são muito mais lentas ao abrir.

Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 21 de Outubro de 2015, 11:02
De momento estou a usar Win8.1 Pro e o AvrStudio7 e com SSD abre em 2-3 segundos.
Eu tenho as imagens todas no pc posso fazer re-upload para outro host qualquer.

Com esta conversa fui espreitar o pó do blog, tem quase 300 mil views, e ainda faz média de 300 views por dia, abandonado á anos  :-[
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: Kristey em 22 de Outubro de 2015, 09:41
Senso actualiza isto :)
E continua a thread
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 22 de Outubro de 2015, 11:15
Anda tudo a brincar com ARM's agora, ARM é que é, é bom, é barato, tens 55 mil periféricos num chip de 50 centimos, os Atmega subiram agora de preço por causa da compra da Atmel lel, e somos quê 20 users deste fórum?
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: MAntunes em 25 de Outubro de 2015, 13:21
Anda tudo a brincar com ARM's agora, ARM é que é, é bom, é barato, tens 55 mil periféricos num chip de 50 centimos, os Atmega subiram agora de preço por causa da compra da Atmel lel, e somos quê 20 users deste fórum?

Eu sei que esta pergunta merecia um tópico só desta, mas continua a valer a pena aprender as arquiteturas mais antigas, ou vale mais ir direito para ARM??
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: jm_araujo em 25 de Outubro de 2015, 13:52
Eu acho que vale a pena. Nos micros mais simples (atmega, pic16, etc) os periféricos são mais acessíveis e melhor documentados, e entender o seu funcionamento acho que é um boa iniciação para os mais complexos que encontras em ARMs e afins. Senão ficas sempre agarrado ao que as bibliotecas  te disponibilizam (e por vezes com erros) e não aproveitas em pleno a plataforma.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: dio123 em 09 de Setembro de 2016, 22:12
Hoje fiquei desanimado. Comprei um SSD da samsung instalei de raiz windows 10, e depois quando vou abrir o  avr studio 7 leva-me 30s.  Enquanto os outros programas abrem num tiro avr demora o mesmo que hd normal. Tá bem que o bixo é intel q9400 4gb ram, mas ainda é maquina.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 11 de Setembro de 2016, 01:03
10 segundos num BX200 de 480GB.
Instala os drivers do Chipset da tua board.
Confirma que tens o SATA em modo AHCI, SSD's não gostam de trabalhar em modo IDE que limita e muito os IOPS..
Confirma no gestor de tarefas que estás a usar o driver ahci da Intel e não o genérico do Windows.
Nos primeiros dias housekeeping do Windows roi até um SSD decente.

Com um Qxxxx não tens Intel ME para actualizar á mão, mas se calhar update á BIOS para teres uma oROM SATA menos antiga tambem não se perdia nada..
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: senso em 11 de Setembro de 2016, 01:05
Also:
Continuo a não ser fã de ARM's lol..
Tenho um projectinho que já se manda para as 30k linhas de código em atmega256rfr2's e até ver zero falta de poder de processamento, só depende se sabes o que estás a fazer.
Título: Re: Introdução ao avr-gcc usando o AvrStudio [Mais timers/mood light]
Enviado por: samc em 11 de Setembro de 2016, 03:51
Hoje fiquei desanimado. Comprei um SSD da samsung instalei de raiz windows 10, e depois quando vou abrir o  avr studio 7 leva-me 30s.  Enquanto os outros programas abrem num tiro avr demora o mesmo que hd normal. Tá bem que o bixo é intel q9400 4gb ram, mas ainda é maquina.
Eu uso um evo pro da Samsung e a ferramenta para Windows está até muito boa, todas as preocupações com settings que podem não ajudar na performance da drive são lá assinaladas. Para quem tem um ssd deles o Samsung magician é uma óptima ferramenta!