Проблемы прерывания USART_RX_vect в Atmel Studio 6

Обсуждаем контроллеры компании Atmel.
Аватара пользователя
DX168B
Друг Кота
Сообщения: 4468
Зарегистрирован: Вс янв 24, 2010 19:19:52
Откуда: Главный Улей России (Moscow)
Контактная информация:

Re: Проблемы прерывания USART_RX_vect в Atmel Studio 6

Сообщение DX168B »

Моя реализация.
Тем не менее, успешно работает в ряде проектов:

os_uart.h
Спойлер

Код: Выделить всё

//////////////////////////////////////////////////////////////////////////
//

#ifndef _OS_UART1_H
#define _OS_UART1_H

//////////////////////////////////////////////////////////////////////////
//
#define UART1_READ_INTERVAL_TMOUT 4

#define UART_RECEIVED_DATA      3
#define UART_TRANSMITTING        2
#define UART_RECEIVING            1
#define UART_FREE                0

#define UART1_RX_DDR            DDRD
#define UART1_RX_PORT            PORTD
#define UART1_RX_IN                PINE
#define UART1_RX_PIN            (1<<PD2)

#define UART1_TX_DDR            DDRD
#define UART1_TX_PORT            PORTD
#define UART1_TX_IN                PINE
#define UART1_TX_PIN            (1<<PD3)

#define UART1_485_DDR            DDRE
#define UART1_485_PORT            PORTE
#define UART1_485_PIN            (1<<PE7)

//////////////////////////////////////////////////////////////////////////
//
void UART1_Init(void);
void UART1_RcvTmOutCallBack(void);
uint8_t UART1_GetStatus(void);
uint8_t* UART1_GetRxBuffer(void);
uint8_t* UART1_GetTxBuffer(void);
uint8_t UART1_GetRxCount(void);
void UART1_RxRestart(void);
void UART1_SetRxBuffer(uint8_t *buff, uint8_t sizeOfBuff);
void UART1_SetTxBuffer(uint8_t *buff, uint8_t sizeOfBuff);

uint8_t UART1_ReceiveIntBuff(uint8_t *str, uint8_t lenght);
uint8_t UART1_Send(uint8_t *str, uint8_t lenght);
uint8_t UART1_SendIntBuff(uint8_t lenght);

#endif

 

os_uart1.c
Спойлер

Код: Выделить всё

#include <avr/interrupt.h>
#include <string.h>
#include "os_watchdog.h"
#include "os_systimer.h"
#include "os_uart1.h"

//////////////////////////////////////////////////////////////////////////
//
uint8_t *uart1TxBuff = 0;      // указатель на передаваемую строку
volatile uint8_t uart1SizeOfTxBuff = 0;
volatile uint8_t uart1TxNum;       // счетчик передаваемых байт
volatile uint8_t uart1TxCount = 0;

uint8_t *uart1RxBuff = 0;
volatile uint8_t uart1SizeOfRxBuff = 0;
volatile uint8_t uart1RxCount = 0;
volatile uint16_t uart1RxTmOut = 0;

volatile uint8_t uart1_bsy_flag = UART_FREE;


int64_t uart1_timers[1];

//////////////////////////////////////////////////////////////////////////
// Обработчик окончания передачи буфера
ISR(USART1_TX_vect)
{
    UART1_485_PORT &= ~UART1_485_PIN;  // RS485 на прием
    
    if((uart1RxBuff != 0) && (uart1SizeOfRxBuff > 0)) // Буфер задан корректно?
    {
        UART1_RxRestart(); // Запускаем UART в работу на прием
    }
    else // иначе, блокируем его
    {
        UCSR1B = 0; // отключаем УСАПП
        uart1_bsy_flag = UART_FREE; // освобождаем УСАПП
    }
}

//////////////////////////////////////////////////////////////////////////
// Обработчик очереди передачи буфера
ISR(USART1_UDRE_vect)
{
    if(uart1TxCount < uart1TxNum)// если есть еще данные на отправку
    {
        UDR1 = uart1TxBuff[uart1TxCount];// запихиваем следующий байт
        uart1TxCount++; // инкрементируем индекс
    }
    else
    {
        UCSR1B = ((1<<TXCIE1)|(1<<TXEN1)); // иначе выставляем прерывание по окончанию передачи
        uart1TxCount = 0;
    }
}

