Продолжаем последовательно настраивать все периферийные модули микроконтроллеров STM32 при помощи STM32CubeMx. Вот ссылка на все статьи курса - ссылка - а сегодня у нас на очереди инициализация модуля USART в STM32, а также прием и передача данных при помощи вышеупомянутого протокола UART. Начнем с небольшого экскурса в теоретические аспекты.
Теоретическая часть.
Обзор модуля UART.
Что такое UART в целом, и зачем он нужен думаю объяснять не надо 🙂 Так что перейдем сразу к реализации в STM32. Предлагаю сначала посмотреть, какие регистры за что отвечают, а потом уже набросать какой-нибудь проект для наглядности.
Небольшое лирическое отступление... Как вы уже догадываетесь, модуль USART в STM32, как и все остальное в этих контроллерах, имеет множество настроек и режимов. Тут тебе и обычный прием/передача и поддержка LIN (об этом протоколе как-нибудь поговорим отдельно). И если в AVR частенько приходилось реализовывать программные UART’ы, поскольку аппаратных просто не хватало, то, к примеру, в STM32F103CB их как минимум три штуки! А это уже немало. Итак, начинаем копаться в даташите.
Регистр USART_SR.
Статусный регистр. Тут содержатся флаги, отражающие текущее состояние дел, то есть текущее состояние модуля USART. Поглядим на некоторые из них поближе:
- LBD - Lin Break Detection flag. Выставляется при обнаружении брейка при использовании LIN (о том, что это такое обязательно расскажу в статье про LIN).
- TXE - Transmit data register empty. Регистр данных пуст, пора его заполнить.
- TC - Transmission complete. Передача завершена.
- RXNE - Read data register not empty. Приемный регистр не пуст, надо срочно прочитать данные.
Есть там еще 4 флага для классификации ошибок – ошибка кадра, наличие шума, ошибка переполнения и т. д.
Регистр USART_DR.
Регистр данных. Именно тут мы будем забирать ценнейшую информацию, которая к нам пришла извне, сюда же будем записывать свои данные.
Регистр USART_BRR.
Здесь настраивается скорость обмена, которую мы хотим получить.
Регистр USART_CR1.
Регистр контроля – контролирует весь процесс. Биты по порядку:
- UE - разрешение работы USART
- M - длина посылки: 0 – 8 бит данных, 1 – 9 бит данных
- Wake - будильник для USART’a – то есть метод его пробуждения
- PCE - контроль паритета – ВКЛ или ВЫКЛ
- PS - тип четности: 0 – четный, 1 – нечетный
- PEIE - разрешение прерывания при обнаружении ошибки четности
- TXEIE - прерывание от TXE
- RXNEIE - прерывание от RXNE
Ну и там еще парочка битов, разрешающих/запрещающих прием/передачу, а также бит для отправки брейка. В общем, это все есть в даташите, все наглядно, красиво и понятно. Поэтому подробно углубляться не буду, нет в этом смысла.
Регистр USART_CR2 - в этом регистре такие биты как включение/выключение режима работы по LIN, установка количества стоп-битов, настройки clock’а, установка параметров LIN брейка и некоторые другие.
Регистр USART_CR3 - а здесь биты для использования DMA и SmartCard.
Регистр USART_GTPR - биты, отвечающие за предделитель.
В общем, вот они, 7 регистров, которые контролируют весь модуль USART в STM32. У меня описаны довольно поверхностно, только основное, если что, спрашивайте в комментариях, буду рад помочь.
Плавно переходим к практической части в виде небольшого базового примера. Забегая вперед скажу, что в данном случае проект будет действительно несложный, ознакомительный, а вот еще парочка материалов для STM32 с использованием UART:
- Драйвер протокола LIN для микроконтроллеров на базе UART.
- Библиотека для работы с шиной 1-Wire на STM32.
- Modbus RTU Slave. Пример реализации на микроконтроллере STM32.
- Modbus RTU Master. Библиотека для микроконтроллеров STM32.
Практическая часть.
Инициализация в STM32CubeMx.
Сегодня же для тестирования реализуем следующее - будем передавать данные при помощи USART2, а принимать посредством USART1. Таким образом, сравнив отправляемые через один и принятые другим модулем данные, мы сможем убедиться, правильно ли функционирует приемопередатчик. Я буду использовать отладочную плату STM32F4Discovery, но это не особо важно, так как механизм взаимодействия с CubeMx по сути один и тот же для любого контроллера и семейства.
Для того, чтобы осуществить обмен данными, необходимо соединить выводы Rx/Tx USART1 с аналогичными выводами USART2. Только не забываем, что Rx одного идет на Tx второго и наоборот:
В итоге коммутируем так:
- PA2 (USART2_TX) - PA10 (USART1_RX)
- PA3 (USART2_RX) - PA9 (USART1_TX)
С электрико-физическими подключениями разобрались, переходим к работе непосредственно в STM32CubeMx. Создаем новый проект и активируем нужные нам модули USART1 и USART2, установив для них режим работы:
Здесь же доступны для изменения классические параметры UART:
- скорость передачи данных
- длина пакета
- четность
- количество стоп-битов
- направление обмена данными
- over sampling
Для нашего тестового проекта это не очень важно, важно лишь, чтобы настройки для USART1 и USART2 были одинаковыми. Поэтому аналогичным же образом конфигурируем и второй модуль. На вкладке "NVIC_Settings" также активируем прерывание:
На этом работу с STM32CubeMx заканчиваем, генерируем код и открываем проект. Вся инициализация портов и модулей USART уже включена в функцию main()
:
/* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART1_UART_Init(); MX_USART2_UART_Init();
Кроме того, CubeMx сгенерировал обработчики прерываний, в которых реализованы механизмы обмена данными и сброса всех нужных флагов - все это находится внутри функции HAL_UART_IRQHandler()
. Сами же обработчики в файле stm32f4xx_it.c:
/** * @brief This function handles USART2 global interrupt. */ void USART2_IRQHandler(void) { /* USER CODE BEGIN USART2_IRQn 0 */ /* USER CODE END USART2_IRQn 0 */ HAL_UART_IRQHandler(&huart2); /* USER CODE BEGIN USART2_IRQn 1 */ /* USER CODE END USART2_IRQn 1 */ } /** * @brief This function handles USART1 global interrupt. */ void USART1_IRQHandler(void) { /* USER CODE BEGIN USART1_IRQn 0 */ /* USER CODE END USART1_IRQn 0 */ HAL_UART_IRQHandler(&huart1); /* USER CODE BEGIN USART1_IRQn 1 */ /* USER CODE END USART1_IRQn 1 */ }
Прием и передача данных по UART.
Для приема и отправки данных в HAL реализованы следующие функции:
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout); HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout); HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
Из их названия, в принципе, уже понятно, какие механизмы будут использованы:
- прием и передача в обычном режиме
- прием и передача с использованием прерываний
- прием и передача посредством DMA
Поскольку в нашем проекте кроме работы с UART больше ничего не будет, особо оценить преимущества работы на прерываниях мы не сможем, но тем не менее осуществим обмен именно таким образом. Кроме того, объявим в файле main.c массивы (transmitBuffer[]
, receiveBuffer[]
) и их размер (BUFFER_SIZE
) для отправляемых и принимаемых данных и в итоге получаем следующее:
/* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ #define BUFFER_SIZE 32 /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ UART_HandleTypeDef huart1; UART_HandleTypeDef huart2; /* USER CODE BEGIN PV */ uint8_t transmitBuffer[BUFFER_SIZE]; uint8_t receiveBuffer[BUFFER_SIZE]; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_USART1_UART_Init(void); static void MX_USART2_UART_Init(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART1_UART_Init(); MX_USART2_UART_Init(); /* USER CODE BEGIN 2 */ for (unsigned char i = 0; i < BUFFER_SIZE; i++) { transmitBuffer[i] = i + 1; receiveBuffer[i] = 0; } HAL_UART_Receive_IT(&huart1, receiveBuffer, BUFFER_SIZE); HAL_UART_Transmit_IT(&huart2, transmitBuffer, BUFFER_SIZE); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }
Собираем проект, зашиваем контроллер и проверяем работоспособность программы. Для этого запускаем отладку и смотрим, что у нас попадает в буфер receiveBuffer[]
:
Принятые данные в точности соответствуют отправленным👍 Для того, чтобы отследить момент окончания передачи, либо приема существуют callback-функции:
HAL_UART_TxCpltCallback()
HAL_UART_RxCpltCallback()
Их можно, к примеру, переопределить прямо в main.c:
/* USER CODE BEGIN 4 */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART1) { // USART1 завершил прием данных } } void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART2) { // USART2 завершил отправку данных } } /* USER CODE END 4 */
В эти функции программа попадет автоматически при возникновении соответствующих событий, то есть окончания процесса отправки, либо приема - типичный механизм для библиотеки HAL.
И на этом на сегодня завершаем деятельность, знакомство с UART в STM32 осуществлено, спасибо за внимание, и до скорого 🤝
Добрый день! Подскажите а как принимать данные побайтно в прерывании?
Можно непосредственно из регистра читать
Еще вопросик) а как узнать что прием 32 байт уже закончен?
Заранее благодарен.
Счетчик заведи и суммируй в прерывании - все по старинке ) Либо, если через HAL - там в структуре huart насколько я помню есть поле, в котором количество принятых байт записано.
если по хорошему, то надо переопределить функции
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
они автоматически вызываются из прерываний USART, когда обработан последний байт из числа указанных в поле длинны. (в статье это 32).
Еще раз отмечу что функции вызываются обработчиком прерываний
HAL_UART_IRQHandler(UART_HandleTypeDef *huart); поэтому код в колл-бэках должен быть минимальный. Выставляйте глобальные флаги или выдавайте семафоры в случае с работай с ОС.
На мой взгляд эти call-бэки не слишком удобны. Может у меня просто такие задачи обычно, что требуется на лету анализировать данные принятые, а не только после принятия всей посылки. Поэтому все вручную в прерываниях.
Спасибо) как непривычно после Standard Peripheral Library
А не подскажите как запустить debug?
Компилируя проект в CubeMX отладчик отваливается.
BOOT_0 подтянут к земле, разрешается запуск программы, если собрать обычный проект, то отладчику пофиг запущена программа или в режиме загрузчика.
Если собираю в CubeMX, то в дальнейшем загрузку можно сделать только через загрузчик, т.е. убрать перемычку с BOOT_0. Соотвественно и отладка не работает.
Камень STM32F107RB
а вы попробуйте назначить программатору его законные ноги в контроллере при инициализации. пункт из дерева SYS
Прошу совета. Проблема связана с применением УАРТа для передачи данных с АЦП. Результат при вызове HAL_ADC_GetValue 32х битный (из которых я беру 16), а аргумент pData в HAL_UART_Transmit - 8ми битный. Передаю побайтно. В итоге в терминале на ПК мешанина из подряд идущих байт, которую нужно делить на парные байты. Может как-то можно по-людски организовать так, чтобы в терминале было, к примеру не 00000111 11100110 а соответствующее число 2022?
00000111 11100110 - как то это не похоже совсем на 2022 )
А вообще это же от терминала зависит и от его настроек отображения.
00000111 11100110 - это одно число, двухбайтовое (или как там его правильно BCD что-ли), Так что это как раз 2022. Т.е. это надо найти прогу-терминал которая это правильно интерпретирует?
все, нашел подходящий терминал
Приветствую уважаемые знатоки. Решаю похожую задачу, только с SPI. Пытаюсь связать два микроконтроллера STM32. Интересует следующее, хочу проверить, попадает ли контроллер в прерывание при приеме данных. Видимо оно называется SPI1_IRQHandler. Не ясен механизм. При при обработке прерывания таймера (TIM3_IRQHandler к примеру) понятно, что тело функции пишем мы сами. А тут оно как-бы уже должно быть, дак где же посмотреть его тело и соответственно воткнуть туда точку останова для проверки. Извиняюсь за примитивные вопросы, заранее спасибо за помощь.
Если используешь Cube, то он генерирует прерывание, внутри которого вызывает свою функцию обработчик - ставишь на ней брейкпоинт и смотришь попадает туда или нет. Само прерывание Cube помещает в файл stm32fxxxx_it.c, как и все остальные прерывания.
Спасибо, нашел. Тогда еще вопрос. Пытаюсь на более простом примере еще разобраться. Плата discoveryf4, настраиваю SPI1 как master, SPI2 как slave. Посмотрел пример. Там сначала вне бесконечного цикла инициируется прерывание для SPI2 , а в цикле идет передача данных по SPI1. И в таком виде ничего не работает. Если беру инициализацию прерывания и добавляю в бесконечный цикл начинает что-то работать. В чем тут смысл и как вообще это правильно настроить, чтоб понятно было, что за чем следует. Заранее спасибо
Ну что тут сказать...Правильно так - инициализация один раз, отправка столько сколько надо раз.
А зачем нужна вот эта конструкция? Видел ее в примере.
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
if (hspi->Instance == hspi2.Instance)
{
HAL_SPI_Receive_IT(&hspi2, (uint8_t*) data_get, 1);
}
}
С нем все работает вообще дико странно. Результат получается половина от того, что я отправлял. Как такое может быть?
Ну тут видимо по окончанию приема запускается новый прием
Сталкивался с этим: куб не включает выводы арма для jtag - ручками в куб-проекте отредактируй их как используемые для jtag. Тогда отладка и программирование заработает. А прошить можешь так: зажать ресет, запустить программирование и отпустить ресет.
Снова у меня вопрос. Ситуация все та же. STM32f4 - Master, STM32f100 - Slave. Настроил передачу данных Slave. Но удалось заставить принимать правильные данные только воткнув в цикле задержку после отправки данных. В связи с этим вопросы
1) Что за задержка тогда 10000 в функции HAL_SPI_Transmit(&hspi1, (uint8_t*) data_send, BITS, 10000) и чего она дает
2) Что вообще является событием для прерывания:
приход данных по SLK или NSS, или же вообще по MOSI.
3) Какова правильная последовательность действий при приеме и передаче данных между Slave и Master. Должно ли это обязательно ли быть прерывание или нет и что за чем идет. Кто и за кем обращается к сдвиговому регистру
4) Нужно ли Master контролировать скорость отправки данных на конкретной частоте или достаточно сконфигурировать скорость передачи данных
Заранее спасибо
Всем здравствуйте. Имею такой вопрос. Настроил SPI между двумя stm32. Master — f4, slave — f100. Вопрос следующий. Отсылаю значение одной 16 битовой переменной. При приеме данных слейвом в первый раз младший бит всегда теряется. Отправляю 4 (b100), получаю 2 (b10). При приеме данных мастером все наоборот, появляется лишний бит. Отправил 2 (b10), получаю 4 (b100). При повторном приеме, передаче, приходят правильные данные и больше такое не возникает. В чем причина? Подскажите, заранее спасибо
Ребята, не могу настроить USART DMA в circular mode через Cube. Достаточно ли в Cube поставить галочку в DMA request setting - mode circular? Запускаю командой HAL_UART_Transmit_DMA (&huart1, uartTX, 16) в main. Первый раз при запуске передает данные, потом висит, пока снова не запускаю HAL_UART_Transmit_DMA (&huart1, uartTX, 16) в цикле while. Хочу что бы постоянно передавались данные из массива. Подскажите в чем ошибка
Здравствуйте!
А если не известна заранее длина принимаемых данных? Как в таком случае организовать прием?
Принимать заведомо большее число, а когда станет понятно, что данные кончились (придет спецсимвол, к примеру), отключать прием.
Зачем счётчик, есть callback-функция HAL_UART_RxCpltCallback?
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_USART2_UART_Init(void);
на эти три строки ругается компилятор
да и вообще что то не могу проследить изменение receiveBuffer[32] правда использую эклипс
Там через адрес receiveBuffer в него кладутся принятые данные.
А вот почему ругается - это вопрос, все эти строки генерирует Cube. Может дело действительно в среде разработки.
перегенерировал код кубом, этих строк куб не генерит
Может в новой версии Куба другая немного организация или названия, суть то не в этом, а в том, что из статьи в свой проект надо копировать только пользовательский код.
в общем один буфер заполняется нулями, другой числами, и потом ничего (зависает на определенном адресе), где ошибка хз
Скинь проект на почту.
Как то кривопопово работает HAL_UART_Receive_IT....
Никто не победил как ей сбросить в начало кольцевой буфер...
может появилось более менее человеческое описание ....
у кого не получается проследить значение переменной receiveBuffer, строки
HAL_UART_Receive_IT(&huart1, receiveBuffer, 32);
HAL_UART_Transmit_IT(&huart2, transmitBuffer, 32);
ставим в цикл вайл, дли наглядности можно поставить чекпоинт
Приветствую. Цикла для CubeMX под SPI нет, так что пишу сюда. Принцип по идее один и тот же.
Пытаюсь подружить два микроконтроллера STM32f103. Но как-то функции передачи непонятно работают. Отсюда вопросы:
1) Чем отличаются функции с препиской IT (как я понял, они прерывания вребают) от обычных бункций приема и передачи?
2) Что конкретно означает задержка Timeout - аргумент функции приемо-передачи без IT. Когда она вступает в силу, эта задержка?
3) Какова логика вызова колбеков и зачем они нужны. Почему, если сам не вызвал ни разу прерывание, то и колбек неактивен. И зачем он вообще нужен?
Заранее спасибо)
Добрый день)
1). Обычная функция "заблокирует" контроллер пока будет идти прием/передача. То есть он будет крутиться в ней пока процесс не закончится.
2). Параметр timeout, насколько я помню, нужен для того, чтобы контроллер не завис навсегда в этих функциях. То есть если прием/передача не завершатся в течение этого времени, то функция вернет ошибку и контроллер выйдет из нее.
3). Callback'и нужны для удобства - чтобы не нужно было ничего менять в самих прерываниях, а просто писать код в соответствующем callback'е. Но я, к примеру, ими не пользуюсь.
Да, и еще вопросик:
4) Когда в дело вступает прерывание, доступное для мастера. Когда оно вообще используется?
4). Скорее всего там сделано так - само прерывание срабатывает по приему/передаче одного байта, а callback функция будет вызвана после полного окончания приема/передачи. Но вот этот момент я точно не помню, можно посмотреть по коду или экспериментальным путем выявить)
Приветствую. Понадобилось освоить USART в режиме Half Duplex. Подскажите пожалуйста, какая должна быть последовательность действий при отправке и приеме данных. Я действовал так. Само собой сначала соответствующим образом инициализируем интерфейс. Затем отправляем так:
HAL_HalfDuplex_EnableTransmitter(...);
HAL_UART_Transmit_IT(...);
И данные уходят, вижу их на осциллографе. После этого принимающий девайс шлет ответ и его я тоже вижу. Прием осуществляю так:
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
HAL_HalfDuplex_EnableReceiver(...);
HAL_UART_Receive_IT(...);
}
И данных процессор в итоге не видит, хоть они и идут. Что странно, HAL_UART_TxHalfCpltCallback Keil даже не компилит, видит что он тут не используется, хотя именно этот колбек по идее и должен работать в таком режиме передачи. Что я делаю нет так?
Точно не компилит? А если оптимизацию отключить?
Компиляция то идет, но данный калбек не активен. Это видно, если запустить отладку кейла. Что не так то делаю?
А где callback определен, в каком файле?
Колбек определен в моем файле, только дело явно не в этом, Другие то колбеки там же прописаны и работают. Подкажите сам алгоритм. Как в таком режиме USARTом управлять?
Конкретно про этот режим не могу ничего сказать определенного. Но раз данные через функцию Transmit_IT уходят логично было бы предположить, что и callback должен срабатывать.
Надо по коду функции пройтись и посмотреть не происходит ли там чего-то специфичного при таком режиме.
Здравствуйте, спасибо за статью. Вы не могли пояснить процесс передачи. Почему вначале мы пишем функцию приема, а затем передачи?
Вызывая функцию приема мы как бы ставим USART в режим ожидания данных, если сначала вызвать отправку, то за то время, что будет выполняться команда приема данные могут уже успеть "пролететь" и тогда они не будут приняты.
Спасибо
Присоединяюсь к клубу теряющих последний байт. 🙁
Здравствуйте! Немного не по теме. Сейчас поднимаю ModbussRTU на USARTe (портирую FreeModbuss). Вопрос, собственно, можно ли такой протокол передавать средствами DMA ? Еще не доконца разобрался с портированием и не могу понять как в DMA реализовать направление передачи (устройство будет мастером) или это скрыто под капотом?
Если вы сталкивались с подобными задачами - направите может.
В любом случае спасибо за ваш труд. По вашим урокам уже много полезных устройств трудится 🙂
Добрый вечер!
Рад, что уроки полезны!
С точки зрения протокола, в целом, без разницы - с DMA, либо без. В одном случае мы формируем буфер на отправку и из него данные по очереди кладем в регистр данных USART, в другом случае - даем DMA указатель на буфер и уже модуль DMA кладет эти данные в регистр USART.
Тут вопрос в том - будет ли какая то особая выгода от использования DMA. Все-таки, в первую очередь, плюсы есть при использовании больших массивов данных - загрузил передачу и процессор свободен. Здесь же скорее всего будет формат "Команда - Ответ - Новая команда - Ответ".
Спасибо за быстрый ответ! Выгода, думаю будет: на шине (rs-485) - 6 блоков InOut по восем каналов каждый + LCD 16x4 (то же на STM). Каждую итерацию главного цикла я должен обработать ввод-вывод. А в DMA привлекает прерывание обработки половины буфера. Т.е. он пока DMA обрабатывает первую половины, я работаю со второй и наоборот. Просто с МК я работаю только год (до этого я чистый ("десктопник"), и не уверен что в срок успею разобраться с портированием freeModBuss + DMA. Но попробую. Если положительно, то поделюсь. Думаю многим будет интересно (особенно когда сроки "жмуть" 🙂 )
Отлично, заранее благодарю!
Собираю устройство, скажем так прибор, состоящий из 2-х плат и 2-х микроконтроллеров.
На первой плате МК STM32F103 - принимает данные с датчиков по I2C, выполняет расчеты, сохраняет все настройки во внешнюю EEPROM по SPI.
На второй плате МК STM32F401 - работает на вывод графики на дисплей по интерфейсу 8080-параллельному.
Хочу соединить МК-1 с МК-2, думаю использовать UART, но также думаю и про SPI.
Чтобы передавать команды от МК-1 для отрисовки на дисплее с помощью МК-2.
Если использовать UART, то у первого МК(F103) - не хочу "занимать" нативные ноги TX/RX(PA10/PA9).
Как понял их статьи, UART 2(PA3/PA2) - без проблем.
А что касается UART 3, могу задействовать его для передачи данных?
И вообще, стоит ли делать связь между МК по UART?
Или лучше взять для передачи команд управления от МК-1 к МК-2 через SPI?
Подскажите ваше видение как лучше, или как сделали бы вы?
P.S. Есть такая марка дисплеев DWIN - так они используют UART для управления дисплеем от МК.
Доброго времени!
Если скорости UART'а хватает, то я бы делал на нем. Использовать можно любой модуль (USART1, USART2 и т. д.), также есть возможность ремапить сигналы на другие выводы. Допустим, для STM32F103RB USART1 может использовать PA9/PA10, либо PB6/PB7 в качестве Tx/Rx.
В receive buffer ничего не приходит (пробовал реализовать на stm32f103rbt6) В чем может быть проблема? P. s. Нужно для диплома,а так я не силен в этом
Много вариантов, скорее всего с подключением электрическим что-то.
Использую отладочник stm32f103 nucleо - 64, все вилки в норме, проверял
А есть исходник этого кода? Мб у меня чего-то не хватает,хотя все по вашему гайду делал
Не, не сохранил, у тебя тоже usart1 + usart2? Можешь проект выложить?
Да,куда выложить?
Да без разницы, яндеск.диск итп.
Выложу позже, пока выяснил,что hal_uart_transmit_it и hal_uart_receive_it в состояние hal_busy сразу встают(
https://disk.yandex.ru/d/0MLfpQQZ3XDG-g Ссылка на диск с файлом
В проекте все в порядке вроде как. Физически соединено PA2-PA10, PA3-PA9?
В while(1) надо поставить простенькую задержку на пару секунд, чтобы процессы успевали завершиться гарантированно, потом добавить проверку флагов завершения.
Да,соединено, что именно прописать для флагов?
Я остальной код здесь пропустил:
Спасибо за дополнение к коду,но у меня все равно hal transmit на hal busy уходит,поэтому ничего не может передать( Я уже даже не знаю в чем проблема
Уже при самом первом заходе в HAL_UART_Transmit_IT() hal_busy возвращает?
Да
А если прием убрать вообще, оставить только однократную отправку и убрать все внешние подключения?
То же самое
Я сейчас с stm32f103rb плату собрал по-быстрому, проверил с Вашим проектом - все четко работает.
У меня все еще не работает, мб дело в отладочнике stm32f103rb nucleo64?
Ну в теории вряд ли конечно... но можно попробовать с другого uart'а передачу проверить. Я вот думаю по поводу Keil'а, может там настройки отладчика не те?
А что может быть не так? И какие параметры должны быть установлены?
Закинул скрин - https://disk.yandex.ru/i/J-Xu5Dlh4Wb46A
И в настройках проекта контроллер точно верный выбран?
Все точно также, контроллер выбран правильно
Ну остается только плату поменять
У меня не заработало, прошил микроконтроллер (в дебаг режиме), массив transmitBuffer заполнился, много раз нажал следующий шаг, у цикла while, а в receiveBuffer одни нули:(
Правильно понял, что шаг "В итоге коммутируем так:", не означает действие в проекте? (Хотя пины именно так и расположились)
Также функции:
не отработали
Коммутируем в том плане, что физически соединяем проводами. Хотя HAL_UART_TxCpltCallback() так и так должен сработать.
О, понял, да я в целом этот термин так себе и представил, только вот у меня плата с stm32 есть и там уже все пины направлены в нужные места - видимо поэтому у меня receive и не приходит на usart1, так как у меня комутация не выполнена!
Скоро приедет Nucleo, там попробую!
Спасибо за ответ, очень приятно!)
Я почему-то подумал, что мы программно можем пины направить друг на друга, но видимо как раз таки и нет, а вот именно физической связи у меня нет, поэтому и не получилось)
Обращайся если что)
Увидел последний комментарий (от Антона), тоже не работало у человека. Добавил рекомендации от Админа, отличный код, от автора получился таким:
while (1)
{
HAL_Delay(100);
HAL_UART_Receive_IT(&huart1, receiveBuffer, BUFFER_SIZE);
HAL_UART_Transmit_IT(&huart2, transmitBuffer, BUFFER_SIZE);
HAL_Delay(100);
}
Также добавил две переменные, которые становятся true, когда случился Transive / Receive, переменная isTransived становится True, а вот isReceived остается false;
Подождал 10 циклов, isReceived остается false:(
Доброго времени суток! Приехала Nucleo, выполнил проект 1 в 1, комутацию выполнил, не работает:(
В комментариях прикреплю что есть, если нужно что-то специфичное, попросите, я пришлю.
Думается мне что на 1 устройстве нельзя usart -> usart передачи делать, но может, я ошибаюсь.
Комутация на плате, слева пины закрепленные от внешнего ст-линка, плата рабочая (проверил миганием светодиода)
Справа - сверху вниз
PC5 - USART1 RX
PC4 - USART1 TX
PA2 - USART2 TX
PA3 - USART2 RX
(ioc проекта, в следующем комментарии)
ioc проекта (usart идентичны, настройки не менял, они изначально одинаковы, на обоих включены EXTI)
ioc проекта (usart идентичны, настройки не менял, они изначально одинаковы, на обоих включены NVIC)
расположение пинов на моей nucleo
Вроде все нормально, можешь проект целиком выложить? Я в течение дня посмотрю.
Да могу, сейчас выложу, спасибо большое за обратную связь, коллеги на работе тоже не особо понимают почему не работают, я постараюсь сейчас на гитхаб выложить
Я пробовал передачу/прием в обычном режиме;
Менял местами Receive/Transmit
BUFFER_SIZE менял, но кажется твои 32 так и не ставил ни разу, 16, 8, 4, 2 проверял, receive не реагирует
Там лишний код имеется, отличный от того что в гайде, надеюсь не запутаешься)
https://github.com/b0r1ngx/usart-to-usart-stm32
В HAL_UART_RxCpltCallback() в принципе не попадает (без учета проверки if (huart->Instance == USART1))?
А в USART1_IRQHandler()?
В HAL_UART_RxCpltCallback - не попадает
В USART1_IRQHandler - сейчас гляну
В USART1_IRQHandler() - приходит!
Добавил переменную которая переключается когда туда заходит, сразу после выполнения строчки
HAL_UART_Receive_IT(&huart1, receiveBuffer, BUFFER_SIZE);
она обновляется
При том обновляется 3 раза)
Потом сижу в цикле, выполняю действия, а receive и переменная эта, всеравно не срабатывает
Супер!
Надо посмотреть, что происходит в UART_RxISR_8BIT(). Я мельком пробежался, должно туда попадать, затем на этом участке, что на скрине, обслуживаются счетчики байт, и когда все данные будут приняты должен быть переход в HAL_UART_RxCpltCallback(). В этой цепи где-то сбой )
Не знаю что это за функция (UART_RxISR_8BIT) и где ее искать, сейчас буду гуглить
Забыл написать, тут она - https://github.com/b0r1ngx/usart-to-usart-stm32/blob/main/Drivers/STM32F3xx_HAL_Driver/Src/stm32f3xx_hal_uart.c
Нашел ее, мне нужно в этот файл изменения внести?
Не, надо под отладчиком посмотреть, что там происходит.
да верняк, щас так и сделаю а то я обычно step_over нажимал, спасибо!
Так, буду в общем тогда, обо всем писать тут, в первое место куда попал это HAL_UART_Receive_IT(), здесь дошел до return - все ок значит
Хм, при инициализации 109 строки - HAL_UART_Receive_IT(&huart1, receiveBuffer, BUFFER_SIZE) - я вот захожу во внутрь, попадаю в функцию HAL_UART_Receive_IT() и UART_Start_Receive_IT(), они успешно инициализируются и я возвращаюсь в main() функцию - и все, дальше до цикла (while) дохожу, ну и собственно ни в UART_RxISR_8BIT, ни в HAL_UART_RxCpltCallback() - не попадаем
На 3244 строке huart->RxISR = UART_RxISR_8BIT;
я вижу эту функцию UART_RxISR_8BIT, но я так понимаю это установка колбэк функции с название RxISR в адрес объекта huart - если я правильно синтаксис понял (я в С/STM32 начинающий совсем)
Вот скрин, про что говорю, забыл прикрепить
Так вот еще один скрин того же самого, но с именем файла и номером строк (нельзя фотографии редактировать, поэтому так)
Так, тогда есть ощущение, что тут (на скрине) errorFlags != 0 (тот же файл)
Я до понедельника, или даже до вторника/среды следующей прервусь тут! Надеюсь еще спишемся! Возможно даже созвониться можно если ты не против!
Окей, до связи, пиши если что )
имел в виду что попробовать отладить как-то в этой функции, узнать где проблема?
Когда гайд увидел, смотрю шикарный короткий, а дает повзаимодействовать по USART на одном устройстве, а тут опять программистские проблемы, без них никуда... каждый день ошибки/баги фиксить приходится, тяжело жить, ахаха)
:DDD
Ну в целом можно поразмыслить, что может быть не так, просто обычно проще и быстрее выходит наглядно посмотреть )
Попробовал повзаимодействовать с USART2 и USART3 (т.к в мануале, не увидел информации о первом USART), пины поменялись, провода переключил, тоже ничего не получилось
При этом смена, отправка (transive) с USART3 работает, и USART2 не принимает
Доброго дня, спасите, помогите, уже 3 дня бьюсь и бестолку.
Создал проект в МХ, тактирование HSE чип отправляет по UART число с инкрементом.
В терминале компа получаю хрень типа (<0x15> <break>) грешил на тактирование, перестроился на HSI внутренний то точно не врёт.
На приёме тоже самое, пробовал разные скорости, без результата.
Грешил на железо, прошил ардуиной иде, всё в норме, данные корректны.
аА вот КУБ победить никак не получается.
Куда копать. не пойму.
Приветствую, а можете проект выложить? И скрин с терминала.
Да проекта пока нет, изучаю возможности куба, переезжаю с ардуины.
А, так, вот, что есть.
while (1) {
if (HAL_GetTick()-flash > 500){
++count;
if(count >= 5){count = 0;}
HAL_UART_Transmit_IT(&huart1,count, 1);
flash = HAL_GetTick();
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);
терминал кидает это
Здесь адрес должен передаваться:
Вот, что с адресом.
Ай, вот я туканул, СПАСИБО!!!!!!!!!!!!!
Раз уж мне с вами так повезло, подскажите как передать не 16ое число, а 10е
Это уже к приемной стороне вопрос, передается-то в виде последовательности битов, а отображаться уже может по-разному.
🙂 да, точно.
Вообще как оказалось долго на ардуине сидеть нельзя, она слишком много за человека делает, всё забыл и расслабился.но тебе спасибо, ещё неделю бы сидел.
и ведь нигде нет инфы, что перед данными адрес ставить нужно, нигде не встречал.
был бы хоть один сайт для бывших ардуинщиков, жить стало бы легче.
arduino опасная штука, не у всех выходит бросить )
Вообще компилятор должен был как минимум варнинг кинуть, что тип данных неверный, а по-хорошему, не должно было собраться вообще.
Доброго дня, ругается на for (unsigned char i = 0; i < BUFFER_SIZE; i++)
{
transmitBuffer[i] = i + 1;
receiveBuffer[i] = 0;
}
HAL_UART_Receive_IT(&huart1, receiveBuffer, BUFFER_SIZE);
HAL_UART_Transmit_IT(&huart2, transmitBuffer, BUFFER_SIZE); и uint8_t transmitBuffer[BUFFER_SIZE];
uint8_t receiveBuffer[BUFFER_SIZE];
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define BUFFER_SIZE
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;
UART_HandleTypeDef huart2;
/* USER CODE BEGIN PV */
uint8_t transmitBuffer[BUFFER_SIZE];
uint8_t receiveBuffer[BUFFER_SIZE];
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
for (unsigned char i = 0; i < BUFFER_SIZE; i++)
{
transmitBuffer[i] = i + 1;
receiveBuffer[i] = 0;
}
HAL_UART_Receive_IT(&huart1, receiveBuffer, BUFFER_SIZE);
HAL_UART_Transmit_IT(&huart2, transmitBuffer, BUFFER_SIZE);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief USART1 Initialization Function
* @param None
* @retval None
*/
static void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
/* USER CODE END USART1_Init 2 */
}
/**
* @brief USART2 Initialization Function
* @param None
* @retval None
*/
static void MX_USART2_UART_Init(void)
{
/* USER CODE BEGIN USART2_Init 0 */
/* USER CODE END USART2_Init 0 */
/* USER CODE BEGIN USART2_Init 1 */
/* USER CODE END USART2_Init 1 */
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART2_Init 2 */
/* USER CODE END USART2_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
}
/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1)
{
// USART1 завершил прием данных
}
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART2)
{
// USART2 завершил отправку данных
}
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
В чем может быть проблема?
Привет, судя по всему размер буфера не задан:
спасибо!
Пытаюсь реализовать двухстороннюю передачу UART-USB, приём в UART реализовал по одному байту(записывал в буфер), а завершение приёма было после байта окончания сообщения. Но что делать, если в конце сообщения нет байта окончания сообщения? Он же так и будет его ждать? Можно ли как-то реализовать некий таймер, по окончанию которого приём автоматически закончится? (желательно внутри этого коллбэка)
Отдельным таймером, выставлять флаг из серии isWaitingEof в колбэке, по срабатыванию таймера флаг сбрасывать и выполнять другие необходимые действия.
Добрый день, возникала проблема. Микроконтроллер stm32f407zet6 на плате запаял rs485 (конкретно adm3485) к ногам usart1 PA9/PA10 rx/tx настроил в асинхронном режиме, pa8 управляет режимами передачи/отправки данных. В CubeIDE выбираю нужные режимы, настроил тактирование по hse согласно мануалу. При отправке сообщения в терминале ком порта ничего не отображается, с железом проблем нет, с него все уходит (тыкал осцилографом). С чем может быть связана данная проблема ?
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
HAL_UART_Transmit(&huart1, (uint8_t*)"check UART\n", 11, 1000);
Добрый день, с выходов adm3485 на осциле нормальный сигнал значит выходит?
Добрый вечер, выходило всё хорошо. Ошибка была в том что на шине была предельная частота, ещё и скорость работы UART тоже высокая была, поэтому данные ломались. Спасибо, всё починил.
Отлично👍