UART в STM32. Часть 2.

Теперь, когда мы познакомились с UART'ом довольно подробно, попробуем создать устройство которое возможно даже послужит для чего-то полезного. Одно я могу сказать точно - оно поможет еще лучше понять как работать с UARTом. Устройство не особо хитрое - есть 10 светодиодов, состоянием которых мы можем управлять с компьютера через UART. Когда мы отправляем с компьютера символ "1" у нас загорается первый светодиод, отправляем еще раз - светодиод гаснет. Отправляем символ "?" и контроллер выдает нам состояние всех светодиодов. Аппаратно ничего нового (кроме светодиодов) тут нет. Я использовал свою любимую STM32VL Discovery и беспаячную макетную платку, в результате был рождён этот монстр :)

uart_led

Практическое применение данного устройства - управлять нагрузками с компьютера. Разумеется, придётся внести некоторые аппаратные изменения - вместо светодиодов поставить транзисторные ключи с реле. Ну а если практического использование не интересно, то поиграем со светодиодами

uart leds

В схеме ничего интересного нет, 10 светодиодов, 10 резисторов и контроллер. Преобразователь интерфейсов USB-UART (или RS-232 - UART) на схеме не показан для простоты. Его можно собрать, например по схеме из самой первой статьи про UART. А вот на коде остановимся поподробнее. Во-первых в этом примере мы не только передаем что-то из устройства в компьютер, но и принимаем данные от компьютера. Вторая особенность - использование специализированной библиотеки для работы с UARTом. Код выглядит страшновато, но попробуем разобраться что к чему. 

 
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_usart.h"
//Макрос для проверки состояние определенного светодиода.
#define   get_led_state(pin) ((GPIOB->ODR & pin) != 0)
 
//Функция отправляет байт в UART
void send_to_uart(uint8_t data)  {
 while(!(USART1->SR & USART_SR_TC));
 USART1->DR=data;
}
 
//Функция отправляет строку в UART
void send_str(char * string) {
 uint8_t i=0;
 while(string[i]) {
  send_to_uart(string[i]);
  i++;
 }
 send_to_uart('\r');
 send_to_uart('\n');
}
 
int main(void)
{
 uint8_t uart_data;
 uint16_t led;
 //Включаем порты и UART1
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB  , ENABLE);
 //Выключаем JTAG (он занимает ноги нужные нам)
 GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
 
 //Настраиваем первые 10 ног порта B на выход
 GPIO_InitTypeDef gpio_port;
 gpio_port.GPIO_Pin = 0x3FF;
 gpio_port.GPIO_Mode = GPIO_Mode_Out_PP;
 gpio_port.GPIO_Speed = GPIO_Speed_2MHz;
 GPIO_Init(GPIOB, &gpio_port);
 GPIOB->ODR=0x00;
 
    // Настраиваем ногу PA10 как вход UARTа (RxD)
 gpio_port.GPIO_Pin   = GPIO_Pin_10;
 gpio_port.GPIO_Mode  = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &gpio_port);
 
    // Настраиваем ногу PA9 как выход UARTа (TxD)
    // Причем не просто выход, а выход с альтернативной функцией
 
    gpio_port.GPIO_Pin   = GPIO_Pin_9;
    gpio_port.GPIO_Speed = GPIO_Speed_50MHz;
    gpio_port.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &gpio_port);
 
    //Заполняем структуру настройками UARTa
    USART_InitTypeDef uart_struct;
    uart_struct.USART_BaudRate            = 9600;
    uart_struct.USART_WordLength          = USART_WordLength_8b;
    uart_struct.USART_StopBits            = USART_StopBits_1;
    uart_struct.USART_Parity              = USART_Parity_No ;
    uart_struct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    uart_struct.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;
    //Инициализируем UART
    USART_Init(USART1, &uart_struct);
    //Включаем UART
    USART_Cmd(USART1, ENABLE);
 
    while(1) //Бесконечный цикл в нем мы проверяем ...
    {
     if (USART1->SR & USART_SR_RXNE) { // ... не пришло ли что-то в UART ?
      uart_data=USART1->DR; //Считываем то что пришло в переменную...
      switch(uart_data)  { //И выполняем определённое действие...
      case '0':
       GPIOB->ODR^=GPIO_Pin_0;  //Инвертируем состояние ноги №0
       break;
      case '1':
       GPIOB->ODR^=GPIO_Pin_1;
       break;
      case '2':
       GPIOB->ODR^=GPIO_Pin_2;
       break;
      case '3':
       GPIOB->ODR^=GPIO_Pin_3;
       break;
      case '4':
       GPIOB->ODR^=GPIO_Pin_4;
       break;
      case '5':
       GPIOB->ODR^=GPIO_Pin_5;
       break;
      case '6':
       GPIOB->ODR^=GPIO_Pin_6;
       break;
      case '7':
       GPIOB->ODR^=GPIO_Pin_7;
       break;
      case '8':
       GPIOB->ODR^=GPIO_Pin_8;
       break;
      case '9':
       GPIOB->ODR^=GPIO_Pin_9;
       break;
      case '?': //Печатаем состояние каждого светодиода
       send_str("===LEDs state:===");
       for (led=1;led<=0x200;led=led<<1) {
        if (get_led_state(led)) {
         send_str("ON");
        } else  {
         send_str("OFF");
        }
       }
       send_str("=================");
       break;
      }
     }
    }
}

Для начала пару слов о настройке UARTa. Как я уже говорил, я использовал в этом примере специальную библиотеку, чего и вам рекомендую. Конечно, знать для чего нужны все эти регистры для управления UARTом надо, но напрямую настраивать UART через них это дольше и сложнее. Куда проще заполнить структуру и вызвать функцию для настройки и все готово к работе. Тем более код становится более читабельным. Теперь о передаче данных в компьютер. Я написал две функции send_to_uart и send_str. Первая передаёт байт в UART, вторая передаёт строку в UART. Передача строки представляет собой вызов send_to_uart для каждого её символа. Строка должна заканчиваться нульсимволом. Приём данных от компьютера реализован очень тупо и просто. В бесконечном цикле постоянно проверяется бит RXNE статусного регистра  USART1. Если этот бит установился в единицу, значит что-то пришло и надо забрать данные и обработать их. После того как байт данных принят он проверяется на соответствие символам от 0 до 9 или знаку вопроса. Если в UART пришла цифра то мы просто меняем состояние соответствующей ноги на противоположное. Ну а если пришел знак вопроса то при помощи макроса get_led_state проверяем состояние каждого светодиода и печатаем соответствующую информацию в UART. Для экспериментов я использовал свой любимый терминал

Ну собственно вот что у меня получилось после нажатия 012789?. Естественно при этом соответствующие светодиоды загорелись. Если у вас получился иной результат, пишите ниже. Попробуем разобраться.

P.S. На всякий случай выкладываю свой проект который у меня получился.