//////////////////////////////////////////////////////////////////////////
// Обработчик приема данных
ISR(USART1_RX_vect) // Вектор прерывания по окончанию приема байта УСАПП
{
    if(uart1RxCount == 0) // Если принят первый байт
    {
        uart1_bsy_flag = UART_RECEIVING; // Занимаем УСАПП, выставляя флаг "Идет сеанс приема данных"
        uart1RxBuff[0] = UDR1; // Копируем принятый байт в начало буфера
        uart1RxCount = 1; // Задаем индекс на следующий элемент буфера
        SYSTIMER_Set(uart1_timers, 0, UART1_READ_INTERVAL_TMOUT); // Заводим таймер (таймаут между принимаемыми байтами)
    }
    else // Иначе, если это уже не первый за сеанс принятый байт
    {
        if(uart1RxCount < (uart1SizeOfRxBuff - 1)) // Если есть куда принимать (буфер не окончен)
        {
            uart1RxBuff[uart1RxCount] = UDR1; // Копируем принятый байт в буфер
            uart1RxCount++; // Задаем индекс на следующий элемент буфера
            SYSTIMER_Set(uart1_timers, 0, UART1_READ_INTERVAL_TMOUT); // Заводим таймер (таймаут между принимаемыми байтами)
        }
        else
        {
            uart1RxBuff[uart1RxCount] = UDR1; // Копируем принятый байт в последний элемент буфера
            UCSR1B = 0; // Блокируем УСАПП полностью
            uart1_bsy_flag = UART_RECEIVED_DATA; // Выставляем флаг "Есть принятые данные"
        }
    }
}

//////////////////////////////////////////////////////////////////////////
// Вызываемая системным таймером процедура проверки таймаута по приему (период = 1мс)
void UART1_RcvTmOutCallBack(void) 
{
    if(uart1_bsy_flag == UART_RECEIVING) // Если запущен сеанс приема
    {
        if(SYSTIMER_Test(uart1_timers, 0) <= 0) // Проверяем таймаут. Если истек, то ...
        {
            if(uart1RxCount == 0) // На всякий случай: Если счетчик принятых байтов равен нулю
            {
                uart1_bsy_flag = UART_FREE; // УСАПП свободен
            }
            else
            {
                uart1_bsy_flag = UART_RECEIVED_DATA; // Иначе, выставляем флаг "Есть принятые данные"
                UCSR1B = 0; // Блокируем УСАПП полностью
            }
        }
    }
}

//////////////////////////////////////////////////////////////////////////
// Инициализация UART
void UART1_Init(void)
{
    UBRR1H = 0;
    UBRR1L = 103;//Bdr - 9600
    
    UART1_485_PORT &= ~UART1_485_PIN;
    UART1_485_DDR |= UART1_485_PIN;
    //Parity control disable, parity mode - none
    UCSR1C = ((1<<USBS1)|(3<<UCSZ10));
    
    UCSR1A = 0;
    UCSR1B = 0;
    
    uart1_bsy_flag = UART_FREE;
}

//////////////////////////////////////////////////////////////////////////
// Текущее состояние трансивера
uint8_t UART1_GetStatus(void)
{
    return uart1_bsy_flag;
}

//////////////////////////////////////////////////////////////////////////
uint8_t* UART1_GetRxBuffer(void)
{
    return uart1TxBuff;
}

//////////////////////////////////////////////////////////////////////////
uint8_t* UART1_GetTxBuffer(void)
{
    return uart1RxBuff;
}

//////////////////////////////////////////////////////////////////////////
void UART1_SetRxBuffer(uint8_t *buff, uint8_t sizeOfBuff)
{
    uart1RxBuff = buff;
    uart1SizeOfRxBuff = sizeOfBuff;
    UCSR1B = ((1<<RXEN1) | (1<<RXCIE1));
}

//////////////////////////////////////////////////////////////////////////
void UART1_SetTxBuffer(uint8_t *buff, uint8_t sizeOfBuff)
{
    uart1TxBuff = buff;
    uart1SizeOfTxBuff = sizeOfBuff;
}

//////////////////////////////////////////////////////////////////////////
void UART1_RxRestart(void) // Запуск приема после обработки принятых данных
{
    uart1RxCount = 0;
    uart1_bsy_flag = UART_FREE;
    UCSR1B = ((1<<RXEN1) | (1<<RXCIE1));
}

