Совсем недавно мы начали использовать микроконтроллеры семейства STM32F4 (вот ссылочка), так что надо продолжать это хорошее дело ) Сегодня посмотрим , как работает в STM32F4 USART, ну и, как обычно, создадим какой-нибудь проект для примера. Ковыряться в регистрах, пожалуй, не будем, все отлично описано в даташитах, так что останавливаться на этом смысла нету. Но и проект сразу создавать не будем, для начала полезем в SPL и посмотрим, что там припрятано для работы с USART.
Время традиционной вставки: поскольку компания STMicroelectronics прекратила поддержку библиотеки SPL, которая использовалась в этом курсе, я создал новый, посвященный работе уже с новыми инструментами, так что буду рад видеть вас там - STM32CubeMx. Кроме того, вот глобальная рубрика по STM32, а также статья на смежную тему из нового курса: STM32 UART. Прием и передача данных по UART в STM32CubeMx.
По большому счету, тут все похоже на то, что мы видели при работе с контроллерами STM32F10x (обязательно посмотрите). В файле stm32fxx_usart.h видим определение структуры, в которой мы зададим нужные нам параметры работы:
typedef struct { uint32_t USART_BaudRate; uint16_t USART_WordLength; uint16_t USART_StopBits; uint16_t USART_Parity; uint16_t USART_Mode; uint16_t USART_HardwareFlowControl; } USART_InitTypeDef;
Собственно, все что надо тут есть – и скорость и формат кадра, так что для настройки модуля USART в STM32F4xx нам надо лишь создать структуру USART_InitTypeDef, заполнить ее поля нужными значениями и передать ее в качестве параметра в функцию USART_Init(). Эта функция, как и все остальные функции, находятся в SPL в файле sm32f4xx_usart.c.
Для отправки данных в USART нужно вызвать функцию USART_SendData(), для приема - USART_ReceiveData(). В STM32 USART может работать в режиме, поддерживающем протокол передачи данных LIN, соответственно, для этого в Standard Peripheral Library есть ряд функций для работы с LIN. Вообще приемо-передатчик в контроллерах STM32, как и все остальное, довольно навороченный – там и обычные прием и передача, и, опять же, поддержка LIN, и работа совместно с DMA. Вот для всего этого есть множество готовых функций, ну и, конечно же, еще функции для работы с прерываниями и для проверки различных флагов.
Давайте уже на практике посмотрим, как все это работает. Напишем для STM32F4 пример для передачи данных с использованием соответствующего прерывания.
Итак, что вообще нужно сделать, чтобы USART запустился и начал работать:
- Во-первых включаем тактирование модуля.
- Затем включаем тактирование нужных портов контроллера (ножки Tx/Rx).
- Настраиваем пины для работы в режиме альтернативных функций.
- Запускаем модуль USART.
- И, наконец, включаем нужные прерывания и начинаем высылать/принимать данные.
Вроде бы все, что нужно упомянул, переходим к делу. Создаем новый проект и пишем программу:
/***************************************************************************************/ // Цепляем нужные файлы #include "stm32f4xx.h" #include "stm32f4xx_rcc.h" #include "stm32f4xx_gpio.h" #include "stm32f4xx_usart.h" /***************************************************************************************/ // Объявляем переменные GPIO_InitTypeDef gpio; USART_InitTypeDef usart; // Пусть нам надо передать 8 байт, объявим массив для этих данных uint8_t sendData[8]; uint8_t bytesToSend = 8; // Счетчик отправленных байт uint8_t sendDataCounter = 0; /***************************************************************************************/ // Инициализация всего, что нам надо void initAll() { // Включаем прерывания __enable_irq(); // Запускаем тактирование RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); // Инициализация нужных пинов контроллера, для USART1 – PA9 и PA10 GPIO_StructInit(&gpio); gpio.GPIO_Mode = GPIO_Mode_AF; gpio.GPIO_Pin = GPIO_Pin_9; gpio.GPIO_Speed = GPIO_Speed_50MHz; gpio.GPIO_OType = GPIO_OType_PP; gpio.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOA, &gpio); gpio.GPIO_Mode = GPIO_Mode_AF; gpio.GPIO_Pin = GPIO_Pin_10; gpio.GPIO_Speed = GPIO_Speed_50MHz; gpio.GPIO_OType = GPIO_OType_PP; gpio.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOA, &gpio); // И вот еще функция, которой не было при работе с STM32F10x, но которую нужно вызывать при использовании STM32F4xx GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1); GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1); // А теперь настраиваем модуль USART USART_StructInit(&usart); usart.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; usart.USART_BaudRate = 9600; USART_Init(USART1, &usart); // Включаем прерывания и запускаем USART NVIC_EnableIRQ(USART1_IRQn); USART_Cmd(USART1, ENABLE); } /***************************************************************************************/ // Функция main() int main() { // Для начала заполним массив тестовыми данными for (uint8_t i = 0; i < 8; i++) { sendData[i] = i; } // Вызываем функцию инициализации initAll(); // Включаем прерывание по окончанию передачи USART_ITConfig(USART1, USART_IT_TC, ENABLE); while(1) { // А тут мы ничего не делаем, вся работа у нас в прерывании __NOP(); } } /***************************************************************************************/ // Обработчик прерывания void USART1_IRQHandler() { // Проверяем, действительно ли прерывание вызвано окончанием передачи if (USART_GetITStatus(USART1, USART_IT_TC) != RESET) { // Очищаем флаг прерывания USART_ClearITPendingBit(USART1, USART_IT_TC); // Отправляем байт данных USART_SendData(USART1, sendData[sendDataCounter]); // Увеличиваем счетчик отправленных байт sendDataCounter++; // Если отправили все данные, начинаем все сначала if (sendDataCounter == bytesToSend) { sendDataCounter = 0; } } } /***************************************************************************************/
Вот и все! Микроконтроллер начинает постоянно по кругу высылать наши данные во внешний мир. Таким образом, с передачей мы разобрались. В следующей статье сразу же без лишних слов напишем пример для приема данных.
Это хорошо что есть люди которым не только интересно самому разобраться, но и находят время чтоб выложить свои достижения, пояснить профанам еще))) успехов!!!
Спасибо! =))
Не могу выразить признательность вашим трудам. Огромное человеческое спасибо !!! Мне недавно поставили задачу : к контролеру stm32f407vgt6 подключить дисплей + клавиатуру и написать управление.
Дали STM32f4 - Discavery и сказали : давай к июлю нужно разобраться и сделать. У меня выпали глаза из глазниц и волосы из фолликул, так как раньше не имел удовольствия работать с контролерами, только прошивки правил для Ethernet - платок. В общем благодаря вам начали появляться хоть кусочки этой мозаики в моей голове и приходит хоть какое-то понимание ... Если можно потом по терзаю вас вопросами, когда они из общего разряда "АААААА как это все сделать !?!!?!" перейдут в более конкретный, осмысленый разряд.
Рад, что статьи помогли) Вопросы, конечно, задавай, постараюсь помочь разобраться
Доброго времени суток. Если я правильно понял то на РА9 - выходят данные, а на РА10 - 1ца если смотреть осциллографом так?
Ну да)
Здравствуйте, у Вас хорошие статьи.
Не могли бы Вы помочь мне при освоении карты Ethernet (DP83848) под эту Discovery?
Ну никак платка работать не хочет. Уже все примеры, которые были, перепробовал, перечитывал множество топиков и форумов - результата не дало.
Если можете, дайте код с подробными объяснениями, схему подключения, что нибудь.
Спасибо.
e-mail: rozmathplus@gmail.com
(если можете сбросить какие-то файлы)
Проекти пишу в IAR, CoIDE
Я, честно говоря, особо с этим не работал) Не было пока задач таких..Как будет полегче со свободным временем, могу поковырять, набросать статейку
Буду очень благодарен. Для ускорения написания Вами статьи, могу сбросить собранный мною материал. В некоторых проектах впечатление, что "вот-вот" заработает, но недостаток опыта не позволяет решить проблемы или компиляции и др..
Но я пока правда даже не представляю, когда у меня предвидится свободное время..очень много работы сейчас
Здравствуйте . Очень нравится ваш блог ссылка в избранных уже:) Пытаюсь завести USART по вашему примеру (CooCox) но STM32f4 — Discavery молчит ничего не передает . Пытался с другими примерами тоже никак. Есть вопрос про тактирование ,что плата по умолчанию тактируется от 8МГЦ это где то нужно поправлять?
День добрый )
Вообще под discovery все должно с ходу заработать. А если просто светодиод попробовать заставить помигать?
диодиками моргаю нормально, находил исходник где генерируют файл через макрос с ним работает вот у меня возникает вопрос, что может такая загвоздка и у меня, в KEIL может сразу проект под плату Discovery stm34f4 создается и автоматом настраивает, в кокосе под чип по дефаулту.
Да нет, в Keil'e нет ничего специфического в начальной конфигурации, по идее в CooCox'е то же самое должно происходить по умолчанию на этапе настройки
Привет!
Зачем нужно писать GPIO_StructInit(&gpio);
Включаем все ноги на вход, а потом две из них на AF? Зачем?
Для того, чтобы гарантировать то, что все неиспользуемые поля структуры будут заполнены дефолтными значениями
Здравствуйте, Вы сделали обработчик прерывания, которые будет вызываться по окончанию приема, но чтобы начать процесс передачи вроде надо же что то передать, или я что то упустил?
по окончанию передачи*
При разрешении прерывания программа должна уйти на обработчик прерывания
до речі
1. закрийте тіло main-a
2. програма не працюватиме, якщо не причепитись до правильних ніг b6 b7, а не а9 а10.
3. дякую)
Хочу задействовать два USART в МК STM32F4, настраиваю два USARTA`а но работает тока один почему так? Нужно принимать данные по USART 2 затем сохранять их в памяти и со скоростью 9600 отправлять из USART6 в ПК.
Товарищи как одновременно принимать данные по USART2 на скорости 115200 сохранять в память и передавать на ПК с USART6 на скорости 9600?
Ну так забей 2 отдельных инит. структуры
к примеру :
USART_InitTypeDef _MY_USART_INIT2;
USART_InitTypeDef _MY_USART_INIT6;
точнее 4, т.к. GPIO нежно как AF сконфигурировать, значит
GPIO_InitTypeDef _GPIO_AF_USART2_INIT;
GPIO_InitTypeDef _GPIO_AF_USART6_INIT;
Дальше все так же как в статье выше, НО
в той что инициализирует USART2
_MY_USART_INIT2.USART_BaudRate = 115200;
_MY_USART_INIT2.USART_Mode = USART_Mode_Rx;
-----------------------------------------------------------------------------------
Ну а ту что USART 6
_MY_USART_INIT2.USART_BaudRate = 9600;
_MY_USART_INIT2.USART_Mode = USART_Mode_Tx;
//------------------------------------------
А в остальном все так же.
Ну и если на прерываниях, то разные "Хендлеры" для вызова прерываний на прием со 2ого и передачи с 6ого.
Описываешь их в маин, как ф-ции
void USART2_IRQHandler(void)
{
Твой прием
}
void USART6_IRQHandler(void)
{
твоя передача
}
А в инициализации
тож 2 раза 🙂
NVIC_EnableIRQ(USART2_IRQn);
NVIC_EnableIRQ(USART6_IRQn);
----------------------------------------------
все "хендлеры", ака обработчики при необходимости смотрите. в startup_stm32f4xx.s
там вот такого плана будет :
DCD SPI1_IRQHandler ; SPI1
DCD SPI2_IRQHandler ; SPI2
DCD USART1_IRQHandler ; USART1
DCD USART2_IRQHandler ; USART2
DCD USART3_IRQHandler ; USART3
========================================
К автору и хозяину статьи - надеюсь не помешал ?, просто рад помочь в твоем нужном, альтруистическом деле 🙂
Здравствуйте! Пытался повторить данный пример, и столкнулся с небольшой проблемой. После передачи 0 байта массива, регистр SR заполняется нулями, передача останавливается. Тот же эффект если передавать данные в while
while(1)
{
for(i=0;iSR & USART_SR_TC));
}
}
Добрый день, не очень понял проблему.. Можешь выслать проект полностью?
Прошу прощения
while(1)
{
for(i=0;iSR&USART_SR_TC));
}
}
Помогите пожалуйста с такой проблемой:
Я сделал программу основываясь на вашем примере, но есть косяк - постоянно срабатывает прерывание на окончание передачи(даже если ничего не передавал), оно срабатывает настолько часто, что никакой другой код не выполняется.
Подскажите как поступать в данном случае?!
Привет!
Присылай на почту проект - я все попробую, Aveal.MicroTechnics@gmail.com
Прикрутил printf как указано в https://blablacode.ru/mikrokontrollery/450
Но он работает если только я отключу NVIC_EnableIRQ(USART2_IRQn); закоментарив.
А если включу прерывания то printf не работает так как перехватывается вот здесь USART2_IRQHandler()
Как сделать чтобы одновременно работало и printf и работали прерывания?
А симптомы какие? printf() вызывает функцию отправки в USART, по сути то же самое, как если бы эта функция вызывалась из другого места.
Вопрос решился:
// see https://blablacode.ru/mikrokontrollery/450
// MyPutChar is instead of MyLowLevelPutchar in write.c
// for C:\Program Files\IAR Systems\Embedded Workbench 9.1\arm\src\lib\file\write.c
int MyPutChar(int x)
{
while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, x);
return x;
}
write.c:
*
* Copyright 1998-2017 IAR Systems AB.
*
* This is a template implementation of the "__write" function used by
* the standard library. Replace it with a system-specific
* implementation.
*
* The "__write" function should output "size" number of bytes from
* "buffer" in some application-specific way. It should return the
* number of characters written, or _LLIO_ERROR on failure.
*
* If "buffer" is zero then __write should perform flushing of
* internal buffers, if any. In this case "handle" can be -1 to
* indicate that all handles should be flushed.
*
* The template implementation below assumes that the application
* provides the function "MyLowLevelPutchar". It should return the
* character written, or -1 on failure.
*
********************/
#include <LowLevelIOInterface.h>
#include "charm.h"
#pragma module_name = "?__write"
int MyLowLevelPutchar(int x);
/*
* If the __write implementation uses internal buffering, uncomment
* the following line to ensure that we are called with "buffer" as 0
* (i.e. flush) when the application terminates.
*/
size_t __write(int handle, const unsigned char * buffer, size_t size)
{
/* Remove the #if #endif pair to enable the implementation */
// #if 0
size_t nChars = 0;
if (buffer == 0)
{
/*
* This means that we should flush internal buffers. Since we
* don't we just return. (Remember, "handle" == -1 means that all
* handles should be flushed.)
*/
return 0;
}
/* This template only writes to "standard out" and "standard err",
* for all other file handles it returns failure. */
if (handle != _LLIO_STDOUT && handle != _LLIO_STDERR)
{
return _LLIO_ERROR;
}
for (/* Empty */; size != 0; --size)
{
// if (MyLowLevelPutchar(*buffer++) < 0)
if (MyPutChar(*buffer++) < 0) // minem
{
return _LLIO_ERROR;
}
++nChars;
}
return nChars;
// #else
/* Always return error code when implementation is disabled. */
return _LLIO_ERROR;
// #endif
}
Сейчас всё работает.
Отлично 👍
В write.c надо добавить:
#include "*.h"
Где *.h это файл с функцией MyPutChar(int x).