Перейти к содержанию
    

Всем привет.

Разбираюсь в UARTe на STM32F100.

 

UART запутился, передачу и прием реализовал на прерываниях по окончанию передачи и приему.

В связи с этим есть вопросы:

 

Необходимо: Сделать передачу сообщения по событию, например - установке флага при нажатии на кнопку. (в программе message_must_send=1)

 

Проблема: Если реализовать это в прерывании, то прерывание по окончанию передачи просто напросто не генерируется и не происходит передача.(даже в обработчик не заходит, смотрел в отладчике) Передача начинается только после того, как был отправлен хотя бы один байт вне обработчика прерывания.

Т.е. нажимаю кнопкку на отладочной - ничего не передается, пока через терминал не пошлю любое примитивное соообщение(возвращается эхо, где и происходит отправка сообщения).

 

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

 

Код ниже прилагается:

Инициализация:

//инициализация юарта
void init_uart()
    {
    RCC->APB2ENR|= RCC_APB2ENR_AFIOEN; // Тактирование альтернативных функций GPIO.
    RCC->APB2ENR|= RCC_APB2ENR_USART1EN; // Включение тактирования USART1.
    GPIOA->CRH |= GPIO_CRH_MODE9; // Вывод TX PA.9 - на выход.
    GPIOA->CRH &=~GPIO_CRH_CNF9; GPIOA->CRH |=GPIO_CRH_CNF9_1; // Альтернативный выход.
    USART1->CR1 |=(USART_CR1_RE | USART_CR1_TE); // Разрешить выводы RX, TX.
          // Скорость 9.6 kbps. USARTDIV=FSYS/(16*baud) = 24e6/(16*9600)=156.25
    USART1->BRR=(156<<4); // Целая часть коэффициента деления USART1. 
    USART1->BRR|=4; // Дробная часть*16 = 0,25*16 = 4.
    USART1->CR1 |=USART_CR1_UE; // Включение USART1.
    USART1->CR1 |=USART_CR1_TCIE|USART_CR1_RXNEIE; // Разрешить прерывания TC, RXNE.
    NVIC_EnableIRQ(USART1_IRQn); // Разрешить прерывание USART1_IRQn в NVIC.
    NVIC_SetPriority(USART1_IRQn, 3); //задать приоритет прерыванию
    }

 

Обработчик:

void USART1_IRQHandler (void) // Обработчик прерывания USART1.
     {
          if (USART1->SR & USART_SR_RXNE) USART1->DR=USART1->DR;  // Если прерывание по приёму, то возвращаем эхо
          if (USART1->SR & USART_SR_TC) // Если прерывание по завершению передачи.
          {
            if(message_must_send==1)
            {
               if (message[tmp]) 
                {
                USART1->DR=message[tmp]; 
                tmp++;
                }
               else 
                {
                tmp=0;
                message_must_send=0;
                }
            }
            USART1->SR&=~(USART_SR_TC|USART_SR_RXNE); // Очистить флаги прерывания.
          }
     }

 

 

Теперь вопросы:

1. Есть ли какой-то способ программно сгенерировать прерывание по окончанию передачи? Т.е. установить в статус-регистре SR бит ТС? Перерыл всю документацию - такой инфы не нашел.

2. Каким образом лучше производить передачу сообщения - в обработчике прерывания или же в основном цикле?

3. Если описать передачу в основном цикле - есть ли смысл в прерывании по окончанию передачи? Ведь можно просто отслеживать с таким же успехом состояние битов в статус-регистре.

 

Возможно, получилось несколько сбито и сумбурно, но столько вопросов и предложений вертится в голове, что сразу все и не вспомнишь. Буду очень благодарен за обьяснения и советы.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вместо прерывания по окончанию передачи удобнее использовать прерывание по опустошению передатчика (TXE).

Тогда отправка символа выглядит так:

 

void putch(char ch)
{
    TxChannel.push(ch);
    enable_tx_interrupt();
}

а обработчик прерывания - вот так:

void USART1_IRQHandler (void)
{
    uint16_t status = USART1->SR;
    if (status & USART_SR_RXNE)
    {
        uint8_t ch = USART1->DR;
        if (RxChannel.get_free_size())
            RxChannel.push(ch);
    }

    if (status & USART_SR_TXE)
    {
        if (TxChannel.get_count())
        {
            char ch = 0;
            TxChannel.pop(ch);
            USART1->DR = ch;
        }
        else
        {
            disable_tx_interrupt();
        }
    }
}

Запрет/разрешение прерывания по опустошению передатчика:

inline void disable_tx_interrupt() { USART1->CR1 &= ~USART_CR1_TXEIE; }
inline void enable_tx_interrupt() { USART1->CR1 |= USART_CR1_TXEIE; }

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Необходимо: Сделать передачу сообщения по событию, например - установке флага при нажатии на кнопку. (в программе message_must_send=1)

Помоему нужно сформировать прерывание по нажатию кнопки (изменению сигнала на пине ) и там начать передачу первого байта передать, а остальные уже по вашей технологии через окончание передачи. Можно при помощи DMA (автоматом все нужные байты передадутся из памяти в UART). Можно послать первый байт там, где формируется message_must_send=1, если передатчик пуст, а если он занят, то сработает прерывание завершения передачи и дальше Ваш метод сработает.

1. Есть ли какой-то способ программно сгенерировать прерывание по окончанию передачи? Т.е. установить в статус-регистре SR бит ТС? Перерыл всю документацию - такой инфы не нашел.

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

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Дружище, а подскажите какая у вас среда разработки и были-ли проблемы запустить STM32?

Сам я к ST взор обратил недавно. Разобрался уже с STM8, а вот с STM32 проблемы.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вместо прерывания по окончанию передачи удобнее использовать прерывание по опустошению передатчика (TXE).

Тогда отправка символа выглядит так:

 

void putch(char ch)
{
    TxChannel.push(ch);
    enable_tx_interrupt();
}

а обработчик прерывания - вот так:

...

Запрет/разрешение прерывания по опустошению передатчика:

inline void disable_tx_interrupt() { USART1->CR1 &= ~USART_CR1_TXEIE; }
inline void enable_tx_interrupt() { USART1->CR1 |= USART_CR1_TXEIE; }

Спасибо большое за совет. Действительно, с таким прерыванием отлично заработало, как предпологалось изначально.

 

Сначала прочитал про него, подумал что полный аналог прерывания по окончанию передачи. Продолжил ковырять со старым прерыванием. Пока экспериментировал проследил в дебаге как меняются все регистры состояния и контроля юарта, вот там то и заметил, что когда буфер передатчика пуст - флаг TXE всегда установлен, и как только разрешено прерывание - оно сразу генерируется, в виду пустого передатчика.

В отличии от флага окончания передачи, который до прерывания сброшен.

 

Помоему нужно сформировать прерывание по нажатию кнопки (изменению сигнала на пине ) и там начать передачу первого байта передать, а остальные уже по вашей технологии через окончание передачи. Можно при помощи DMA (автоматом все нужные байты передадутся из памяти в UART). Можно послать первый байт там, где формируется message_must_send=1, если передатчик пуст, а если он занят, то сработает прерывание завершения передачи и дальше Ваш метод сработает.

У меня антидребезг еще программно реализован с помощью прерываний таймера.

А передавать первый байт с установкой флага - мне казалось каким-то извратом. В общем сейчас все заработало как хотелось, что не может не радовать.

DMA контроллер - вещь весьма полезная, надо будет с нею разобраться.

 

 

Дружище, а подскажите какая у вас среда разработки и были-ли проблемы запустить STM32?

Сам я к ST взор обратил недавно. Разобрался уже с STM8, а вот с STM32 проблемы.

Среда разработки - IAR 6.21.

Проблемы, конечно же, были. Куда без этого?!))

Были проблемы и с работой IARа, т.к. в новая версия(6.21) не хотела работать с внешними хидерами core_cm3.h, и с включением прерываний(пытался их включить, не добавив в проект файл с заголовками прерываний- startup для моего контроллера).

 

Но, для начала, хочу вам обьяснить, каким вообще образом можно создавать проекты для данных контроллеров:

Спасоб 1 : читая даташит и управляя всеми необходимыми регистрами для запуска и управления периферией(в этом случае используются только те стандартные библиотеки, где описываются бит-маски, обработчики прерываний и т.п.).

Спасоб 2 : используя стандартные библиотеки для работы с периферией(STM32F10x_StdPeriph), где, в принципе, интуитивно понятно происходит работа с периферией. Хотя, даташит, скорее всего, читать все же прийдется. Многие ругаются на её глючность.

 

Я покаместь использую только первый спасоб.

 

Для начала советую помотреть видео с данного ресурса:

http://bsvi.ru/category/embedded/arm/

Там небольшое вступление по документации и настройка ИАРа плюс создание простейшего проекта. Видео на русском, лишним однозначно не будет.

 

Много информации(статьи с примерами) есть на ресурсах:

http://easyelectronics.ru/ и http://we.easyelectronics.ru/