//////////////////////////////////////////////////////////////////////////
uint8_t UART1_Send(uint8_t *str, uint8_t lenght)
{
    if(uart1_bsy_flag == UART_FREE)
    {
        uart1_bsy_flag = UART_TRANSMITTING;
        uart1TxCount = 1;
        uart1TxNum = lenght;
        UART1_SetTxBuffer(str, lenght);
        UART1_485_PORT |= UART1_485_PIN;    // RS485 на передачу
        UCSR1B = ((1<<TXEN1) | (1<<UDRIE1));
        UDR1 = str[0];
        return 1;
    }
    else
    {
        return 0;
    }
}

//////////////////////////////////////////////////////////////////////////
uint8_t UART1_SendIntBuff(uint8_t lenght)
{
    if(uart1TxBuff == 0)
    {
        return 0;
    }
    
    if(((uart1_bsy_flag == UART_FREE) || (uart1_bsy_flag == UART_RECEIVED_DATA)) && (uart1TxBuff != uart1RxBuff))
    {
        uart1_bsy_flag = UART_TRANSMITTING;
        UART1_485_PORT |= UART1_485_PIN;    // RS485 на передачу
        uart1TxCount = 1;
        uart1TxNum = lenght;
        UCSR1B = (1<<TXEN1);
        UDR1 = uart1TxBuff[0];
        UCSR1B = ((1<<TXEN1) | (1<<UDRIE1));
        return 0;
    }
    else
    {
        return 1;
    }
}

//////////////////////////////////////////////////////////////////////////
uint8_t UART1ReceiveIntBuff(uint8_t *str, uint8_t lenght)
{
    if(uart1RxBuff == 0)
    {
        return 0;
    }
    else
    {
        for(uint8_t i = 0; i < uart1RxCount; i++)
        {
            if(i >= uart1SizeOfRxBuff)
            {
                return i;
            }
            else
            {
                str[i] = uart1RxBuff[i];
            }
        }
        return uart1RxCount;
    }
}

//////////////////////////////////////////////////////////////////////////
uint8_t UART1_GetRxCount(void)
{
    return uart1RxCount;
}

 
Использование:

Код: Выделить всё

#include "os_uart1.h"

***

UART1_Init();
UART1_SetTxBuffer(pnsTxBuffer, sizeof(pnsTxBuffer));
UART1_SetRxBuffer(pnsRxBuffer, sizeof(pnsRxBuffer));

***

if(UART1_GetStatus() == UART_RECEIVED_DATA)
{
    // обрабатываем данные и за тем вызываем
    UART1_RxRestart();
}

// Передача
if((UART1_GetStatus() == UART_FREE) || (UART1_GetStatus() == UART_RECEIVED_DATA))
{ // Если используется два буффера, то конструкция верна, иначе следует проверять только на UART_FREE
    pnsTxBuffer[ADDR] = pnsAddr;
    pnsTxBuffer[CMD] = SET_CTRL_ONE;
    pnsTxBuffer[DATA_SIZE] = SZ_SET_CTRL_ONE;
    pnsTxBuffer[DATA_BEGIN] = ctrl;
    pnsTxBuffer[(DATA_BEGIN + 1)] = ~ctrl;
    UART1_SendIntBuff((SZ_HEAD + SZ_SET_CTRL_ONE));
}
Разработан был под протокол, пакеты которого не имеют полей с признаками начала и конца передачи.
Сами признаки имеются, только в виде таймаутов между принятыми байтами.
Может кому и пригодится.
I am DX168B and this is my favourite forum on internet!
Реклама
Аватара пользователя
eess9
Вымогатель припоя
Сообщения: 672
Зарегистрирован: Ср фев 29, 2012 01:58:32
Откуда: Харьков, Украина

Re: Проблемы прерывания USART_RX_vect в Atmel Studio 6

Сообщение eess9 »

9600 бит в секунду, т.е. 9600/8=1200 байт в секунду. Ваше прерывание будет вызываться с частотой 1.2кГц. В промежутках между прерываниями (когда вы не в прерывании от uart) работает основная программа и другие прерывания.
Реклама
m.zdorenko
Первый раз сказал Мяу!
Сообщения: 21
Зарегистрирован: Вс май 03, 2015 14:18:46

Re: Проблемы прерывания USART_RX_vect в Atmel Studio 6

Сообщение m.zdorenko »

