Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: USART на АTmega48
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
NikitoS-86
Доброго времени суток. Значит так, собрал платку на 48 Атмеге, работает, необходимо отправлять данные на ПК... Как топорный вариант выбрал COM-порт. Для этого была взята схемка MAX232, ну вобщем всё по книжке... Однако, не получается даже сделать простешего зеркала=/. При этом ситуация следующая:
- Атмега работает от внешнего кварца в 20 МГц;
- скорость - не важно, пробовал на многих, ни на одной не работает...
- МАХ рабочий, тупо закорачивал ноги - сигнал возвращался тут же...
- если оциллографом смотреть на ногах самой Атмеги, то приходит всегда то, что нужно, а в ответ уходит какая-то ересь... (в моём случае это 0x80)

Ну вот собственно код:
Код
#include <avr/io.h>
#include <avr/interrupt.h>

#define FOSC 20000000 // Clock Speed
#define BAUD 2400
#define MYUBRR FOSC/16/BAUD-1

void USART_Init (unsigned int);

volatile unsigned char buff;

ISR (USART_RX_vect, ISR_BLOCK)
{
    buff=UDR0;
    while ( !( UCSR0A & (1<<UDRE0)) );
    
    UDR0 = buff;    
}

ISR (USART_TX_vect, ISR_BLOCK)
{
    buff=0;
}



void main( void )
{
    sei();
    USART_Init(MYUBRR);
    for(;;);
}

void USART_Init(unsigned int ubrr)
{
UBRR0H = (unsigned char)(ubrr>>8);            
UBRR0L = (unsigned char)ubrr;

UCSR0B |= (1<<RXCIE0);            
UCSR0B |= (1<<TXCIE0);

UCSR0B |= (1<<RXEN0)|(1<<TXEN0);            

UCSR0C = (1<<USBS0)|(3<<UCSZ00);            
}



Может быть кварц слишком быстрый? Но по даташиту ограничения нету на его частоту никаких...
Спасибо.
Палыч
Вы уверены что вот это будет подсчитано правильно

#define MYUBRR FOSC/16/BAUD-1

Я - не уверен. Посчитайте на калькуляторе и запишите результат:

#define MYUBRR 520 // Как-то так

Кстати, при F=20МГц целые делители для стандартных скоростей не получить...
NikitoS-86
Цитата(Палыч @ Dec 9 2008, 19:36) *
Вы уверены что вот это будет подсчитано правильно

#define MYUBRR FOSC/16/BAUD-1

Я - не уверен. Посчитайте на калькуляторе и запишите результат:

#define MYUBRR 520 // Как-то так

Кстати, при F=20МГц целые делители для стандартных скоростей не получить...

Без разницы, уже пробовал=/ И при этом не только на этой скорости... Пробовал прям ставить цифры даже для интересующей меня (например 115200, т.е. 10) - всёравно на выходе не то... А данная формула - это кусок из даташита... В принципе совершенно естественно, что точно не посчитается... Но не должно же быть так, что вообще другое число...
Палыч
Цитата(NikitoS-86 @ Dec 9 2008, 19:43) *
Но не должно же быть так, что вообще другое число...
Ну, почему же не должно? С какой разрядностью производиятся вычисления? Число 20 000 000 в целое (16 бит) не помещается... Да и деления вовсе не обязаны выполняться в том порядке, в котором записаны...
NikitoS-86
Цитата(Палыч @ Dec 9 2008, 19:49) *
Ну, почему же не должно? С какой разрядностью производиятся вычисления? Число 20 000 000 в целое (16 бит) не помещается... Да и деления вовсе не обязаны выполняться в том порядке, в котором записаны...

Даже когда ставлю туда интересующее меня число, всёравно на выход левое значение выходит...
Сергей Борщ
Цитата(NikitoS-86 @ Dec 9 2008, 18:27) *
- если оциллографом смотреть на ногах самой Атмеги, то приходит всегда то, что нужно, а в ответ уходит какая-то ересь... (в моём случае это 0x80)
Странно. Уходить не должно вообще. Прерывания приема не разрешены, значит до записи в UDR дело не доходит. Зачем вы используете прерывания, если внутри обработчика все равно ждете в цикле?

