|
|
  |
UART в Xmega, Проблема с настройкой скорости |
|
|
|
Jul 17 2015, 12:29
|

Частый гость
 
Группа: Свой
Сообщений: 85
Регистрация: 6-02-15
Пользователь №: 84 967

|
Здравствуйте) Набросал не большой код: UART настраивал на скорость в 9600 , 8 бит данных, 1 стоп бит и без бита парритета. Плюс отключен параметр удвоения скорости. CODE #define F_CPU 12000000UL #define __DELAY_BACKWARD_COMPATIBLE__
#include <stdlib.h> #include <avr/io.h> #include <stdio.h> #include <stddef.h> #include <avr/interrupt.h> #include <avr/pgmspace.h>
//Переменные volatile char buf[26]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; volatile char dat[26]={0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55}; unsigned char i,cnt,y;
// Функция включения USART_D1, cкорость 9600( а надо 250000) bod void StartUsartD1() { USARTD1_BAUDCTRLB = 0; USARTD1_BAUDCTRLA = 0x4D; USARTD1_CTRLA = 0; //Отключение прерываний USARTD1_CTRLC = USART_CHSIZE_8BIT_gc; //8 data bits, no parity and 1 stop bit USARTD1_CTRLB = USART_TXEN_bm | USART_RXEN_bm; //Включение приема передатчика PORTD_OUTSET = PIN7_bm; //Настраиваем выводы PC6 и PC7 PORTD_DIRSET = PIN7_bm; PORTD_OUTCLR = PIN6_bm; PORTD_DIRCLR = PIN6_bm; } // Функция получения данных USART_D1. char getChar1(void) { char buffer1; while (1) { while( !(USARTD1_STATUS & USART_RXCIF_bm) ); buffer1=USARTD1_DATA; //if ((USARTD1_STATUS & (USART_BUFOVF_bm))==0) if ((USARTD1_STATUS & (USART_FERR_bm | USART_PERR_bm | USART_BUFOVF_bm))==0) return buffer1; }; }
// Фунуция передачи данных USART_D1 void sendChar1(char d1) { while( !(USARTD1_STATUS & USART_DREIF_bm) ); USARTD1_DATA = d1; }
// Функция инициализации int main(void) { // Настройка работы от внешнего кварца в 12МГц cli(); OSC.XOSCCTRL = 0x8B; OSC.CTRL = 0x08; while((OSC.STATUS & 0x08) == 0); //PORTD.OUTSET =0x04; PORTD.DIR = 0x04; //Активация USART StartUsartD1(); while (1) { cnt = getChar1(); if (cnt ==0x55) { for (i=0;i<12;i++) sendChar1(buf[i]); } else { for (i=0;i<12;i++) sendChar1(dat[i]); } }; } Скомпилировал, прошил, запускаю терминал По моей задумке если я отправляю 0x55(символ юникода получается U) то мне шлется массив с 0xFF, если что нить другое то 0x55. В итоге две проблемы: 1) ответ идет в виде массива 0x0E, в теории говорит что скорость настроена не правильно. Но вроде перепроверил все.  BSEL = 12000000/(2^0 * 16 * 9600) = 78.125 -1 = 77 (0x4D) 2) Как только прошил схему, без отправки контроллеру чего либо, он мне уже присылает в ответ 2 массива. Помогите пожалуйста разобраться  =(
Сообщение отредактировал Nosaer - Jul 17 2015, 12:30
|
|
|
|
|
Jul 17 2015, 16:19
|

Профессионал
    
Группа: Участник
Сообщений: 1 620
Регистрация: 22-06-07
Из: Санкт-Петербург, Россия
Пользователь №: 28 634