Поиск по тегам : STM32

 

http://eugenemcu.ru/publ/13 - тоже хороший ресурс

 

В принципе, почти все эти статьи - это переводы даташитов + примеры. Тем не менее, мне приятнее читать на родном языке. Потом я все равно еще пересматриваю описание работы нужной периферии в ДШ.

 

Всем еще раз спасибо за помощь.

Изменено пользователем ierofant

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

STM32F10x_StdPeriph использую уже пару лет, с 1 по 3 версию. Ни разу не наткнулся на баг.

Все там прозрачно - исходники же есть (и видно что как происходит если есть какие то сомнения).

А конфигурировать даже GPIO через регистры вручную это абсолютно неповоротливо, если только не начать писать собственную STM32F10x_StdPeriph, но это - "велосипед".

С чем нужно разобраться, чтобы начать писать под STM32 в порядке приоритета:

1. Субмодуль тактировоания RCC - надо понять на 100% хотя бы идеологию.

(после этого худо бедно можно что то написать - типа мигание диодом)

2. Модуль прерываний NVIC.

в общем то и ВСЕ!

Остальное - уже периферия, которая изучается по мере надобности в ней.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Но этот запрет НЕ СБРАСЫВАЕТ ЭТОТ ФЛАГ.

Поэтому, если в прерывание мы попали из-за приемника , то ветка обработки-анализа нужно ли что- нибудь передавать, всегда активна, на ее анализ тратится время. Это следует учитывать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Среда разработки - IAR 6.21.

Проблемы, конечно же, были. Куда без этого?!))

 

Для начала советую помотреть видео с данного ресурса:

http://bsvi.ru/category/embedded/arm/

Там небольшое вступление по документации и настройка ИАРа плюс создание простейшего проекта. Видео на русском, лишним однозначно не будет.

Спасибо, буду разбираться. Видео очень ценное. О многом просто не догадывался.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Но этот запрет НЕ СБРАСЫВАЕТ ЭТОТ ФЛАГ.

Поэтому, если в прерывание мы попали из-за приемника , то ветка обработки-анализа нужно ли что- нибудь передавать, всегда активна, на ее анализ тратится время. Это следует учитывать.

Какой выход предлагаете?

Ведь TXE флаг-то сбрасывается только записью нового байта в data_reg или есть ещё какой-то способ?

Может трансмиттер вообще отключать, но тогда нужно ожидать ТXC флага.

Я так на AVR делал (по UDRE тх-фифо выгребал, по TXC отключал передатчик - работало красиво).

Только тут всё это не поможет - прерывание одно, поэтому число условий останется прежним:(

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Какой выход предлагаете?

Можно сначала проверять состояние разрешения/запрета прерываний от TXE, (если каждый раз проверять наличие символов на передачу накладно):

void USART1_IRQHandler (void) {
    uint16_t status = USART1->SR;
    if (status & USART_SR_RXNE) {
      ...
    }

    if (USART1->CR1 & USART_CR1_TXEIE)
    if (status & USART_SR_TXE)) {
        ..
    }
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Повторил пример из видеоурока. Всё заработало.

Попытался использовать функции из библиотеки подключив stm32f10x_gpio.h, стало выдавать ошибку линковщика

Error[Li005]: no definition for "assert_param"

 

файл конфигурации stm32f10x_conf.h с определением данного макроса в проект включил не пойму чего ему не нравится.

 

Error[Li005]: no definition for "assert_param" Проблема решена.

Для таких же мучеников, как я сообщаю.

Нужно добавить в опциях проекта в разделе Preprocessor настроек C/C++ Compiler строку с путем к файлу stm32f10x_conf.h в строке ввода "Preinclude file"

Изменено пользователем Apollo

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Можно сначала проверять состояние разрешения/запрета прерываний от TXE, (если каждый раз проверять наличие символов на передачу накладно):
Можно так, только всё равно третье условие в обработчике появляется.

А можно и так (и из-за tail-chaining на cm3 будет работать очень даже оптимально и при одновременном приходе RX и TX прерываний):

static __inline void uart_isr(uart_t* const uart)
{
    uint_fast16_t status = uart->sfr->SR;

    if (status & USART_FLAG_RXNE)        // if RX data_reg isn't empty (auto-clr by reading data_reg)
    {  
        uart_rx_isr(uart, status);
    }
    else if (status & USART_FLAG_TXE)      // if TX data_reg is empty (auto-clr by writing data_reg)
    {
        uart_tx_isr(uart);
    }
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Сдаётся мне, что и с tail-chaining-ом будет проигрыш варианту с тремя проверками:)

Но так красивее, да.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Сдаётся мне, что и с tail-chaining-ом будет проигрыш варианту с тремя проверками :)