Код
UCSR0B |= (1<<RXCIE0);            
UCSR0B |= (1<<TXCIE0);

UCSR0B |= (1<<RXEN0)|(1<<TXEN0);
Каков глубокий смысл этих манипуляций? Почему не написать сразу UCSR0B = (1<<RXCIE0)| (1<<TXCIE0)| (1<<RXEN0)|(1<<TXEN0); и не забыть | (1<<RXСIE0)
Цитата(NikitoS-86 @ Dec 9 2008, 18:27) *
Может быть кварц слишком быстрый? Но по даташиту ограничения нету на его частоту никаких...
Ограничения есть в зависимости от напряжения питания. Красивые графики №131 и 132.
Цитата(Палыч @ Dec 9 2008, 18:36) *
Я - не уверен. Посчитайте на калькуляторе и запишите результат:
Абсолютно глупый совет. А если завтра кварц другой поставят? Снова калькулятор в зубы? А если скорость другая потребуется? А как не забыть все места, в которых надо подправить такие "магические" числа? Или вы думаете, что калькулятор в компиляторе может ошибаться? Тогда надежнее всего писать прямо в кодах - мало ли, ассемблер ошибется и не тот код команды подставит. Чтобы компилятор посчитал правильно, надо ему правильно написать: #define FOSC 20000000ULL Все, дальше вычисления идут в длинных целых. Порядок действий компилятор изменить может лишь в том случае, если это не повлияет на результат.
Палыч
Может fuse CKDIV8 остался запрограммирован? С завода он таким приходит - нужно сбросить в 1
man with no name
попробуйте uart включать после окончательной инициализации (строчку UCSR0B |= (1<<RXEN0)|(1<<TXEN0); поставить после установки параметров uart)

В #define FOSC 20000000 я бы добавил ul (#define FOSC 20000000ul) - не хочется гадать какой тип по умолчанию использует препроцессор.

Ну и убедитесь, что проц работает от внешнего кварца - проверьте fuse-биты. "Вобще другое число" получается, возможно, при работе от внутреннего RC-генератора на 1МГц, либо от 20МГц/8, если бит деления частоты на 8 (CLKDIV8 вроде) оставили сброшенным, как по умолчанию.
Палыч
Цитата(Сергей Борщ @ Dec 9 2008, 19:54) *
Странно. Уходить не должно вообще. Прерывания приема не разрешены, значит до записи в UDR дело не доходит.
Так вот же - разрешены:
Код
UCSR0B |= (1<<RXCIE0);
NikitoS-86
Цитата(Сергей Борщ @ Dec 9 2008, 19:54) *
Странно. Уходить не должно вообще. Прерывания приема не разрешены, значит до записи в UDR дело не доходит. Зачем вы используете прерывания, если внутри обработчика все равно ждете в цикле?


Цитата
Код
UCSR0B |= (1<<RXCIE0);            
UCSR0B |= (1<<TXCIE0);

UCSR0B |= (1<<RXEN0)|(1<<TXEN0);
Каков глубокий смысл этих манипуляций? Почему не написать сразу UCSR0B = (1<<RXCIE0)| (1<<TXCIE0)| (1<<RXEN0)|(1<<TXEN0); и не забыть | (1<<RXСIE0)

Это как это не разрешены? Помоему как раз строчка
UCSR0B |= (1<<RXCIE0);
отвечает за их разрешение, в связи с чем, просто не совсем понятно "и не забыть | (1<<RXСIE0)"...

Цитата
Ограничения есть в зависимости от напряжения питания. Красивые графики №131 и 132.

А это где такая нумерация? Если в даташите, то там все графики в формате "номер главы"-"номер рисунка"
Цитата
...Чтобы компилятор посчитал правильно, надо ему правильно написать: #define FOSC 20000000ULL Все, дальше вычисления идут в длинных целых. Порядок действий компилятор изменить может лишь в том случае, если это не повлияет на результат.

А вот этого не знал - спасибо...

PS: Если где туплю - тыкайте носом=) Целый день перед монитором - уже буквы на мониторе пляшут))
gormih
Цитата(Сергей Борщ @ Dec 9 2008, 19:54) *
А если завтра кварц другой поставят? Снова калькулятор в зубы? А если скорость другая потребуется? А как не забыть все места, в которых надо подправить такие "магические" числа? Или вы думаете, что калькулятор в компиляторе может ошибаться?