|
Куски из моего кода с инициализацией: Код PORTE.DIRSET = PIN3_bm; // PE3 (TXD0) as output PORTE.DIRCLR = PIN2_bm; // PE2 (RXD0) as input PORTE_PIN2CTRL = (PORTE_PIN2CTRL & ~ PORT_OPC_gm) | PORT_OPC_PULLUP_gc; // pin is pulled high
USARTE0.CTRLC = USART_CMODE_ASYNCHRONOUS_gc | USART_PMODE_DISABLED_gc | USART_CHSIZE_8BIT_gc; USARTE0.CTRLB = USART_RXEN_bm | USART_TXEN_bm;
// Использование автоматического расчёта предделителя unsigned value; const uint_fast8_t prei = calcdivider(calcdivround(baudrate), ATXMEGA_UBR_WIDTH, ATXMEGA_UBR_TAPS, & value, 1); if (prei == 0) USARTE0.CTRLB |= USART_CLK2X_bm; else USARTE0.CTRLB &= ~USART_CLK2X_bm; // todo: проверить требование к порядку обращения к портам USARTE0.BAUDCTRLA = (value & 0xff); /* Значение получено уже уменьшенное на 1 */ USARTE0.BAUDCTRLB = (ATXMEGA_UBR_BSEL << 4) | ((value >> 8) & 0x0f); Делители расчитаете и в ручную, ту просто что-то неочевидное при записи BAUDCTL A/B учитывать пришлось. Полный код тут: https://188.134.5.254/browser/hfreceiver/trunk/hardware.c
|
|
|
|
|
Jul 22 2015, 04:51
|

Профессионал
    
Группа: Свой
Сообщений: 1 292
Регистрация: 26-06-07
Пользователь №: 28 718

|
1. Вы неправильно считаете. 2. Для работы с УАПП используют специальные кварцы: 7.3728, 11.0592, 14.7456 и подобные. Тогда при делении на целое число вы получите правильную скорость. Приведу пример, чтобы было видно, что там всё просто считать Код void SetBaudrate(uint32_t pBaudrate) const { if(pBaudrate != 0) { //attention: value of BSEL always is 0 uint16_t ubrr;
ubrr = F_PER / (16 * pBaudrate) - 1;
mRegisterBase->CTRLB |= (1 << USART_RXEN_bp) | (1 << USART_TXEN_bp); mRegisterBase->BAUDCTRLA = (uint8_t)ubrr; mRegisterBase->BAUDCTRLB = ~USART_BSCALE_gm & (ubrr >> 8); } else { mRegisterBase->CTRLB &= ~((1 << USART_RXEN_bp) | (1 << USART_TXEN_bp)); } } , где F_PER - тактовая частота периферии. В моём случае это было: 7.3728 МГц(кварц) * 4(умножитель PLL)
|
|
|
|
|
Jul 27 2015, 11:13
|

Частый гость
 
Группа: Свой
Сообщений: 85
Регистрация: 6-02-15
Пользователь №: 84 967