Да но одновременный приход TX+RX прерываний скорее исключение чем правило, мне так думается:)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот уже несколько лет работаю с STM32 и его различной периферией.

А сейчас понадобился RS485 и модбас. Ну думаю раз плюнуть. Делал такое на AVR...

Состряпал я программный модуль-драйвер UARTа с режимом RS485 и словил непонятное поведение.

При отправке байта получаю его ЭХО (loop-back режим). Иногда такое нужно но не сейчас.

Вообщем вопрос... Это я чего-то не понимаю или так и должно быть (что сильно вряд ли).

static INLINE void rs485_set_txe_int(rs485_handle_t* const rs, int x)
{
    if (x)  rs->sfr->CR1 |=  USART_CR1_TXEIE;
    else    rs->sfr->CR1 &= ~USART_CR1_TXEIE;
}

static INLINE void rs485_set_txc_int(rs485_handle_t* const rs, int x)
{
    if (x)  rs->sfr->CR1 |=  USART_CR1_TCIE;
    else    rs->sfr->CR1 &= ~USART_CR1_TCIE;
}

static INLINE void rs485_txe_isr(rs485_handle_t* const rs)
{
    *rs->de = 1;
    if (!fifo_get_byte(&rs->fifo.tx, (uint8_t*)&rs->sfr->DR))
    {
        rs485_set_txc_int(rs, 1);
        rs485_set_txe_int(rs, 0);
    }
}

static INLINE void rs485_txc_isr(rs485_handle_t* const rs)
{
    rs->sfr->SR = (uint16_t)~USART_SR_TC; // clr pending txc
    *rs->de = 0;
}

static INLINE void rs485_isr(rs485_handle_t* const rs)
{
    uint_fast16_t status = rs->sfr->SR;
    if (status & USART_SR_RXNE)      // if RX data_reg isn't empty (auto-clr by reading data_reg)
    {  
        rs485_rx_isr(rs, status);
    }
    else if (status & USART_SR_TC)   // if TC  tx-complete flag (needs to be clr by user)
    {
        rs485_txc_isr(rs);
    }
    else if (status & USART_SR_TXE)  // if TX data_reg is empty (auto-clr by writing data_reg)
    {
        rs485_txe_isr(rs);
    }
}

void rs485_putc(rs485_handle_t* const rs, char c)
{
    fifo_put_byte(&rs->fifo.tx, (uint8_t)c);
    rs485_set_txe_int(rs, 1);
}

int rs485_init(rs485_handle_t* const rs, uint32_t br, uint_fast8_t data_bits, char parity, uint_fast8_t stop_bits, volatile uint32_t* de)  // br-(7,8,9)-(n,e,o)-(1,2)
{
    USART_DeInit(rs->sfr);

    rs->de = de;

    data_bits += (parity=='e' || parity=='o'); // in stm32 USART data_bits means data_bits+parity_bit

    if (stop_bits==2 && data_bits==7) 
    {
//        stop_bits--;  // 1
//        data_bits++;  // 8

        return 0; //todo: 7bit + 2SB convert to 8bit and 1SB and put manually SB into Bit7=1 before send and
    }

    if (   (br>=9600UL && br<=115200UL)       // guard_time timits
        && (data_bits==8U || data_bits==9U))  // 7bit w/o parity is forbiden !!!
    {
        rs485_init_rcc(rs);

        rs485_flush(rs);

        USART_InitTypeDef USART_InitStruct =
        {
            .USART_BaudRate            = br,
            .USART_WordLength          = (data_bits==9U)? USART_WordLength_9b : USART_WordLength_8b,
            .USART_StopBits            = (stop_bits==2U)? USART_StopBits_2 : USART_StopBits_1,
            .USART_Parity              = (parity=='e')? USART_Parity_Even : ((parity=='o')? USART_Parity_Odd : USART_Parity_No),
            .USART_Mode                = USART_Mode_Tx | USART_Mode_Rx,
            .USART_HardwareFlowControl = USART_HardwareFlowControl_None
        };

        USART_Init(rs->sfr, &USART_InitStruct);

        USART_ITConfig(rs->sfr, USART_IT_RXNE, ENABLE); // rx-interrupt only allowed by default

        USART_Cmd(rs->sfr, ENABLE);

        rs485_init_nvic(rs); // enable irq

        return 1;
    }

    return 0;
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...