Resposta inesperada do Atmega16 sobre o UART

8

Resposta inesperada do Atmega16 sobre o UART

Breve resumo do problema

Eu exibi um Atmega16 com um código que deveria resultar no envio do Atmega16 para qualquer caractere que eu enviar através de um terminal. Eu recebo uma resposta, mas raramente é o personagem que enviei. Consigo ver a saída correta alterando a taxa de transmissão, mas não entendo por que a taxa de transmissão correta funciona.

Mais detalhes

Estou tentando aprender mais sobre programação de firmware no meu tempo livre, porque gosto bastante. Até agora, na programação de firmware que fiz na uni, recebemos arquivos de código de esqueleto que fazem grande parte da interface periférica e configurados para nós, mas eu mesmo gostaria de aprender isso. Eu tenho algumas perguntas sobre o que estou fazendo aqui espalhadas por todo o post, mas vou descrevê-las no final. Se você perceber algum mal-entendido ou possíveis lacunas em meu conhecimento, eu apreciaria muito qualquer contribuição que você possa ter.

O código

O código que atualizei no meu Atmega16 está quase na linha do tutorial 'Usando o USART no AVR-GCC' encontrado nesta página . Tudo o que eu adicionei é o #define para F_CPU. O código original não tinha um #define para F_CPU, portanto meu código não seria compilado no AtmelStudio 7. Alguém poderia explicar por que o autor não teria definido F_CPU em seu arquivo original? Suponho que eles possam estar usando alguma outra ferramenta ou compilador que não seja o Atmel Studio 7, mas não posso dizer com certeza.

#include <avr/io.h>
#define F_CPU 7372800 //this was chosen because the tutorial states this is the frequency we want to operate at
#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((( F_CPU / 16) + ( USART_BAUDRATE / 2)) / ( USART_BAUDRATE )) - 1)

int main ( void )
{
    char ReceivedByte ;
    UCSRB = (1 << RXEN ) | (1 << TXEN ); // Turn on the transmission and reception circuitry
    UCSRC = (1 << URSEL ) | (1 << UCSZ0 ) | (1 << UCSZ1 ); // Use 8- bit character sizes
    UBRRH = ( BAUD_PRESCALE >> 8); // Load upper 8- bits of the baud rate value into the high byte of the UBRR register
    UBRRL = BAUD_PRESCALE ; // Load lower 8- bits of the baud rate value into the low byte of theUBRR register
    for (;;) // Loop forever
    {
        while (( UCSRA & (1 << RXC )) == 0) {}; // Do nothing until data have been received and is ready to be read from UDR
        ReceivedByte = UDR ; // Fetch the received byte value into the variable " ByteReceived "
        while (( UCSRA & (1 << UDRE )) == 0) {}; // Do nothing until UDR is ready for more data to be written to it
        UDR = ReceivedByte ; // Echo back the received byte back to the computer
    }
}

A configuração do hardware

Foto da configuração do hardware

  • MCU: Atmega16;
  • Conjunto de ferramentas: Atmel Studio 7, piscando com o dragão AVR;
  • Fonte de alimentação: barramento de 5V retirado de uma placa de desenvolvimento fornecida pela universidade (retirada do USB do computador). Capacitor de disco de cerâmica de 100nF usado para contornar as linhas de energia da placa de ensaio
  • Conversor USB para serial: Este . TXD no conversor USB para serial conectado ao RXD Atmega (pino 15). RXD no conversor conectado ao RXD no Atmega (pino 14).
  • Software do terminal: PuTTY (com taxa de transmissão de 9600).

    Evidência de respostas incorretas

    Para reiterar, o Atmega deve retornar o que foi enviado a ele, ou seja, OUTPUT deve ser exatamente o mesmo que INPUT.

    Saída PuTTY

    ENTRADARESULTADOf&f6z>d0 0espaço0 0x8

    Capturas de osciloscópio

    Eu usei meu Picoscope com decodificação serial para verificar se o Atmega está recebendo a entrada correta, o que parece ser. Por exemplo, quando pressiono a tecla 'f', ela é recebida corretamente. A saída ainda é um '6' (ou um e comercial '&' na ocasião).

Captura de escopo no pino RX do Atmega16, mostrando que o caractere correto está sendo enviado através do software do terminal ('f')

Captura de escopo no pino TX do Atmega16, mostrando que uma resposta indesejada está sendo enviada de volta ('6')

Uma correção que me deparei que eu não entendo

Se eu alterar a taxa de transmissão para 2500in PuTTY, tudo será exibido corretamente. Escolhi esse valor aleatoriamente e não sei por que ele funciona (isso me leva a acreditar que cometi um erro em algum lugar relacionado à taxa de transmissão, mas não vejo onde, pois copiei o tutorial quase exatamente ... pensamento).

Questões

  1. O que eu fiz de errado / o que está acontecendo aqui?
  2. Por que o tutorial original não # define F_CPU?
  3. Por que definir a taxa de transmissão para 2500 corrige o problema? (Eu suspeito que isso será respondido se a pergunta 1 for respondida)