|
Почему не правильно то. Все как у Вас: Цитата ubrr = F_PER / (16 * pBaudrate) - 1; 12000000 / (16*9600) -1 Получаю 0x4D. После недельных изысканий решил вновь обратиться за помощью. Программу переделал, нашел несколько ошибок, реализовал передачу данных по прерыванию. Алгоритм работы в следующем, я шлю что угодно, мне приходит 0x55. Если я шлю символ юникода "U"(он же 0x55), то в ответ должно прийти 0x77. Проблемы собственно такие: 1) На настроенную скорость в 9600 шлется полнейший мусор. Если ставлю в терминале скорость от 9500 bod и ниже, то ответ начинает приходить более реальный в виде массива 0x55. Но если если я начинаю слать "U", то в ответ жду массив 0x77. А он приходит 1 раз из 5-10, в остальных 4-9 случаях приходит зачем то массив 0x55. 2) Отвечает контроллер не на каждую мою посылку. То есть раз 5 щелкнешь отправить, а ответ придет только один раз. Вот собственно последняя версия программы: CODE #define F_CPU 12000000UL
#include <stdlib.h> #include <avr/io.h> #include <stdio.h> #include <stddef.h> #include <avr/interrupt.h> #include <avr/pgmspace.h>
volatile char buf[5]={0x77,0x77,0x77,0x77,0x77}; volatile char dat[5]={0x55,0x55,0x55,0x55,0x55}; unsigned char i,cnt,y;
void StartUsartD1() { USARTD1_CTRLC= USART_PMODE1_bm | USART_CHSIZE0_bm | USART_CHSIZE1_bm; USARTD1_BAUDCTRLB = 0; USARTD1_BAUDCTRLA = 0x4D; //47 для 11,052 4D для 12 USARTD1_CTRLB = USART_TXEN_bm | USART_RXEN_bm; USARTD1_CTRLA=USART_RXCINTLVL0_bm; PORTD_OUTSET = PIN7_bm; PORTD_DIRSET = PIN7_bm; PORTD_OUTCLR = PIN6_bm; PORTD_DIRCLR = PIN6_bm; } char getChar1(void) { while(1){ char buffer1; buffer1=USARTD1_DATA; //if ((USARTD1_STATUS & (USART_FERR_bm | USART_PERR_bm | USART_BUFOVF_bm))==0) return buffer1; } }
void sendChar1(char d1) { while( !(USARTD1_STATUS & USART_DREIF_bm) ); USARTD1_DATA = d1; } ISR(USARTD1_RXC_vect) { cnt = getChar1(); }
int main(void) { // Clock Source Select OSC.XOSCCTRL = 0x8B; // выбор внешнего генератора с временем запуска 16 тыс. CLK и частотой 8-12 МГц OSC.CTRL = 0x08; // разрешение работы внешнего генератора while((OSC.STATUS & 0x08) == 0); // ожидание появления в регистре статуса бита включения синхронизации от внешнего генератора OSC_PLLCTRL = 0xC1; // настройка блока PLL на синхронизацию от внешнего источника и 1-х кратоное умножение OSC_CTRL = OSC_CTRL | 0x10; // разрешение работы блока PLL while((OSC_STATUS & 0x10) == 0 ) ; // ожидание появления в регистре статуса бита включения блока PLL CCP = CCP_IOREG_gc; CLK.CTRL = CLK_SCLKSEL_XOSC_gc; OSC_CTRL = OSC_CTRL & 0xFE; // отключение системной синхронизации от внутреннего RC-генератора частотой 2 МГц // Config Port PORTD.DIR = 0x04; StartUsartD1(); sei(); PMIC_CTRL = 1; while (1) { PORTD.OUTSET =0x04; // Led asm("NOP"); if (cnt != 0) { if (cnt == 0x55) for (i=0;i<5;i++) sendChar1(buf[i]); else for (i=0;i<5;i++) sendChar1(dat[i]); } cnt = 0; } } Уже и методом перебора прошелся по всем скоростям, пытаясь найти куда она уплыла из за какой нить погрешности. И кварц на 11.0592 вместо 12Мгц ставил, как советовал выше smalcom, и на другие скорости перенастраивал. Поведение одно и то же.
Сообщение отредактировал Nosaer - Jul 27 2015, 11:17
Эскизы прикрепленных изображений
|
|
|
|
|
Jul 28 2015, 04:48
|

Профессионал
    
Группа: Свой
Сообщений: 1 292
Регистрация: 26-06-07
Пользователь №: 28 718

|
> К ней я тупо двумя проводками и цеплюсь. ну и общий, да? и на всякий случай попробуйте ожидать конца передачи. т.е. Код void sendChar1(char d1) { while( !(USARTD1_STATUS & USART_DREIF_bm) ); USARTD1_DATA = d1; } дополнить до Код void sendChar1(char d1) { while( !(USARTD1_STATUS & USART_DREIF_bm) ); USARTD1_DATA = d1; while( !(USARTD1_STATUS & USART_TXCIF_bm) ); }
|
|
|
|
|
Jul 28 2015, 13:27
|

Частый гость
 
Группа: Свой
Сообщений: 85
Регистрация: 6-02-15
Пользователь №: 84 967

|
Ожидание конца передачи не помогло) Решил проблему иначе. Этот кусок кода сунул в обработку прерывания по приему данных с UART. Буфер с данными по команде cnt = 0; опустошался быстрее, чем подпрограмма успевала его обработать. Поэтому и были пропущенные команды. Код if (cnt != 0) { if (cnt == 0x55) for (i=0;i<5;i++) sendChar1(buf[i]); else for (i=0;i<5;i++) sendChar1(dat[i]); } cnt = 0; Скорость под 9600 подобрал методом подгона.
Сообщение отредактировал Nosaer - Jul 28 2015, 13:28
|
|
|
|
|
Aug 7 2015, 10:05
|
Участник

Группа: Участник
Сообщений: 19
Регистрация: 4-10-10
Из: г.Псков
Пользователь №: 59 908

|
При F_PER =12 000 000 Гц и F_Baud=9600 bit/s чтобы минимизировать ошибки обмена следует положить регистр BSCALE=-3 , тогда BSEL станет равным $0269 - целым числом, что делает обмен стопроцентным. Вообще, следует сделать расчеты для всех значений BSCALE (от -7 до 7) и выбрать тот вариант, где BSEL имеет минимальную дробную часть. При записи в регистры управления вместо -3 следует писать $0d. Т.о получаем: USART_BAUDCTRLA=$69 и USART_BAUDCTRLB=$d2 - параметры задающие скорость.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|