Рано или поздно у каждого разработчика появляется необходимость подключить свое устройство к компьютеру. Для этого можно использовать LPT, COM или USB порты. Если первый морально устарел и зачастую отсутствует на материнских платах, а последний слишком сложен для начинающего, то COM порт является наиболее подходящим. Он все еще есть на материнских платах, и он относительно прост в использовании. Как и раньше, я не буду очень сильно углубляться в теоретические дебри, которых уйма на просторах интернета, а постараюсь максимально доступно объяснить основы работы на практике.
Постановка задачи:
1) Подключить STM32F4Discovery к компьютеру через COM порт;
2) Реализовать режим echo;
3) Набираемые символы должны сохраняться в текстовую переменную, по нажатию Enter в консоль должна вернуться полная строка;
4) При отправки строки “led” должны загореться 4 светодиода на плате.
Подключение к компьютеру является наиболее сложной частью задачи. У UART есть 2 основных вывода: Rx (Received Data) и Tx (Transmitted Data). Главная проблема – согласование уровней напряжения, так как в порте компьютера оно составляет 12В, а в порте микроконтроллера - 3,3В. Для решения этой проблемы используется микросхема MAX3232. Схема переходника показана на рисунке.
Диод VD1 – индикатор питания, светодиод в корпусе 0805, VD2 – защита от переполюсовки, можно взять любой маломощный. Микросхема – MAX3232 в корпусе SO16.
К статье прилагается проект платы в Sprint Layout.
Плата выполнена на одностороннем стеклотекстолите толщиной 2мм. Ответная часть для COM порта берется для монтажа на провод и одевается на текстолит, получается компактно и аккуратно.
Заранее отмечу, что я не изготавливал этот переходник для урока, так как у меня уже есть платка с аналогичной микросхемой, но такие переходники мною делались ранее.
У переходника 4 вывода:
1) Vcc – подключается к 3В питанию на отладочной плате;
2) Rx – подключается к Rx UART2 (PA3) микроконтроллера;
3) Tx – подключается к Tx UART2 (PA2);
4) GND – подключается к «земле» на плате.
Для связи с компьютером используется программа-терминал Putty.
Настройка:
Во вкладке Session выбираем Serial.
В строке Serial line пишем номер порта, к которому вы подключили плату.
В строке Speed пишем 9600 – это скорость работы.
Переходим во вкладку Serial.
Data bits ставим 8.
Stop bits – 1.
Parity и Flow control надо выставить None.
Нажимаем Open и получаем консоль.
Если подключить переходник к COM порту, выводы Vcc и GND подключить к плате, а Tx и Rx соединить, тогда то, что вы будете писать в консоли, будет возвращаться в нее же. Если при нажатии клавиш ничего не происходит и консоль остается чистой – ищите и исправляйте ошибку.
Итак, с подключением вроде справились. Теперь начинаем писать программу.
Первым делом нужно инициализировать UART, для этого создается функция:
//Инициализируем USART2 void usart_init(void) { GPIO_InitTypeDef GPIO_InitStructure; //Структура содержащая настройки порта USART_InitTypeDef USART_InitStructure; //Структура содержащая настройки USART RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //Включаем тактирование порта A RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //Включаем тактирование порта USART2 GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); //Подключаем PA3 к TX USART2 GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2); //Подключаем PA2 к RX USART2 //Конфигурируем PA2 как альтернативную функцию -> TX UART. Подробнее об конфигурации можно почитать во втором уроке. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //Конфигурируем PA3 как альтернативную функцию -> RX UART. Подробнее об конфигурации можно почитать во втором уроке. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_Init(GPIOA, &GPIO_InitStructure); USART_StructInit(&USART_InitStructure); //Инициализируем UART с дефолтными настройками: скорость 9600, 8 бит данных, 1 стоп бит USART_Init(USART2, &USART_InitStructure); USART_Cmd(USART2, ENABLE); //Включаем UART }
Готово, теперь для инициализации достаточно написать usart_init() в main.
В этой функции UART настраивается по дефолтным значениям. Для более тонкой настройки вместо:
USART_StructInit(&USART_InitStructure);
Пишем:
USART_InitStructure.USART_BaudRate = 9600; //Скорость обмена 9600 бод USART_InitStructure.USART_WordLength = USART_WordLength_8b; //Длина слова 8 бит USART_InitStructure.USART_StopBits = USART_StopBits_1; //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; //Включен передатчик и приемник USART2
Теперь надо научится отправлять в COM порт байты, а потом и строки.
//Функция отправляет байт в UART void send_to_uart(uint8_t data) { while(!(USART2->SR & USART_SR_TC)); USART2->DR=data; } //Функция отправляет строку в UART, по сути пересылая по байту в send_to_uart void send_str(char * string) { uint8_t i=0; while(string[i]) { send_to_uart(string[i]); i++; } }
Принцип работы прост: send_to_uart отправляет в COM порт байт, а send_str пересылает строку по байту в send_to_uart.
Теперь нужно включить прерывание по приему сообщений от COM порта:
usart_init(); //Инициализируем UART //Настраиваем прерывания по приему __enable_irq(); //Глобальное включение прерывания NVIC_EnableIRQ(USART2_IRQn); //Включаем прерывания от UART NVIC_SetPriority(USART2_IRQn, 0); //Прерывание от UART, приоритет 0, самый высокий USART2->CR1 |= USART_CR1_RXNEIE; //Прерывание по приему Установка приоритета актуальна при использовании нескольких прерываний во избежание конфликта. Теперь инициализируем порты, на которых висят светодиоды, как во втором уроке. GPIO_InitTypeDef GPIO_InitStructure; //Структура содержащая настройки порта RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); //Включаем тактирование порта D GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12| GPIO_Pin_13| GPIO_Pin_14| GPIO_Pin_15; //Выбераем нужные вывод GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //Включаем режим выхода GPIO_Init(GPIOD, &GPIO_InitStructure); //вызов функции инициализации
И самое главное – функция обработки прерываний UART:
void USART2_IRQHandler (void) { char uart_data; if (USART2->SR & USART_SR_RXNE) //Проверяем, прило ли чтонибудь в UART { USART2->DR = USART2->DR; //Echo по приему, символ отправленный в консоль вернется uart_data=USART2->DR; //Считываем то что пришло в переменную... uart2_rx_buf[uart2_rx_bit]=USART2->DR; //Помещаем принятый байт в буфер. uart2_rx_bit++; //Наращиваем счётчик байтов буфера. if(uart_data=='\r') //Если пришло сообщение о нажатии Enter... { GPIO_ResetBits(GPIOD, GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); //Сбрасываем все пины в «0» if(strcmp(uart2_rx_buf,"led0\r")==0) //Если пришла команда "led0" { GPIO_SetBits(GPIOD, GPIO_Pin_12); //Подаем «1» на PD12 } else if(strcmp(uart2_rx_buf,"led1\r")==0) //Если пришла команда "led1" { GPIO_SetBits(GPIOD, GPIO_Pin_13); //Подаем «1» на PD13 } else if(strcmp(uart2_rx_buf,"led2\r")==0) //Если пришла команда "led2" { GPIO_SetBits(GPIOD, GPIO_Pin_14); //Подаем «1» на PD14 } else if(strcmp(uart2_rx_buf,"led3\r")==0) //Если пришла команда "led3" { GPIO_SetBits(GPIOD, GPIO_Pin_15); //Подаем «1» на PD15 } else { send_str("\n"); //Переходим на новую строку send_str("String: "); send_str(uart2_rx_buf); //Отправляем ее обратно в консоль } memset(uart2_rx_buf, 0, sizeof(uart2_rx_buf)); //Очищаем буфер uart2_rx_bit=0; //Сбрасываем счетчик send_str("\n"); } } }
Вот и все, теперь запускаем консоль, прошиваем плату, подключаем и набираем строку, а затем жмем Enter и наслаждаемся результатом. Команды led0, led1, led2, led3 управляют светодиодами на плате. Как это все работает видно на видео. Исходники и плата прикреплены к статье.
Хочется отметить, что все наработки по данным урокам будут реализованы в плате расширения для STM32F4discovery о разработке которой можно почитать в этой теме
Прикрепленные файлы:
- COM_to_UART.rar (11 Кб)
- Урок_3.rar (171 Кб)
Комментарии (13) | Я собрал (0) | Подписаться
Для добавления Вашей сборки необходима регистрация
Вопрос: имею собственноручный переходник USB-uart на микросхемке ft232rl. Скажите, если я совмещу два переходника, то получу ли я полноценный USB-COM(5вольт на 12вольт)?
И еще кое что, придется ли мне перекрещивать выводы( Rx(ftdi) с tx(max) и наоборот, tx(ftdi)с rx(max)?
[Автор]
По Вашему вопросу: особенность FT232 - наличие полного ком порта, то есть есть выводы RI, RTS, CTS и тд. Так вот, если вы хотите получить полноразмерный ком порт, если можно так выразиться, то MAX вам не подойдет, слишком мало выводов. Советую почитать документацию на эту микруху, там схема есть. Ну и вообще рекомендую читать документацию на компоненты, иногда оооочень полезно:) RX и TX перекрещивать не придется.
Автор, статьи супер, только пожалуйста не упускай мелочей и дальше. Пока вроде получается.
А через встроенный USB подключать нельзя? Я имею ввиду можно ли вшить в память контроллера преобразователь uart-USB? Или это сложно/бесполезно?
[Автор]
По поводу USB-лично я смысла не вижу, да и геморно это. Лучше возьмите FT232.
//Шаблон для программ FD
//
//************************INCLUDES*********************************//
#include "stm32l1xx.h"
#include "stm32l_discovery_lcd.h"
//************************DEFINES**********************************//
#define enableInterrupts __enable_irq();
#define disableInterrupts __disable_irq();
//************************FUNCTIONS********************************//
void STM_USART_INIT(void);
void STM_INTERPUT_INIT(void);
void USART3_IRQHandler(void);
void Usart3_Transmit(uint8_t);
void Usart3_Transmit_str(char* str);
//************************END_FUNCTIONS****************************//
char* tmp;
uint32_t i = 0;
//************************MAIN*************************************//
int main()
{
disableInterrupts;
STM_USART_INIT();
enableInterrupts;
//for(i=0; i < 1000000; i++);
Usart3_Transmit_str("START");
while(1)
{
}
}
//************************END_MAIN********************************//
void STM_USART_INIT(void)
{
STM_INTERPUT_INIT(); //инициализация прерываний
RCC->CR |= RCC_CR_HSION; //Включаем тактовый генератор HSI
while(!(RCC_CR_HSIRDY)); //Ждем его стабилизации
RCC->CFGR |= RCC_CFGR_SW_HSI; //Выбираем источником тактовой частоты SYSCLK генератор HSI
RCC->CR &= ~RCC_CR_MSION; //Отключаем генератор MSI.
RCC->AHBENR |= RCC_AHBENR_GPIOCEN; //Включаем тактирование порта C
RCC->APB1ENR |= RCC_APB1ENR_USART3EN; //Включаем тактирование модуля USART1
USART3->CR1 &= ~USART_CR1_M; //Длина слова - 8 бит
USART3->CR2 &= ~USART_CR2_STOP; //1 стоп-бит
USART3->BRR = 0x683; //baud rate 9600 при частоте HSI = 16 МГц
USART3->CR1 |= (USART_CR1_TE | USART_CR1_RE); //Разрешаем передачу данных
USART3->CR1 |= USART_CR1_RXNEIE; //прерывание по приему данных на МК, флаг RXNE
USART3->CR1 |= USART_CR1_UE; //Включаем USART3
//GPIO_INIT
GPIOC->MODER |= GPIO_MODER_MODER10_1; //PC10 - выход AF
GPIOC->OTYPER &= ~GPIO_OTYPER_OT_10; //PC10 - выход push-pull
GPIOC->PUPDR &= ~(GPIO_PUPDR_PUPDR10); //PC10 - без подтяжки
GPIOC->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR10; //PC10 - скорость 40 МГц
GPIOC->AFR[1] |= (0x00000070); //AF7, для USART3_TX
GPIOC->MODER |= GPIO_MODER_MODER11_1;
GPIOC->OTYPER &= ~(GPIO_OTYPER_OT_11);
GPIOC->PUPDR &= ~(GPIO_PUPDR_PUPDR11);
GPIOC->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR11;
GPIOC->AFR[1] |= (0x00000700); //AF7, для USART3_RX
RCC->AHBENR |= RCC_AHBENR_GPIOBEN; //Включаем тактирование порта B
GPIOB->MODER |= GPIO_MODER_MODER6_0;
GPIOB->OTYPER &= GPIO_OTYPER_OT_6;
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR6;
GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR6;
GPIOB->BSRRL |= GPIO_Pin_6;
GPIOC->MODER |= GPIO_MODER_MODER9_0; // port B pin 6
// light on the blue light
}
//----------------------------------------------------
void STM_INTERPUT_INIT(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_EnableIRQ(USART3_IRQn);
}
//----------------------------------------------------
void USART3_IRQHandler(void)
{
//если причина прерывания прием данных на МК
if(!(USART3->SR & USART_SR_RXNE))
{
USART3->DR = USART3->DR;
//USART3->SR &= ~USART_SR_RXNE; //очистить флаг
}
}
//---------------------------------------------------
//Функци передачи символа через USART
void Usart3_Transmit(uint8_t data)
{
while(!(USART3->SR & USART_SR_TC)); //Ждем установки флага TC - завершения передачи
USART3->DR = data;
}
//---------------------------------------------------
void Usart3_Transmit_str(char* str)
{
uint8_t i=0;
while(str[i])
{
Usart3_Transmit(str[i]);
i++;
}
Usart3_Transmit('\n');
Usart3_Transmit('\r');
}
[Автор]