А если поставят кварц, когда ошибка разсинхронизации выйдет за грани допустимой при любых прескалерах - не думали? Именно для исключения подобных неурядиц пользуются так называемыми Code Wizard или Uart calculator. Например в Code Vision это очень хорошо сделано, да и для IAR вроде уже есть плагины.
Сергей Борщ
Цитата(NikitoS-86 @ Dec 9 2008, 19:10) *
Это как это не разрешены? Помоему как раз строчка
UCSR0B |= (1<<RXCIE0);
отвечает за их разрешение, в связи с чем, просто не совсем понятно "и не забыть | (1<<RXСIE0)"...
Это назывется "между глаз лежало". Но цикл в прерывании лишний. Напишите там просто UDR = UDR; В чем смысл прерывания по окончанию передачи тоже не понятно. Хоть ногой там махните smile.gif
Цитата(NikitoS-86 @ Dec 9 2008, 19:10) *
А это где такая нумерация? Если в даташите, то там все графики в формате "номер главы"-"номер рисунка"
Ну, может у меня даташит и старый, 2545D–AVR–07/04, но подобные параметры обычно расположены в разделе про электрические характеристики. Да, скачал свежий - картинки 28.1 и 28.2
gormih
AVR CALCULATOR
Хватит уже п**у мучать, товарищи...
Сергей Борщ
Цитата(gormih @ Dec 9 2008, 19:37) *
А если поставят кварц, когда ошибка разсинхронизации выйдет за грани допустимой при любых прескалерах - не думали?
И что будет? В худшем случае на первом же тестовом устройстве связи не будет. Причем сразу ясно, где копать. В лучшем - стоит проверка, вычисляющая получившуюся ошибку и дающая #error или #warning прямо на этапе компиляции. А если вы изменили кварц и из пяти таких "магических" цифр вспомнили поменять только четыре - приятной отладки.
gormih
Цитата(Сергей Борщ @ Dec 9 2008, 20:49) *
И что будет? В худшем случае на первом же тестовом устройстве связи не будет. Причем сразу ясно, где копать. В лучшем - стоит проверка, вычисляющая получившуюся ошибку и дающая #error или #warning прямо на этапе компиляции. А если вы изменили кварц и из пяти таких "магических" цифр вспомнили поменять только четыре - приятной отладки.

Будет именно то, что написал товарищ. А на скорости 2400 при кварце 20 Гц нет таких прескалеров, которые сделали бы уарт работоспособным. Именно поэтому и рекомендую пользовать вышеуказанный софт beer.gif

Хотя, извиняюсь... калькулятор кривоват оказался.
В Code Vision все ок..
А на счет #warning и #error поддреживаю, вопрос только в том - зачем делать то, что уже сделано другими (это кстати к вопросу - может на асме наченм программировать? :-) )
=GM=
Цитата(NikitoS-86 @ Dec 9 2008, 16:27) *
- если оциллографом смотреть на ногах самой Атмеги, то приходит всегда то, что нужно, а в ответ уходит какая-то ересь... (в моём случае это 0x80)

Мне кажется, поведение вашего кода можно объяснить следующим. После принятия очередного байта программа обработки прерывания приёмника начинает анализировать флаг готовности передатчика, но прерывание от передатчика срабатывает быстрее и успевает обнулить ваш буфер, так что вы всё время передаёте чистый нуль. А 0х80 возможно получается из-за того, что неправильно выбрана скорость приёмника писюка или вы неправильно измерили.

Чтобы проверить мои предположения, вам нужно удалить обработчик прерывания от передатчика, всё равно он там ни к селу ни к городу и запустить программу. Ну или записывайте в буфер некое число, отличное от нуля, например, 0х41.