daviegravee
fonte
2
Simplesmente definir F_CPU para algum valor não faz o micro funcionar nessa frequência. F_CPU deve ser definida como a frequência em que você tem configurar o micro para rodar - mas eu não vejo nenhuma evidência de que você configurou isso em qualquer lugar ...
brhans
Pergunta bem escrita. A única coisa que melhoraria seria um esquema.
Blair Fonville
+1 apenas para o euUMATEXmesa.
Arsenal
Percebo que você não possui cristal externo na sua placa de ensaio. Você está usando o relógio RC interno? Em que frequência você espera que o processador esteja em execução?
precisa saber é o seguinte
Graças à sua discussão sobre o F_CPU, investiguei e brinquei e publiquei a solução. Eu imagino que seja óbvio para você (como é para mim agora ), mas pode ajudar outra pessoa.
Daviegravee

Respostas:

0

Eu descobri isso! Graças aos comentários sobre o F_CPU em resposta ao OP, investiguei (isso pode ser óbvio para todos).

Breve resumo da solução

O Atmega16 não estava funcionando na frequência que eu pensava, porque não sabia como alterar a frequência do sistema. Ao verificar os fusíveis no Atmel Studio, pude ver que eu estava funcionando a 2 MHz (essa não é a freqüência padrão do relógio, tanto quanto sei, mas não vou entrar nisso), e não 7.3728MHz como no tutorial.

F_CPU não altera a frequência do relógio do MCU (o Atmega16). A frequência do Atmega16 não foi alterada para 7.3728MHz, pois era necessário para que o exemplo de código funcionasse. Ele ainda estava funcionando na frequência definida pelos fusíveis (neste caso, 2MHz, mais sobre isso abaixo), de modo que o cálculo em papel da taxa de transmissão desejada difere do que realmente foi usado.

Código de trabalho

#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 2000000 //THIS LINE IS **NOT** CHANGING THE FREQUENCY OF THE MCU: CHANGE MCU FREQUENCY IN FUSES
#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((( F_CPU / 16) + ( USART_BAUDRATE / 2)) / ( USART_BAUDRATE )) - 1)

int main ( void ){
    char ReceivedByte ;
    UCSRB = (1 << RXEN ) | (1 << TXEN ); // Turn on the transmission and reception circuitry
    UCSRC = (1 << URSEL ) | (1 << UCSZ0 ) | (1 << UCSZ1 ); // Use 8- bit character sizes
    UBRRH = ( BAUD_PRESCALE >> 8); // Load upper 8- bits of the baud rate value into the high byte of the UBRR register
    UBRRL = BAUD_PRESCALE ; // Load lower 8- bits of the baud rate value into the low byte of theUBRR register
    for (;;){ // Loop forever
        while (( UCSRA & (1 << RXC )) == 0) {}; // Do nothing until data have been received and is ready to be read from UDR
        ReceivedByte = UDR ; // Fetch the received byte value into the variable " ByteReceived "
        while (( UCSRA & (1 << UDRE )) == 0) {}; // Do nothing until UDR is ready for more data to be written to it
        UDR = ReceivedByte ; // Echo back the received byte back to the computer
    }
}

Mais detalhes

Taxa de transmissão desejada versus o que o Atmega era realmente fazendo

A taxa de transmissão desejada (do tutorial) foi 9600, que é a taxa de transmissão que usei no PuTTY. A taxa de transmissão real pode ser calculada usando a equação destacada na Tabela 60 (página 147) da folha de dados do Atmega16.

Tabela de equações para calcular baudrate e UBRR na página 147 Folha de dados do Atmega16

No exemplo de código, BAUD_PRESCALEé UBRR no cálculo. BAUD_PRESCALEé avaliado como 47 com os valores definidos para F_CPUe USART_BAUDRATE.

BAUD=fosc16(UBRR+1)
BAUD=2,000,00016(47+1)
BAUD2,604

E essa foi a raiz do problema. O Atmega16 estava operando a 2 MHz, o que significava que o valor de f_ {osc} era diferente do exemplo do tutorial, o que resultou em uma taxa de transmissão de 2.604 em comparação a 9.600.

Observe que f_osc é a frequência real do sistema do MCU, que não é determinada por F_CPU.

Portanto, isso também responde à minha terceira pergunta: alterar a taxa de transmissão para 2.500 por sorte foi suficientemente próximo da taxa de operação do MCU para que o terminal pudesse interpretar corretamente os resultados.

Alterando a frequência do MCU

Para alterar a frequência do MCU no AtmelStudio 7, vá:

Tools > Device programming > Fuses > Change SUT_CKSEL (or LOW.SUT_CKSEL in my case) to desired frequency (make sure you have read up on the side effects of this). 

A frequência usada no exemplo não é uma freqüência de relógio interno padrão, então eu vou ficar com 2MHz.

Resumo das respostas para minhas próprias perguntas

  1. O que eu fiz de errado / o que está acontecendo aqui? Resposta : Na verdade, não alterou a frequência do relógio para a frequência do relógio no tutorial, o que resultou em uma taxa de transmissão diferente da esperada, o que colocou o software do terminal (PuTTY) fora de sincronia com o MCU
  2. Por que o tutorial original não # define F_CPU? Resposta : Ainda não tenho muita certeza, mas meu palpite seria que ele está definido em um makefile não fornecido no tutorial e que o autor não estava usando um IDE como o Atmel Studio
  3. Por que definir a taxa de transmissão para 2500 corrige o problema? (Eu suspeito que isso será respondido se a pergunta 1 for respondida) Resposta : Felizmente, adivinhou um número próximo à taxa de transmissão do Atmega16
daviegravee
fonte