ierofant 0 27 октября, 2011 Опубликовано 27 октября, 2011 · Жалоба Всем привет. Разбираюсь в 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. Если описать передачу в основном цикле - есть ли смысл в прерывании по окончанию передачи? Ведь можно просто отслеживать с таким же успехом состояние битов в статус-регистре. Возможно, получилось несколько сбито и сумбурно, но столько вопросов и предложений вертится в голове, что сразу все и не вспомнишь. Буду очень благодарен за обьяснения и советы. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 15 28 октября, 2011 Опубликовано 28 октября, 2011 · Жалоба Вместо прерывания по окончанию передачи удобнее использовать прерывание по опустошению передатчика (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; } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Tolyaha 1 28 октября, 2011 Опубликовано 28 октября, 2011 · Жалоба Необходимо: Сделать передачу сообщения по событию, например - установке флага при нажатии на кнопку. (в программе message_must_send=1) Помоему нужно сформировать прерывание по нажатию кнопки (изменению сигнала на пине ) и там начать передачу первого байта передать, а остальные уже по вашей технологии через окончание передачи. Можно при помощи DMA (автоматом все нужные байты передадутся из памяти в UART). Можно послать первый байт там, где формируется message_must_send=1, если передатчик пуст, а если он занят, то сработает прерывание завершения передачи и дальше Ваш метод сработает. 1. Есть ли какой-то способ программно сгенерировать прерывание по окончанию передачи? Т.е. установить в статус-регистре SR бит ТС? Перерыл всю документацию - такой инфы не нашел. Я думаю невозможно, это осуществляется аппаратно нужно чтобы передача пошла и остановилась (сперва передатчик должен быть загружен, а когда он опустошится и не загрузятся новые данные, тогда и возникнет это прерывание). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Apollo 0 28 октября, 2011 Опубликовано 28 октября, 2011 · Жалоба Дружище, а подскажите какая у вас среда разработки и были-ли проблемы запустить STM32? Сам я к ST взор обратил недавно. Разобрался уже с STM8, а вот с STM32 проблемы. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ierofant 0 28 октября, 2011 Опубликовано 28 октября, 2011 (изменено) · Жалоба Вместо прерывания по окончанию передачи удобнее использовать прерывание по опустошению передатчика (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 - тоже хороший ресурс В принципе, почти все эти статьи - это переводы даташитов + примеры. Тем не менее, мне приятнее читать на родном языке. Потом я все равно еще пересматриваю описание работы нужной периферии в ДШ. Всем еще раз спасибо за помощь. Изменено 28 октября, 2011 пользователем ierofant Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
kan35 7 29 октября, 2011 Опубликовано 29 октября, 2011 · Жалоба STM32F10x_StdPeriph использую уже пару лет, с 1 по 3 версию. Ни разу не наткнулся на баг. Все там прозрачно - исходники же есть (и видно что как происходит если есть какие то сомнения). А конфигурировать даже GPIO через регистры вручную это абсолютно неповоротливо, если только не начать писать собственную STM32F10x_StdPeriph, но это - "велосипед". С чем нужно разобраться, чтобы начать писать под STM32 в порядке приоритета: 1. Субмодуль тактировоания RCC - надо понять на 100% хотя бы идеологию. (после этого худо бедно можно что то написать - типа мигание диодом) 2. Модуль прерываний NVIC. в общем то и ВСЕ! Остальное - уже периферия, которая изучается по мере надобности в ней. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Serj78 0 29 октября, 2011 Опубликовано 29 октября, 2011 · Жалоба Есть при использовании TXE один нюанс, связанный с оценкой времени обработки прерываний. Дело в том, что внутри обработчика прерываний мы проверяем, выставлен ли флаг TXE ? и если нам передавать нечего- запрещаем генерацию прерываний по TXE. Но этот запрет НЕ СБРАСЫВАЕТ ЭТОТ ФЛАГ. Поэтому, если в прерывание мы попали из-за приемника , то ветка обработки-анализа нужно ли что- нибудь передавать, всегда активна, на ее анализ тратится время. Это следует учитывать. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Apollo 0 31 октября, 2011 Опубликовано 31 октября, 2011 · Жалоба Среда разработки - IAR 6.21. Проблемы, конечно же, были. Куда без этого?!)) Для начала советую помотреть видео с данного ресурса: http://bsvi.ru/category/embedded/arm/ Там небольшое вступление по документации и настройка ИАРа плюс создание простейшего проекта. Видео на русском, лишним однозначно не будет. Спасибо, буду разбираться. Видео очень ценное. О многом просто не догадывался. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
demiurg_spb 0 31 октября, 2011 Опубликовано 31 октября, 2011 · Жалоба Есть при использовании TXE один нюанс, связанный с оценкой времени обработки прерываний. Дело в том, что внутри обработчика прерываний мы проверяем, выставлен ли флаг TXE ? и если нам передавать нечего- запрещаем генерацию прерываний по TXE. Но этот запрет НЕ СБРАСЫВАЕТ ЭТОТ ФЛАГ. Поэтому, если в прерывание мы попали из-за приемника , то ветка обработки-анализа нужно ли что- нибудь передавать, всегда активна, на ее анализ тратится время. Это следует учитывать. Какой выход предлагаете? Ведь TXE флаг-то сбрасывается только записью нового байта в data_reg или есть ещё какой-то способ? Может трансмиттер вообще отключать, но тогда нужно ожидать ТXC флага. Я так на AVR делал (по UDRE тх-фифо выгребал, по TXC отключал передатчик - работало красиво). Только тут всё это не поможет - прерывание одно, поэтому число условий останется прежним:( Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 15 31 октября, 2011 Опубликовано 31 октября, 2011 · Жалоба Какой выход предлагаете? Можно сначала проверять состояние разрешения/запрета прерываний от 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)) { .. } } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Apollo 0 31 октября, 2011 Опубликовано 31 октября, 2011 (изменено) · Жалоба Повторил пример из видеоурока. Всё заработало. Попытался использовать функции из библиотеки подключив 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" Изменено 31 октября, 2011 пользователем Apollo Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
demiurg_spb 0 31 октября, 2011 Опубликовано 31 октября, 2011 · Жалоба Можно сначала проверять состояние разрешения/запрета прерываний от 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); } } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 15 31 октября, 2011 Опубликовано 31 октября, 2011 · Жалоба Сдаётся мне, что и с tail-chaining-ом будет проигрыш варианту с тремя проверками:) Но так красивее, да. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
demiurg_spb 0 31 октября, 2011 Опубликовано 31 октября, 2011 · Жалоба Сдаётся мне, что и с tail-chaining-ом будет проигрыш варианту с тремя проверками :) Да но одновременный приход TX+RX прерываний скорее исключение чем правило, мне так думается:) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
demiurg_spb 0 5 апреля, 2013 Опубликовано 5 апреля, 2013 · Жалоба Вот уже несколько лет работаю с 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; } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться