Давно была идея запилить управление ножками стмки по 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

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

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

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Свежие записи
Последние комментарии
  • Загрузка...
Счетчик
Яндекс.Метрика