eess9 писал(а):9600 бит в секунду, т.е. 9600/8=1200 байт в секунду. Ваше прерывание будет вызываться с частотой 1.2кГц. В промежутках между прерываниями (когда вы не в прерывании от uart) работает основная программа и другие прерывания.
:facepalm: согласен. Что т я взял слонов и умножил на носорогов. Иногда нужно спать :))
Но смысл тот же, только выходит что время между прерываниями больше. имелось ввиду что, пытался проверить входящий и сформировать/отправить пакет исходящий в прерывании по RX. Получается что очень зря. так как довольно много времени у нас есть в основной программе. Только насколько я понимаю использование при этом кольцевого буфера вообще не нужно. Да и как то не могу представить пример - зачем он вообще нужен
Аватара пользователя
eess9
Вымогатель припоя
Сообщения: 672
Зарегистрирован: Ср фев 29, 2012 01:58:32
Откуда: Харьков, Украина

Re: Проблемы прерывания USART_RX_vect в Atmel Studio 6

Сообщение eess9 »

m.zdorenko писал(а):имелось ввиду что, пытался проверить входящий и сформировать/отправить пакет исходящий в прерывании по RX.

Действительно зря. Выставили флаги для основной программы, сохранили принятые байты в буфер и обратно в основную программу прыгаем. Хотя бы для того, чтобы не задерживать другие прерывания, а то можно и потерять парочку другую :))
m.zdorenko писал(а):Да и как то не могу представить пример - зачем он вообще нужен
Если вы отправляете в ваш uart данные с разных подпрограмм. Можно конечно синхронно их выпихивать, но это костыль. А так, если одни данные еще не передались, то новые будут добавлены в конец буфера и не будут потеряны. А uart по прерываниям организовать.
Реклама
Эиком - электронные компоненты и радиодетали
m.zdorenko
Первый раз сказал Мяу!
Сообщения: 21
Зарегистрирован: Вс май 03, 2015 14:18:46

Re: Проблемы прерывания USART_RX_vect в Atmel Studio 6

Сообщение m.zdorenko »

eess9 писал(а): Если вы отправляете в ваш uart данные с разных подпрограмм. Можно конечно синхронно их выпихивать, но это костыль. А так, если одни данные еще не передались, то новые будут добавлены в конец буфера и не будут потеряны. А uart по прерываниям организовать.
Все равно не понял. А можно как полному чайнику?
То есть если передача ведется на один приемник из нескольких (n) точек передатчиков. Тогда скорость передачи на приемник возрастет в n раз, но я думал протокол UART это протокол для общения один к одному, а не один ко многим? Что т чувствую что в последнем я ошибся =)
Реклама
Аватара пользователя
eess9
Вымогатель припоя
Сообщения: 672
Зарегистрирован: Ср фев 29, 2012 01:58:32
Откуда: Харьков, Украина

Re: Проблемы прерывания USART_RX_vect в Atmel Studio 6

Сообщение eess9 »

m.zdorenko писал(а):но я думал протокол UART это протокол для общения один к одному, а не один ко многим?
с точки зрения железа - да. связываются 2 микроконтроллера, например, один к одному. Можно сделать его и один ко многим, если интересно можете глянуть в интернете, в самом простом случае диодами линии развязываются. Но это уж очень частный случай использования UART.

Я имел ввиду использование UART кодом. Например, вы скидываете в UART информацию из разных мест: с SPI, по прерыванию таймера, опрос кнопки в главном цикле и т.д. Вам надо передать строку, например. Запихнули байт, ждем флага окончания передачи, опять запихнули следующий байт и т.д. Процессорное время очень не экономично используется, вы просто бьете баклуши пока не будет установлен байт. А ведь есть прерывание передатчика. Запихнули в буфер данные для передачи и пихнули первый байт. Все. Следующие байты будут браться из буфера в прерывании, а основная программа пошла дальше выполняться.

Еще пример из списка выше. Вы по опросу кнопки в главном цикле выпихиваете длинную строку. Также у вас есть необходимость передавать данные в UART по прерыванию таймера. Может возникнуть ситуация когда в средине передачи строки от кнопки произойдет прерывание таймера? Конечно может, что будет тоже понимаете: данные таймера влезут в средину строки. Надо запрещать прерывания от таймера на время передачи строки. А строка длинная и скорость маленькая, что будет если за это время таймер переполнится больше чем один раз? А так запретили прерывания только на время запихивания строки в буфер, это достаточно быстро делается. Данные в буфер от таймера будут запихиваться в конец. Т.е. все будет последовательно. Сначала строка целиком, потом данные таймера.
Реклама
m.zdorenko
Первый раз сказал Мяу!
Сообщения: 21
Зарегистрирован: Вс май 03, 2015 14:18:46

