Неожиданный ответ Atmega16 через UART

Неожиданный ответ Atmega16 через UART

Краткое описание проблемы

Я прошил Atmega16 с кодом, который должен привести к тому, что Atmega16 отправит обратно любой символ, который я отправлю ему через терминал. Я получаю ответ, но это редко тот персонаж, которого я отправлял. Я вижу правильный вывод, изменяя скорость передачи данных, но я не понимаю, почему работает правильная скорость передачи данных.

Подробнее

Я пытаюсь узнать больше о программировании прошивки в свободное время, потому что мне это очень нравится. До сих пор при программировании прошивки, которое я делал в университете, нам давали файлы скелетного кода, которые выполняют множество периферийных интерфейсов и настраивают для нас, но я хотел бы изучить это сам. У меня есть несколько вопросов о том, что я здесь делаю, разбросанных по всему сообщению, но я перечислю их в конце. Если вы обнаружите какие-либо недоразумения или потенциальные пробелы в моих знаниях, я был бы очень признателен за любой ваш вклад.

Код

Код, который я записал на свой Atmega16, взят почти построчно из руководства «Использование USART в AVR-GCC», которое можно найти на этой странице . Все, что я добавил, это #define для F_CPU. В исходном коде не было #define для F_CPU, поэтому мой код не компилировался в AtmelStudio 7. Может ли кто-нибудь объяснить, почему автор не определил F_CPU в исходном файле? Я предполагаю, что они могли использовать какой-то другой инструмент или компилятор, кроме Atmel Studio 7, но я не могу сказать наверняка.

#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
    }
}

Аппаратная настройка

Фото аппаратной установки

  • Микроконтроллер: Atmega16;
  • Набор инструментов: Atmel Studio 7, прошивка с помощью AVR dragon;
  • Источник питания: шина 5 В, взятая из предоставленной университетом макетной платы (которая взята из компьютерного USB). Керамический дисковый конденсатор емкостью 100 нФ, используемый для шунтирования линий электропередач на макетной плате.
  • Преобразователь USB в последовательный порт: этот . TXD на преобразователе USB в последовательный порт, подключенном к RXD Atmega (контакт 15). RXD на преобразователе подключен к RXD на Atmega (контакт 14).
  • Программное обеспечение терминала: PuTTY (со скоростью 9600 бод).

    Доказательства неправильных ответов

    Повторюсь, Atmega должен вернуть то, что ему было отправлено, т. е. ВЫХОД должен быть точно таким же, как ВХОД.

    Выход PuTTY

    ВХОД ВЫВОД ф & ф 6 г > г 0 Космос 0 Икс 8

    Захваты осциллографа

    Я использовал свой пикоскоп с последовательным декодированием, чтобы убедиться, что Atmega получает правильный входной сигнал, каким он кажется. Например, когда я нажимаю клавишу «f», он принимается правильно. Вывод по-прежнему представляет собой «6» (или иногда амперсанд «&»).

Захват области на выводе RX Atmega16, показывающий, что правильный символ отправляется через программное обеспечение терминала ('f')

Захват области на выводе TX Atmega16, показывающий, что нежелательный ответ отправляется обратно («6»)

Исправление, на которое я наткнулся, но не понимаю

Если я изменяю скорость передачи данных на 2500 в PuTTY, все отображается правильно. Я выбрал это значение наугад, и я не знаю, почему оно работает (это заставляет меня думать, что я где-то сделал ошибку, связанную со скоростью передачи, но я не вижу, где я почти точно скопировал руководство... Я подумал).

Вопросов

  1. Что я сделал не так/что здесь происходит?
  2. Почему в исходном руководстве нет #define F_CPU?
  3. Почему установка скорости передачи 2500 решает проблему? (Я подозреваю, что на это будет дан ответ, если будет дан ответ на вопрос 1)