Если не поможет, то проверьте передачу символов от МК на ПК. Передавайте в цикле известный символ, например, В
Код
while(1)
{
UDR0=0х42;
delay(10000);    
}
NikitoS-86
Значится так...
1) Всем спасибо, поскольку полезной инфы для себя ещё подчерпнул...
2) Правы оказались товарищи Палыч и man with no name, сегодня первым делом прочитал, что было написано в ветке и за место 20 Мгц поставил 2,5 Мгц (как деление на 8) - всё заработало... Для меня если честно стало сюрпризом... Поясню почему - когда прошивал фьюзы - был абсолютно уверен, что если выбрать в закладке фьюзов в последней строке что-то типа внешнего тактового источника и нажать програм, то всё будет как надо... Как видно я ошибался... Разве это не так? К тому же теперь я не доберусь до фьюзов никак, поскольку у меня на руках mk2, то теперь он мне не даст без СТК500 добраться до фьюзов... Или я опять ошибаюсь?

3) Теперь по поводу моего кода... Да, прерывание по передаче, возможно и лишнее, как и цикл, но тогда у меня встаёт вопрос - а как обойтись без цикла (ну который у меня под вайлом идёт)? Просто когда мне надо переслать 1 байт - всё замечательно, он ушёл и бог с ним... А если целый пакет? Не получится ли так, что просто в цикле будет так, что следующий пакет будет наступать на пятки предыдущему? Или может тут как раз стоит подумать о прерывании по окончании передачи?

4) И ещё вопрос... Ситуация - надо отослать массив данных, например, short (2 байта)... Мне достаточно написать:
UDDR0=Mass[i]
и он сам поймёт, что надо просто тупо запихать сначала первый байт инта, потом второй? Или же надо самостоятельно ему поочереди запихнуть сначала первый байт, потом второй?

Спасибо.
Сергей Борщ
Цитата(NikitoS-86 @ Dec 10 2008, 11:07) *
когда прошивал фьюзы - был абсолютно уверен, что если выбрать в закладке фьюзов в последней строке что-то типа внешнего тактового источника и нажать програм, то всё будет как надо...
Вот-вот... тоже попался на удочку визардов. Пользуюсь AVReal. Читаю описание всех фузов, пишу .bat с командной строкой, абсолютно уверен, что "оно само" ничего лишнего не сделает.
Цитата(NikitoS-86 @ Dec 10 2008, 11:07) *
Просто когда мне надо переслать 1 байт - всё замечательно, он ушёл и бог с ним... А если целый пакет?
В данном случае целый пакет вы не получите, потому что уже сидите в прерывании приема. Если надо переслать несколько байт, то используют промежуточный буфер в озу и прерывание по освобождению UDR, а не по окончанию передачи. Примерно вот так (код для меги8, но идея понятна):
CODE
#define TX_BUFFER_SIZE 8
uint8_t volatile TxHead;
uint8_t volatile TxTail;
uint8_t TxPool[TX_BUFFER_SIZE];


void send_byte(unsigned char byte)
{
uint8_t Tmp = TxHead;
while((uint8_t)(Tmp - TxTail) >= (uint8_t) TX_BUFFER_SIZE)
; // buffer full, wait

TxBuffer[ Tmp ] = Byte;
Tmp = (Tmp + 1) & (TX_BUFFER_SIZE - 1);
TxHead = Tmp;
UCSRB |= (1<<UDRIE);
}

void send_str(PGM_P string)
{
char c;
while( (c = pgm_read_byte(string++)) != '\x0' )
{
send_byte( c );
}
}
void send_str_P(char * string)
{
char c;
while( (c = *string++) != '\x0' )
{
send_byte( c );
}
}