Re: Проблемы прерывания USART_RX_vect в Atmel Studio 6

Сообщение m.zdorenko »

Я имел ввиду разницу между просто буфером и кольцевым буфером, то есть оба массива скажем на 100 байт. И туда и туда можно запихивать данные и с ними работать по прерыванию. Оба буфера можно переполнить, в оба можно записывать пакетики но в кольцевом очередь данных поддерживать намного проще, ведь у него нет начала и конца =)
Теперь понял, спасибо.

Еще вопросы по задаю =)
С принципами работы UART разобрался. Управлением приемом передачей тож. По проверке целостности пакета данных, в принципе достаточно байта который говорит об длине всего пакета и байта с контрольной суммой crc8, теоритеческая ошибка в проверке пакета составляет 1 из 65536, даже меньше не учел признаки проверки начала и конца пакета.
Но ошибки передачи в пакетах это постоянное явление, как с ними бороться.
То что знаю на текущий момент, что нужен внешний задающий генератор частоты, кварц попросту. Этим забит весь гугл, но это только стабилизирует частоту МК.
Где то, на форуме еще кто то обронил фразу что линии RX и TX еще можно подтянуть к земле, но как это сделать что б самому не убить сигнал не говорил =(
Как еще можно вести борьбу за чистоту сигнала?
Аватара пользователя
eess9
Вымогатель припоя
Сообщения: 672
Зарегистрирован: Ср фев 29, 2012 01:58:32
Откуда: Харьков, Украина

Re: Проблемы прерывания USART_RX_vect в Atmel Studio 6

Сообщение eess9 »

а какова скорость? Если 9600 или около того, то опыту могу сказать, что не парьтесь попусту. ошибки теоретически возможны, практически маловероятны.
Для улучшения: ставите 2 стоповых бита и бит четности. Линии uart делаете максимально короткими.
m.zdorenko
Первый раз сказал Мяу!
Сообщения: 21
Зарегистрирован: Вс май 03, 2015 14:18:46

Re: Проблемы прерывания USART_RX_vect в Atmel Studio 6

Сообщение m.zdorenko »

Дело в том что, как то мучал Attiny2313A заведенную от внутреннего генератора на 8МГц питал ее напряжением 5 вольт.
Когда игрался с uart 9600 два стоп бита, начал замечать, что бывали ситуации когда вместо переданного 0x00 контроллер принимает 0x80.
Ну я правда тогда еще игрался с питанием переходника. Хочу сделать что б uart и прерывания по нему включались только когда подключен UART => USB переходник или другой контроллер.
По этому питание от переходника UART => USB подключал к ноге контроллера, нога подтянута к земле резистором, и направление вход.
mas123
Потрогал лапой паяльник
Сообщения: 312
Зарегистрирован: Вс июл 29, 2012 16:25:39

Re: Проблемы прерывания USART_RX_vect в Atmel Studio 6

Сообщение mas123 »

eess9 писал(а):9600 бит в секунду, т.е. 9600/8=1200 байт в секунду.
Я таки извиняюсь, но 9600 / 10 = 960 байт / сек.
Минимум 1 старт и 1 стоп биты присутствуют всегда.
m.zdorenko писал(а): кольцевого буфера вообще не нужно. Да и как то не могу представить пример - зачем он вообще нужен
В прерывании принятые байты записываем в буфер, в эдакий "бесконечный FIFO". Основной процесс данные выгребает и парсит их.
Не всегда есть возможность и потребность обрабатывать данные в прерывании.
m.zdorenko писал(а):целостности пакета данных, в принципе достаточно байта который говорит об длине всего пакета и байта с контрольной суммой crc8,
Поиграть "для себя" - можно вместо crc8 использовать XOR'у - быстрее выполняется и проще. :)
А при большом размере буфера crc8 уже не справляется.
m.zdorenko писал(а):Как еще можно вести борьбу за чистоту сигнала?
Экранировать линию. Вместо RS-232 использовать RS-485.
Количество старт-стоп битов тоже не просто так придумано. Еще есть режим "проверки четности", когда сам UART пытается проверить достоверность каждого принятого байта.
А вообще быть готовым, что ошибки были, есть и будут всегда. :)
Ответить

Вернуться в «AVR»