Простое определение F_CPU для некоторого значения не заставит микро работать на этой частоте. F_CPU должен быть определен как частота, на которой вы настроили микро для запуска, но я не вижу никаких доказательств того, что вы настроили это где-либо...
Хорошо написанный вопрос. Единственное, что могло бы его улучшить, это схема.
+1 только за л А Т Е Икс стол.
Я заметил, что у вас нет внешнего кристалла на вашей макетной плате. Используете ли вы внутренние часы RC? Какую частоту вы ожидаете от процессора?
Благодаря вашему обсуждению F_CPU я провел небольшое исследование и поэкспериментировал и опубликовал решение. Я полагаю, что это очевидно для вас (как и для меня сейчас ), но это может помочь кому-то другому.

Ответы (1)

Я понял это! Благодаря комментариям о F_CPU в ответ на OP я провел небольшое расследование (это может быть очевидно для всех вас).

Краткое описание решения

Atmega16 не работал на частоте, которую я думал, потому что я не понимал, как изменить ее системную частоту. Проверив предохранители в Atmel Studio, я увидел, что работаю на частоте 2 МГц (насколько мне известно, это не стандартная тактовая частота, но я не буду вдаваться в подробности), а не на 7,3728 МГц, как в учебнике.

F_CPU не изменяет тактовую частоту микроконтроллера (Atmega16). Частота Atmega16 не была изменена до 7,3728 МГц, поскольку это было необходимо для работы примера кода. Он по-прежнему работал на частоте, определяемой предохранителями (в данном случае 2 МГц, подробнее об этом ниже), поэтому бумажный расчет желаемой скорости отличается от того, что использовалось на самом деле.

Рабочий код

#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
    }
}

Подробнее

Желаемая скорость передачи по сравнению с тем, что на самом деле делал Atmega

Желаемая скорость (из учебника) была 9600, это скорость, которую я использовал в PuTTY. Фактическую скорость передачи данных можно рассчитать с помощью выделенного уравнения в Таблице 60 (стр. 147) таблицы данных Atmega16.

Таблица уравнений для расчета скорости передачи данных и UBRR со страницы 147 таблицы данных Atmega16

В примере кода BAUD_PRESCALEэто UBRR в расчете. BAUD_PRESCALEоценивается как 47 со значениями, определенными для F_CPUи USART_BAUDRATE.

БОД знак равно ф о с с 16 ( УБРР + 1 )
БОД знак равно 2 , 000 , 000 16 ( 47 + 1 )
БОД 2 , 604

И это было корнем проблемы. Atmega16 работала на частоте 2 МГц, а это означало, что значение f_{osc} отличалось от значения в учебном примере, что приводило к скорости 2604 бод вместо 9600.

Обратите внимание, что f_osc — это фактическая системная частота MCU, которая не определяется параметром F_CPU.

Так что это также отвечает на мой третий вопрос: изменение скорости передачи на 2500, к счастью, было достаточно близко к рабочей скорости MCU, чтобы терминал мог правильно интерпретировать результаты.

Изменение частоты MCU

Чтобы изменить частоту MCU в AtmelStudio 7, перейдите:

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

Частота, используемая в примере, не является стандартной внутренней тактовой частотой, поэтому я собираюсь придерживаться 2 МГц.

Резюме ответов на мои вопросы

  1. Что я сделал не так/что здесь происходит? Ответ : На самом деле тактовая частота не была изменена на тактовую частоту в учебном пособии, что привело к скорости передачи данных, отличной от ожидаемой, что привело к рассинхронизации программного обеспечения терминала (PuTTY) с MCU.
  2. Почему в исходном руководстве нет #define F_CPU? Ответ : Все еще не совсем уверен, но я предполагаю, что это определено в make-файле, не указанном в руководстве, и что автор не использовал IDE, такую ​​​​как Atmel Studio.
  3. Почему установка скорости передачи 2500 решает проблему? (Я подозреваю, что на этот вопрос будет дан ответ, если будет дан ответ на вопрос 1) Ответ : Удачно угадал число, близкое к скорости передачи данных Atmega16.