ISR(USART_UDRE_vect)
{
UCSRB &= ~(1<<UDRIE);
sei();
{
uint8_t Tmp = TxTail;
UDR = TxBuffer[ Tmp ];
Tmp = (Tmp + 1) & (TX_BUFFER_SIZE - 1)
TxTail = Tmp;
if(Tmp != TxHead) // more data pending in buffer
{
UCSRB |= (1<<UDRIE);
}
}
}
или, в простейшем случае, без прерываний:
Код
void send_byte(uint8_t Byte)
{
    while(!(UCSRA & (1<<UDRE)));
    UDR = Byte;
}
В любом случае ожидание должно происходить вне обработчика прерывания.
Цитата(NikitoS-86 @ Dec 10 2008, 11:07) *
Не получится ли так, что просто в цикле будет так, что следующий пакет будет наступать на пятки предыдущему? Или может тут как раз стоит подумать о прерывании по окончании передачи?
В случае "эха" - не получится. И прерывание по освобождению UDR. Прерывание об окончании передачи используется для другого - например, для отключения передатчика в RS485.
Цитата(NikitoS-86 @ Dec 10 2008, 11:07) *
4) И ещё вопрос... Ситуация - надо отослать массив данных, например, short (2 байта)... Мне достаточно написать:
UDDR0=Mass[i]
и он сам поймёт, что надо просто тупо запихать сначала первый байт инта, потом второй? Или же надо самостоятельно ему поочереди запихнуть сначала первый байт, потом второй?
Конечно самостоятельно. В вашей строке он запихнет только i-тый элемент массива. Можно написать универсальную функцию:
Код
void send_block(void const *ptr, uint8_t size)
{
    while(size--)
    {
        send_byte(*ptr++);
    }
}

void test()
{
   unsigned char Mass[2];
   unsigned short Short;

   send_block(Mass, sizeof(Mass));
   send_block(&Short, sizeof(Short));
}
NikitoS-86
Цитата(Сергей Борщ @ Dec 10 2008, 12:53) *
В данном случае целый пакет вы не получите, потому что уже сидите в прерывании приема.
/.../или, в простейшем случае, без прерываний:
Код
void send_byte(uint8_t Byte)
{
    while(!(UCSRA & (1<<UDRE)));
    UDR = Byte;
}
В любом случае ожидание должно происходить вне обработчика прерывания.

Вот тут я несколько не понял, что мне мешает, сидя в прерывании, отсылать ему всё, что у меня так накопилось... Как раз таки вставив код "простейшего случая" у меня получилось отослать всё своё, правда конечно реализация выборки байтов элементов массива была ужасная... Я просто не понимаю в чем тут подводный камень?

Цитата
Конечно самостоятельно. В вашей строке он запихнет только i-тый элемент массива. Можно написать универсальную функцию:
Код
void send_block(void const *ptr, uint8_t size)
{
    while(size--)
    {
        send_byte(*ptr++);
    }
}

void test()
{
   unsigned char Mass[2];
   unsigned short Short;

   send_block(Mass, sizeof(Mass));
   send_block(&Short, sizeof(Short));
}

А вот тут меня смущает только одно: цикл while мы проходим по уменьшению размера в байтах, что логично, однако send_byte при этом идёт по увеличению указателя... В случае массива разве не получится, что тут же перепрыгнем на второй элемент массива?

Спасибо.
Сергей Борщ
Цитата(NikitoS-86 @ Dec 10 2008, 15:11) *
Вот тут я несколько не понял, что мне мешает, сидя в прерывании, отсылать ему всё, что у меня так накопилось...
Да потому что не накопится ничего. Неоткуда накопиться - вы уже сидите в прерывании приема. Значит все остальные прерывания запрещены.
Цитата(NikitoS-86 @ Dec 10 2008, 15:11) *
Как раз таки вставив код "простейшего случая" у меня получилось отослать всё своё, правда конечно реализация выборки байтов элементов массива была ужасная... Я просто не понимаю в чем тут подводный камень?
В том, что этот код выполняется не из прерывания, а из основной программы. Все другие прерывания, в том числе и прерывание приема, в этом случае работают.
Цитата(NikitoS-86 @ Dec 10 2008, 15:11) *
А вот тут меня смущает только одно: цикл while мы проходим по уменьшению размера в байтах, что логично, однако send_byte при этом идёт по увеличению указателя... В случае массива разве не получится, что тут же перепрыгнем на второй элемент массива?
Керниган и Ритчи. "Язык программирования С". Второе издание. Ссылку найдете через Гугля. Прочитать от начала до конца. Потом внимательно перечитать раздел 2.8 "Операторы инкремента и декремента". Обратите внимание, чем отличаются постфиксная и префиксная формы этих операторов.

P.S. Да, там у меня ошибка, должно быть
Код
void send_block(void const *buffer, uint8_t size)
{
    uint8_t *ptr = (uint8_t *)buffer;
    while(size--)
    {
        send_byte(*ptr++);
    }
}
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.