Содержание
Урок 1. Первый проект
Урок 2. Управление кнопками
Урок 3. Подключение LCD
Урок 4. Использование ШИМ
Урок 5. Таймеры
Урок 6.1. Статическая индикация
Урок 6.2. Динамическая индикация
Урок 7.1. Генерация звука
Урок 7.2. Генерация звука. Продолжение
Урок 8.1. Передача данных через UART
Урок 8.2. Передача данных через UART. Продолжение»
Урок 9. Передача данных через SPI
Урок 10. Изучение АЦП. Простой вольтметр
Урок 11. Получение синуса при помощи ШИМ
Урок 12. Измерение температуры
Урок 13. Внешние прерывания.
Урок 14. Использование отладчика
Урок 15.1. Управление инкрементальным энкодером
Урок 15.2. Управление громкостью, при помощи энкодера
Урок 16. Управление RGB светодиодом
Урок 17. Использование ИК
Урок 18.1. Знакомство с графическим дисплеем
Урок 18.2 Вывод изображения на графический дисплей
Урок 18.3 Вывод русскоязычного текста
Урок 19. Формирование сигнала, при помощи ЦАП (R2R)
Урок 20. Опрос матричной клавиатуры
Урок 21. Сторожевой таймер
Урок 22.1 Воспроизведение wav. Введение.
Урок 22.2 Воспроизведение wav. Продолжение.
Урок 23.1 Работа с внешней памятью
Урок 23.2 Работа с файловой системой Fat

Давно была идея запилить управление ножками стмки по UART, в виде текстовой консоли. Недавно начал читать Advanced Programming in the UNIX Environment и один из первых примеров, вдохновил таки вернуться к этой мысли.

Оговорюсь сразу, я делал свой велосипед, только ради интереса. Подобные проекты есть, сходу нагуглился microrl. На настройке периферии заострять внимание не буду, т.к. настраивал кубом, использовал UART2, проверял F103 и на Nucleo-L476. Стандартный поток перенастраивался на уарт, пример retarget.c выдернул из кейловских недр, просто переопределил 2 функции. При желании, вы можете сделать это с помощью прерываний.

int SendChar(int ch) {
    HAL_UART_Transmit(&huart2, (uint8_t *)&ch, sizeof(ch), 1000);    
    return 0;
}

int GetKey (void)  {

    uint8_t ch;
    
    while(HAL_UART_Receive(&huart2, &ch, 1, 2000) != HAL_OK);   
    if(isprint(ch) || (ch == '\r') || (ch == '\n')) {
        SendChar(ch); 
        if(ch == '\r') {
            ch = '\n';
        }
    }
    return ch;
}

Функция GetKey, которая читает байты сразу же их отправляет, чтобы можно было видеть отправленные символы на терминале. Так как терминалы по разному отправляют ENTER, то сводим все варианты к одному ‘\n’.

Далее по возможности используются функции стандартной библиотеки. При помощи функции fgets, читаем байты из UART, как только приходит конец строки, начинаем разбирать байты. Вначале выкидываем признак окончания строки ‘\n’. Затем с помощью strtok строка разбивается на аргументы, разделителем может быть пробел, запятая или точка.

int main(void)
{

  init_all();
  printf("\r\n/************************/ \r\nTest terminal v0.1\r\n>");
    
  while (1)
  {
      /* get newline terminated string from stdin */
      if(fgets(rcv_buf, sizeof(rcv_buf), stdin) != 0)  {
          
          /* get string size*/
          str_size = strlen(rcv_buf);

          if(str_size > 0) {
              /* replace newline symbol with null */
              if (rcv_buf[str_size - 1] == '\n'){
                  rcv_buf[str_size - 1] = 0;                        
              }

              /* get args from command */
              char *args[10];
              char *ptr = strtok (rcv_buf," ,.");
                  
              int i = 0;
              
              /* terminate strings with delimiters */
              while (ptr != NULL)  {
                  args[i++] = ptr;
                  ptr = strtok (NULL, " ,.");
              }
              
              /* try execute command */
              if(rcv_buf[0] != 0) {
                  if(execlp(i, args) == EXIT_FAILURE) {
                      printf("command not found: %s\n\r", rcv_buf);
                  }
                  else {
                      printf("\n\rOK\n\r");
                  }
              }
              else {
                  printf("\n\r");
              }
          }
                
          /* wait next command */          
          printf(">");
      }
  }
}
}

Полученные аргументы(args) передаются в execlp(i, args), i — количество аргументов. Вначале была идея для имени каждой команды считать хэш, но в конце концов их вышло немного, поэтому необходимость этого отпала. Поэтому пробегаем if else, по существующим командам с помощью strcmp. Если команда найдена, то дальше парсятся остальные аргументы.

if (!strcmp (argv[0], "gpio_set")) {
    if(argc == 3) {
        if((Char_ToGPIOx(&gpio_port, argv[1]) == EXIT_SUCCESS) && (Str_ToPin(argv[2], &gpio_pin) == EXIT_SUCCESS)) {
             HAL_GPIO_WritePin(gpio_port, gpio_pin, GPIO_PIN_SET);
             return EXIT_SUCCESS;
        }                                                          
    }
}

Если в аргументах есть ошибка или команда не найдена, то выводится сообщение «command not found».

Для разных камней список режимов отличается по регистрам, поэтому в финальной версии я ориентировался на 103 камень, однако перенести под другую серию будет не сложно.

Список поддерживаемых команд:

gpio_set [register] [pin]
Установить бит на выбранном порту GPIO
Пример: gpio_set A 5

gpio_reset [register] [pin]
Сбросить бит на выбранном порту GPIO
Пример: gpio_reset A 5

gpio_read [register] [pin]
Прочитать значение на ножке выбранного порта GPIO
Пример: gpio_read A 5
Ответ: pin = 1 или pin = 0

gpio_mode [register] [pin]
Прочитать настройку пина на выбранном порту GPIO
Пример: gpio_mode A 5
Ответ: mode = in или mode = out.

port_read [register]
Прочитать состояние выбранного порта GPIO
Пример: port_read A
Ответ: IDR=0xFFFF

gpio_readAll
Прочитать состояние всех портов GPIO
Пример: gpio_readAll
Ответ: A=0x00009fff B=0x00009fff C=0x0000ffff D=0x00000000 E=0x00000000 F=0x00000000 G=0x00000000

Пример реально общения:
gpio_terminal

В целом работает. Идей для развития довольно много, можно прикрутить чтение ацп или в принципе любого регистра. Сырцы можно забрать c Github

3 комментария: Консоль на STM32

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Счетчик
Яндекс.Метрика