5. STM32. Програмування STM32F103. USART
Ми вже використовували послідовний порт для програмування мікроконтролера. Тепер застосуємо його за прямим призначенням. STM32F103 Має 3 послідовних USART порти. Ми розглянемо приклад з USART1. Решта портів працюють аналогічно. У цьому прикладі ми підключимо мікроконтролер до комп`ютера за допомогою UART-USB перехідника. Та будемо використовувати термінальну програму для передачі команд мікроконтролеру.
Схема
На комп`ютері будемо використовувати термінальну програму Putty. Можна будь-яку іншу яка працює з послідовним портом. Розглянемо приклад для STM32, який буде працювати як простенький термінал: приймати команди з комп’ютера і обробляти їх. Команди будуть дві: ON, OFF. Звісно, вони будуть вмикати і вимикати світлодіод на платі. Повний текст програми наведений у кінці статті, а зараз розберемо як це працює.
Ініціалізація USART1
Ініціалізація USART1 виконується наступним чином:- Вмикаємо тактування на модуль USART1 та порт GPIOA (ноги TX і RX USART1 підключені до - PA9, PA10). Про тактування йшлося у попередній статті.
- Конфігуруємо NVIC. NVIC - контролер пріоритетних векторних переривань. Про нього буде окрема розмова. Сьогодні треба засвоїти, що всі переривання налаштовуються у NVIC.
- Конфігуруємо GPIO (PA9, PA10). Це ноги TX і RX.
- Конфігуруємо USART1
- Вмикаємо USART1
- Вмикаємо переривання, яке спрацьовує при надходженні байту у USART1.
void usart_init(void)
{
/* Enable USART1 and GPIOA clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
/* NVIC Configuration */
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the USARTx Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Configure the GPIOs */
GPIO_InitTypeDef GPIO_InitStructure;
/* Configure USART1 Tx (PA.09) as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure USART1 Rx (PA.10) as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure the USART1 */
USART_InitTypeDef USART_InitStructure;
/* USART1 configuration ------------------------------------------------------*/
/* USART1 configured as follow:
- BaudRate = 115200 baud
- Word Length = 8 Bits
- One Stop Bit
- No parity
- Hardware flow control disabled (RTS and CTS signals)
- Receive and transmit enabled
- USART Clock disabled
- USART CPOL: Clock is active low
- USART CPHA: Data is captured on the middle
- USART LastBit: The clock pulse of the last data bit is not output to
the SCLK pin
*/
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
/* Enable USART1 */
USART_Cmd(USART1, ENABLE);
/* Enable the USART1 Receive interrupt: this interrupt is generated when the
USART1 receive data register is not empty */
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
}
Як видно з коду ініціалізації, порт USART1 буде працювати на швидкості 115200. У налаштуванні консольної програми треба зазначати таку саму швидкість порту.
Розбір програми
Тепер дивимось що відбувається, коли мікроконтролер отримує від комп’ютера байт (дивись USART1_IRQHandler).
void USART1_IRQHandler(void)
{
if ((USART1->SR & USART_FLAG_RXNE) != (u16)RESET)
{
RXc = USART_ReceiveData(USART1);
RX_BUF[RXi] = RXc;
RXi++;
if (RXc != 13) {
if (RXi > RX_BUF_SIZE-1) {
clear_RXBuffer();
}
}
else {
RX_FLAG_END_LINE = 1;
}
//Echo
USART_SendData(USART1, RXc);
}
}
Перш за все ми перевіряємо, прапорець USART_FLAG_RXNE
if ((USART1->SR & USART_FLAG_RXNE) != (u16)RESET)
і впевнюємося, що прилетів байт. Бо обробник переривань USART1_IRQHandler може викликатися і іншими подіями. Потім отриманий символ складаємо у RX_BUF, перевіряючи переповнення буфера. Якщо прилітає кінець рядка (символ з кодом 13), тобто у консолі термінала натисли клавішу Enter, тоді виставляємо наш прапорець RX_FLAG_END_LINE. По ньому ми будемо знати, що треба розібрати рядок, що був набраний у консолі термінала (тобто, прийшов час розібрати вміст буфера RX_BUF), і, якщо в буфері буде відома команда (ON або OFF), - виконати її.
Зверніть увагу, що у обробнику переривань USART1_IRQHandler ми не будемо виконувати досить важку процедуру обробки команд. Ми тільки встановимо прапорець, а обробку будемо робити у основному циклі програми. Це робиться для того, щоб як найшвидше завершити обробку переривання, тому що можуть чекати інші переривання. STM32 мають пріоритети переривань і обробка переривання може бути перервана перериванням з більшим пріоритетом. Та, незважаючи на це, краще, щоб обробка переривань виконувалась так швидко, як це можливо.
Остання команда у процедурі обробки переривання:
...
USART_SendData(USART1, RXc);
...
відправляє прийнятий символ назад у термінал, тобто виконує функцію Echo.
Тепер роздивимось що відбувається у головному циклі програми.
...
while (1)
{
if (RX_FLAG_END_LINE == 1) {
// Reset END_LINE Flag
RX_FLAG_END_LINE = 0;
USARTSend("\r\nI has received a line:\r\n");
USARTSend(RX_BUF);
USARTSend("\r\n");
if (strncmp(strupr(RX_BUF), "ON\r", 3) == 0) {
USARTSend("\r\nTHIS IS A COMMAND \"ON\"!!!\r\n");
GPIO_ResetBits(GPIOC, GPIO_Pin_13);
}
if (strncmp(strupr(RX_BUF), "OFF\r", 4) == 0) {
USARTSend("\r\nTHIS IS A COMMAND \"OFF\"!!!\r\n");
GPIO_SetBits(GPIOC, GPIO_Pin_13);
}
clear_RXBuffer();
}
}
...
Ми очікуємо, коли буде натиснутий Enter, перевіряючи наш прапорець RX_FLAG_END_LINE:
if (RX_FLAG_END_LINE == 1)
Коли він прилітає, у консоль термінала виводиться рядок який ми будемо аналізувати:
USARTSend(RX_BUF);
А потім перевіряємо прийнятий рядок і, якщо це одна з відомих команд, - вмикаємо чи вимикаємо світлодіод:
if (strncmp(strupr(RX_BUF), "ON\r", 3) == 0) {
if (strncmp(strupr(RX_BUF), "OFF\r", 4) == 0) {
Повний текст програми
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_usart.h"
#include "misc.h"
#include <string.h>
#define RX_BUF_SIZE 80
volatile char RX_FLAG_END_LINE = 0;
volatile char RXi;
volatile char RXc;
volatile char RX_BUF[RX_BUF_SIZE] = {'\0'};
volatile char buffer[80] = {'\0'};
void clear_RXBuffer(void) {
for (RXi=0; RXi<RX_BUF_SIZE; RXi++)
RX_BUF[RXi] = '\0';
RXi = 0;
}
void usart_init(void)
{
/* Enable USART1 and GPIOA clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
/* NVIC Configuration */
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the USARTx Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Configure the GPIOs */
GPIO_InitTypeDef GPIO_InitStructure;
/* Configure USART1 Tx (PA.09) as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure USART1 Rx (PA.10) as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure the USART1 */
USART_InitTypeDef USART_InitStructure;
/* USART1 configuration ------------------------------------------------------*/
/* USART1 configured as follow:
- BaudRate = 115200 baud
- Word Length = 8 Bits
- One Stop Bit
- No parity
- Hardware flow control disabled (RTS and CTS signals)
- Receive and transmit enabled
- USART Clock disabled
- USART CPOL: Clock is active low
- USART CPHA: Data is captured on the middle
- USART LastBit: The clock pulse of the last data bit is not output to
the SCLK pin
*/
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
/* Enable USART1 */
USART_Cmd(USART1, ENABLE);
/* Enable the USART1 Receive interrupt: this interrupt is generated when the
USART1 receive data register is not empty */
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
}
void USART1_IRQHandler(void)
{
if ((USART1->SR & USART_FLAG_RXNE) != (u16)RESET)
{
RXc = USART_ReceiveData(USART1);
RX_BUF[RXi] = RXc;
RXi++;
if (RXc != 13) {
if (RXi > RX_BUF_SIZE-1) {
clear_RXBuffer();
}
}
else {
RX_FLAG_END_LINE = 1;
}
//Echo
USART_SendData(USART1, RXc);
}
}
void USARTSend(const unsigned char *pucBuffer)
{
while (*pucBuffer)
{
USART_SendData(USART1, *pucBuffer++);
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
{
}
}
}
void SetSysClockTo72(void)
{
ErrorStatus HSEStartUpStatus;
/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration -----------------------------*/
/* RCC system reset(for debug purpose) */
RCC_DeInit();
/* Enable HSE */
RCC_HSEConfig( RCC_HSE_ON);
/* Wait till HSE is ready */
HSEStartUpStatus = RCC_WaitForHSEStartUp();
if (HSEStartUpStatus == SUCCESS)
{
/* Enable Prefetch Buffer */
//FLASH_PrefetchBufferCmd( FLASH_PrefetchBuffer_Enable);
/* Flash 2 wait state */
//FLASH_SetLatency( FLASH_Latency_2);
/* HCLK = SYSCLK */
RCC_HCLKConfig( RCC_SYSCLK_Div1);
/* PCLK2 = HCLK */
RCC_PCLK2Config( RCC_HCLK_Div1);
/* PCLK1 = HCLK/2 */
RCC_PCLK1Config( RCC_HCLK_Div2);
/* PLLCLK = 8MHz * 9 = 72 MHz */
RCC_PLLConfig(0x00010000, RCC_PLLMul_9);
/* Enable PLL */
RCC_PLLCmd( ENABLE);
/* Wait till PLL is ready */
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}
/* Select PLL as system clock source */
RCC_SYSCLKConfig( RCC_SYSCLKSource_PLLCLK);
/* Wait till PLL is used as system clock source */
while (RCC_GetSYSCLKSource() != 0x08)
{
}
}
else
{ /* If HSE fails to start-up, the application will have wrong clock configuration.
User can add here some code to deal with this error */
/* Go to infinite loop */
while (1)
{
}
}
}
int main(void)
{
// Set System clock
SetSysClockTo72();
/* Initialize LED which connected to PC13 */
GPIO_InitTypeDef GPIO_InitStructure;
// Enable PORTC Clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
/* Configure the GPIO_LED pin */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_ResetBits(GPIOC, GPIO_Pin_13); // Set C13 to Low level ("0")
// Initialize USART
usart_init();
USARTSend(" Hello.\r\nUSART1 is ready.\r\n");
while (1)
{
if (RX_FLAG_END_LINE == 1) {
// Reset END_LINE Flag
RX_FLAG_END_LINE = 0;
USARTSend("\r\nI has received a line:\r\n");
USARTSend(RX_BUF);
USARTSend("\r\n");
if (strncmp(strupr(RX_BUF), "ON\r", 3) == 0) {
USARTSend("\r\nTHIS IS A COMMAND \"ON\"!!!\r\n");
GPIO_ResetBits(GPIOC, GPIO_Pin_13);
}
if (strncmp(strupr(RX_BUF), "OFF\r", 4) == 0) {
USARTSend("\r\nTHIS IS A COMMAND \"OFF\"!!!\r\n");
GPIO_SetBits(GPIOC, GPIO_Pin_13);
}
clear_RXBuffer();
}
}
}
Бажаю успіхів!
Дивись також:
- 1. STM32. Програмування STM32F103. Тестова плата. Прошивка через UART та через ST-Link
- 2. STM32. Програмування. IDE для STM32
- 3. STM32. Програмування STM32F103. GPIO
- 4. STM32. Програмування STM32F103. Тактування
- 5. STM32. Програмування STM32F103. USART
- 6. STM32. Програмування STM32F103. NVIC
- 7. STM32. Програмування STM32F103. ADC
- 8. STM32. Програмування STM32F103. DMA
- 9. STM32. Програмування STM32F103. TIMER
- 10. STM32. Програмування STM32F103. TIMER. Захоплення сигналу
- 11. STM32. Програмування STM32F103. TIMER. Encoder
- 12. STM32. Програмування STM32F103. TIMER. PWM
- 13. STM32. Програмування STM32F103. EXTI
- 14. STM32. Програмування STM32F103. RTC
- 15. STM32. Програмування STM32F103. BKP
- 16. STM32. Програмування STM32F103. Flash
- 17. STM32. Програмування STM32F103. Watchdog
- 18. STM32. Програмування STM32F103. Remap
- 19. STM32. Програмування STM32F103. I2C Master
- 20. STM32. Програмування STM32F103. I2C Slave
- 21. STM32. Програмування STM32F103. USB
- 22. STM32. Програмування STM32F103. PWR
- 23. STM32. Програмування STM32F103. Option bytes
- 24. STM32. Програмування STM32F103. Bootloader
- STM32. Скачати приклади
- System Workbench for STM32 Інсталяція на Ubuntu
- Keil uVision5 – IDE для STM32
- IAR Workbench – IDE для STM32
- Керування безколекторним двигуном постійного струму (BLDC) за допомогою STM32
- Керування PMSM за допомогою STM32
Доброго дня! виникло питання: чи не потрібно скидати прапор USART_IT_RXNE у IRQHandler?
Доброго дня. Дякую за запитання. Це один з небагатьох флагів, які скидуються контролером при зчитуванні з USARTn->DR (функція USART_ReceiveData). Якщо у обробнику переривання не виконувати USART_ReceiveData, тоді треба скидади вручну.
Привіт. Моє запитання стосується першого виводу тексту через USARTSend а саме
USARTSend(" Hello.\
\
USART1 is ready.\
\
");
У мене при виконанні даного коду втрачається перший символ. У приведеному коді строка починається з пробілу і якщо його видалити то втратиться відповідно буква H. Чи є цьому якесь пояснення?Доброго дня. Дякую що звернули на це увагу. Я не знайшов цьому пояснення. Тому взяв за звичку відразу після ініціалізації відправляти пустий рядок. Звісно, це "костиль", але коли найду цьому рішення, обов`язково повідомлю.
В чому різниця між USART1 і USART2 . на USART2 працюють всі протоколи а на USART1 ні
USART - це послідовний порт. У мікроконтроллера їх може бути декілька. Наприклад, у STM32F103C8T6 їх три. Позначаються відповідно USART1, USART2, USART3. Функціонально однакові.
Tags
pmsm mongodb bme280 bmp280 exti nodemcu timer ssd1331 dma sms meteo max1674 foc stm32 i2c eeprom adc usart git java-script programmator ssd1306 watchdog nvic html flask raspberry-pi st-link mpu-9250 remap examples rs-232 hih-4000 css rtc bkp bluetooth bldc motor esp8266 mpu-6050 encoder capture soldering ngnix wifi usb flash rfid displays tim battery avr smd websocket 3d-printer barometer books lcd python servo eb-500 atmega web pwm piezo mpx4115a uart dht11 solar gps options dc-dc ethernet brushless docker sensors gpio led
Архіви