Stm32. программирование stm32f103. тестовая плата. прошивка через последовательный порт и через st-link программатор

Главная идея нашего USB Mass Storage Bootloader

bin0x08002800binbinHEXbin-файлddWin32DiskImagerST-LINK Utilitybin

Команда для заливки на Mass Storage bin-файла под Ubuntu:

Итак, наш USB Mass Storage, который теперь играет роль Bootloader-а, прошивку уже обновляет. Теперь осталось доделать USB Mass Storage пример таким образом, чтобы он мог передавать управление залитой программе. Я это сделал с помощью кнопки. Когда мы подключаем микроконтроллер к компьютеру USB кабелем, и при этом нога PB1 замкнута на землю, контроллер стартует как USB Mass Storage устройство и можно заливать прошивку. Если во время старта PB1 на землю не замкнута, а висит в воздухе, или подключена к +, микроконтроллер передает управление залитой прошивке.
Вот как выглядит код нашего Bootloader-а (доработанного примера USB Mass Storage):

https://github.com/avislab/STM32F103/tree/master/Example_Bootloader

Прошивка STM32 с помощью USB-Uart переходника под Windows

STM32BootloaderSTM32USARTFT232RLFT232RLftdichip.comVCP

Подключаем RX и TX выходы к соответствующим выводам USART1 микроконтроллера. RX переходника подключаем к TX микроконтроллера (A9). TX переходника подключаем к RX микроконтроллера (A10). Поскольку USART-USB имеет выходы питания 3.3В подадим питания на плату от него.

Чтобы перевести микроконтроллер в режим программирования, надо установить выводы BOOT0 и BOOT1 в нужное состояние и перезагрузить его кнопкой Reset или выключить и включить питание микроконтроллера. Для этого у нас есть перемычки. Различные комбинации загоняют микроконтроллер в различные режимы. Нас интересует только один режим. Для этого у микроконтроллера на выводе BOOT0 должно быть логическая единица, а на выводе BOOT1 — логический ноль. На плате это следующее положение перемычек:

После нажатия кнопки Reset или отключения и подключения питания, микроконтроллер должен перейти в режим программирования.

Спецификация точности

В спецификации преобразования третье необязательное поле является спецификацией точности. Он состоит из точки ( ), за которой следует Неотрицательное десятичное целое число, которое в зависимости от типа конвертации указывает число символов строки, число десятичных разрядов или число значащих цифр для вывода.

В отличие от спецификации ширины, спецификация точности может вызывать либо усечение выходного значения, либо округление значения с плавающей запятой. Если параметр имеет значение 0, а преобразуемые данные равны 0, результат не будет содержать символы, как показано в следующем примере:

Если спецификация точности представляет собой звездочку (), аргумент из списка аргументов предоставляет значение. В списке аргументов аргумент должен предшествовать форматируемому значению, как показано в следующем примере:

Символ определяет либо интерпретацию, либо точность по умолчанию , если аргумент не указан, как показано в следующей таблице.

Влияние значений точности на тип

Тип Значение По умолчанию
, Точность определяет количество цифр после запятой. Точность по умолчанию — 13. Если точность равна 0, десятичная запятая не выводится, если не используется флаг .
, Точность не применяется. Символ выводится.
, , , , , Точность определяет минимальное выводимое количество цифр. Если количество цифр в аргументе меньше значения precision, выходное значение дополняется слева нулями. Значение не усекается, если число цифр превышает точность. Точность по умолчанию — 1.
, Выводимое количество знаков дробной части задается спецификацией точности. Последняя выводимая цифра округляется. Точность по умолчанию — 6. Если точность равна 0 или точка ( ) отображается без номера, то десятичная запятая не печатается.
, Значение точности задает количество цифр после десятичной запятой. Если десятичная запятая присутствует, перед ней присутствует по крайней мере одна цифра. Значение округляется до соответствующего количества цифр. Точность по умолчанию — 6. Если точность равна 0 или если точка ( ) отображается без числа после него, десятичная запятая не печатается.
, Точность определяет максимальное выводимое количество значащих цифр. Выводятся шесть значащих цифр, а конечные нули усекаются.
, Точность определяет максимальное количество выводимых символов. Символы, выходящие за рамки precision, не выводятся. Символы выводятся до тех пор, пока не будет найден символ null.

Datasheet

Datasheet содержит в себе информацию о наличии определенной периферии в конкретном МК, цоколевке, электрических характеристиках и маркировке чипов для STM32F103x8 и STM32F103xB, то есть для вот этих, которые обведены красным прямоугольником:

Некисло, один даташит на 8 микроконтроллеров.

Основное в Datasheet-е

В первую очередь нужно обратить внимание на раздел 7. Ordering information scheme, в котором указано, то обозначает каждый символ в маркировке. Например, для STM32F103C8T6: корпус  LQFP-48, 64Кб flash-а, температурный диапазон –40 to 85 °C

Далее 2.1 Device overview. В нем есть таблица, в которой сказано, какая периферия есть в конкретном микроконтроллере и в каком количестве:

Основное различие между микроконтроллерами из разных колонок в количестве ножек и объеме флеша, остальное все одинаково. Небольшое исключение составляет первая колонка версий Tx: в этих микроконтроллерах поменьше модулей SPI, I2C и USART-ов. Нумерация периферии идет с единицы: то есть, если в STM32F103Cx у нас 2 SPI, то они имеют имена SPI1 и SPI2, а в STM32F103Tx у нас только SPI1. Так как Datasheet у нас на микроконтроллеры STM32F103x8 и STM32F103xB, то эта таблица справедлива только для этих моделей. К примеру STM32F103C8 или STM32F103CB соответствуют этой таблице, а STM32F103C6 нет, для него есть отдельный даташит.

В разделе 2.2 Full compatibility throughout the family говорится о том, что устройства STM32F103xx являются программно, функционально и pin-to-pin (для одинаковых корпусов) совместимыми.

В reference manual-е есть разделение на следующие «виды» микроконтроллеров: STM32F103x4 и STM32F103x6 обозначены как low-density devices, STM32F103x8 и STM32F103xB как medium-density devices, STM32F103xC, STM32F103xD и STM32F103xE как high-density devices. В устройствах Low-density devices меньше Flash и RAM памяти, таймеров и периферийных устройств. High-density devices имеют больший объем Flash и RAM памяти, а так же имеют дополнительную периферию, такую как SDIO, FSMC, I2S и DAC, при этом оставаясь полностью совместимыми с другими представителями семейства STM32F103xx. То есть, если на каком-то этапе разработки стало ясно, что выбранного микроконтроллера не хватает для реализации всех возможностей, то можно безболезненно выбрать более навороченный камень без необходимости переписывать весь существующий софт, при этом, если новый камень будет в том же корпусе, то отпадает необходимость заново разводить печатную плату.

Прошивка STM32 с помощью USB-Uart переходника под Linux (Ubuntu)

Устанавливаем stm32flash

http://launchpadlibrarian.net/188294676/stm32flash_0.4-2_i386.debstm32flashhttps://launchpad.net/ubuntu/wily/i386/stm32flash/0.4-2

Если используем USB-UART переходник, имя порта буде примерно такое /dev/ttyUSB0

Получить информацию о чипе

Результат:

stm32flash 0.4

http://stm32flash.googlecode.com/

Interface serial_posix: 57600 8E1
Version      : 0x22
Option 1     : 0x00
Option 2     : 0x00
Device ID    : 0x0410 (Medium-density)
- RAM        : 20KiB  (512b reserved by bootloader)
- Flash      : 128KiB (sector size: 4x1024)
- Option RAM : 16b
- System RAM : 2KiB

Пишем в чип

Результат:

stm32flash 0.4

http://stm32flash.googlecode.com/

Using Parser : Raw BINARY
Interface serial_posix: 57600 8E1
Version      : 0x22
Option 1     : 0x00
Option 2     : 0x00
Device ID    : 0x0410 (Medium-density)
- RAM        : 20KiB  (512b reserved by bootloader)
- Flash      : 128KiB (sector size: 4x1024)
- Option RAM : 16b
- System RAM : 2KiB
Write to memory
Erasing memory
Wrote and verified address 0x08012900 (100.00%) Done.

Starting execution at address 0x08000000... done.

Модификатор * и #

Для некоторых из своих спецификаторов преобразования функция printf() поддерживает два дополнительных модификатора: * и #.

Непосредственное расположение # перед спецификаторами преобразования g, G, f, Е или e означает, что при выводе обязательно появится десятичная точка — даже если десятичных цифр нет. Если вы поставите # непосредственно перед x или X, то шестнадцатеричное число будет выведено с префиксом 0x. Если # будет непосредственно предшествовать спецификатору преобразования o, число будет выведено с ведущим нулем. К любым другим спецификаторам преобразования модификатор # применять нельзя. (В С99 модификатор # можно применять по отношению к преобразованию ; это значит, что обязательно будет выведена десятичная точка.)

Модификаторы минимальной ширины поля и точности можно передавать функции printf() не как константы, а как аргументы. Для этого в качестве заполнителя используйте звездочку (*). При сканировании строки формата функция printf() будет каждой звездочке * из этой строки ставить в соответствие очередной аргумент, причем в том порядке, в каком расположены аргументы. Например, при выполнении оператора, показанного на рис. 8.1, минимальная ширина поля будет равна 10 символам, точность — 4, а отображаться будет число 123.3.

В следующей программе показано применение обоих модификаторов # и *:

#include <stdio.h>

int main(void)
{
  printf("%x %#x\n", 10, 10);
  printf("%*.*f", 10, 4, 1234.34);

  return 0;
}

Рис. 8.1

Обратите внимание на то, каким образом звездочке (*) ставится в соответствие определенное значение

printf("%*.*f", 10.4, 123.3);
         | |     |     |
         '-+-----'     |
           |           |
           '-----------'

<<<>>>

Пример программы

В качестве демонстрации настроим внешнее прерывание по нарастающему и спадающему краю импульса на пине PA0 микроконтроллера. Создадим функцию void EXTI_Init(void), в которой будем производить все необходимые настройки:

void EXTI_Init(void)
{
  
}

GPIOAAFIOEXTI

  RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; //Тактирование GPIOA
  RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; //Тактирование AFIO

PA0

  /*
    Настройка GPIO
      Пин: PA0
      Режим: Input Pull Up
  */
  GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0);
  GPIOA->CRL |= (0x02 << GPIO_CRL_CNF0_Pos); //Вход Pull Up/Pull Down
  GPIOA->ODR |= (1 << 0); //Подтяжка вверх

GPIOвот в этой статье

Теперь настройка EXTI. Так как мы выбрали пин PA0, то прерывание будет висеть на канале EXTI0. Нам нужно в регистре AFIO_EXTICR1 с помощью группы бит EXTI0 выбрать PA0 в качестве источника сигнала. Делается это вот так:

AFIO->EXTICR &= ~(AFIO_EXTICR1_EXTI0); //Нулевой канал EXTI подключен к порту PA0
  EXTI->RTSR |= EXTI_RTSR_TR0; //Прерывание по нарастанию импульса
  EXTI->FTSR |= EXTI_FTSR_TR0; //Прерывание по спаду импульса

EXTI_IMREXTI0_IRQnNVIC

  EXTI->PR = EXTI_PR_PR0;      //Сбрасываем флаг прерывания 
                               //перед включением самого прерывания
  EXTI->IMR |= EXTI_IMR_MR0;   //Включаем прерывание 0-го канала EXTI
  
  NVIC_EnableIRQ(EXTI0_IRQn);  //Разрешаем прерывание в контроллере прерываний

Вот полный код функции:

void EXTI_Init(void)
{
  RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; //Тактирование GPIOA
  RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; //Тактирование AFIO
  
  
  /*
    Настройка GPIO
      Пин: PA0
      Режим: Input Pull Up
  */
  GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0);
  GPIOA->CRL |= (0x02 << GPIO_CRL_CNF0_Pos); //Вход Pull Up/Pull Down
  GPIOA->ODR |= (1 << 0); //Подтяжка вверх
  
  /*
    Настройка EXTI
  */
  AFIO->EXTICR &= ~(AFIO_EXTICR1_EXTI0); //Нулевой канал EXTI подключен к порту PA0
  
  EXTI->RTSR |= EXTI_RTSR_TR0; //Прерывание по нарастанию импульса
  EXTI->FTSR |= EXTI_FTSR_TR0; //Прерывание по спаду импульса
  
  EXTI->PR = EXTI_PR_PR0;      //Сбрасываем флаг прерывания 
                               //перед включением самого прерывания
  EXTI->IMR |= EXTI_IMR_MR0;   //Включаем прерывание 0-го канала EXTI
  
  NVIC_EnableIRQ(EXTI0_IRQn);  //Разрешаем прерывание в контроллере прерываний
}
void EXTI0_IRQHandler(void)
{
  EXTI->PR = EXTI_PR_PR0; //Сбрасываем флаг прерывания 
  asm("nop");
  asm("nop");
  asm("nop");
  asm("nop");
  asm("nop");
}

main()

void main(void)
{
  EXTI_Init();
  for(;;)
  {
  }
}

PA0EXTI0_IRQHandler()

Тут необходимо обратить внимание на кучу asm(«nop»)-ов в обработчике прерывания. Дело в том, что сразу после вызова EXTI->PR = EXTI_PR_PR0; необходимо хотя бы 2 такта процессора перед выходом из функции обработки прерывания, для того, чтобы флаг прерывания успел сброситься

Иначе обработчик прерывания будет вызван повторно. Поэтому сброс флага прерывания в регистре EXTI_PR желательно выполнять в начале обработчика.

Ну и напоследок небольшой эксперимент. В Reference manual-е сказано, что при работе пина микроконтроллера в качестве источника сигнала для EXTI он должен быть настроен как вход. А что будет, если этот пин настроить на выход? Как будет себя вести система?

void EXTI_Init(void)
{
  RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; //Тактирование GPIOA
  RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; //Тактирование AFIO
  
  
  /*
    Настройка GPIO
      Пин: PA0
      Режим: Output 
  */
  GPIOA->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0);
  GPIOA->CRL |= (0x00 << GPIO_CRL_CNF0_Pos) | (0x01 << GPIO_CRL_MODE0_Pos);
  
  /*
    Настройка EXTI
  */
  AFIO->EXTICR &= ~(AFIO_EXTICR1_EXTI0); //Нулевой канал EXTI подключен к порту PA0
  
  EXTI->RTSR |= EXTI_RTSR_TR0; //Прерывание по нарастанию импульса
  EXTI->FTSR |= EXTI_FTSR_TR0; //Прерывание по спаду импульса
  
  EXTI->PR = EXTI_PR_PR0;      //Сбрасываем флаг прерывания 
                               //перед включением самого прерывания
  EXTI->IMR |= EXTI_IMR_MR0;   //Включаем прерывание 0-го канала EXTI
  
  NVIC_EnableIRQ(EXTI0_IRQn);  //Разрешаем прерывание в контроллере прерываний
}

PA0PA0Open-drain

На этом предновогодняя 15-я статья по микроконтроллерам STM32 окончена, всех с наступающим 0x7E3 годом!  ?

Регистры GPIO

Давайте рассмотрим регистры портов GPIO.

Port configuration register low (GPIOx_CRL)

Рис. 1. Регистр CRL

Это конфигурационный регистр для выводов порта с номерами от 0 до 7. Каждому выводу предоставлено 4-ре бита конфигурации: 2 бита MODEy и 2 бита CNFy.

MODEy: Режим ножки порта, вход или выход. В режиме выхода нужно выбрать максимальную частоту переключения данной ножки, насколько понял это является оптимизацией энергопотребления порта.

  • 00: Вход (значение после сброса)
  • 01: Выход, максимальная частота 10 MHz.
  • 10: Выход, максимальная частота 2 MHz.
  • 11: Выход, максимальная частота 50 MHz.

CNFy: Конфигурация режима.

В режиме входа (MODEy=00):

  • 00: Analog mode — аналоговый режим (подключен к АЦП или ЦАП-у)
  • 01: Floating input — вход с отключенными подтягивающими резисторами (значение после сброса)
  • 10: Input with pull-up / pull-down — вход с подтяжкой вверх или вниз
  • 11: Reserved — не используется

В режиме выхода (MODEy>00):

  • 00: General purpose output push-pull — выход в режиме тяни/толкай
  • 01: General purpose output Open-drain — выход с открытым коллектором
  • 10: Alternate function output Push-pull — выход альтернативной функции режиме тяни/толкай
  • 11: Alternate function output Open-drain — выход альтернативной функции с открытым коллектором

Port configuration register high (GPIOx_CRH)

Рис. 2. Регистр CRH

Это конфигурационный регистр для выводов порта с номерами от 8 до 15. Тут все то же, что и для регистра GPIOx_CRL.

Рис. 3. Регистр IDR

IDRy: в этих битах содержится входное значение соответствующего порта ввода-вывода.

Рис. 4. Регистр ODR

ODRy: выходные данные порта.

Port bit set/reset register (GPIOx_BSRR)

Рис. 5. Регистр BSRR

С помощью этого регистра можно сбросить или установить любой бит регистра ODR без операций чтение-модификация-запись.

BRy: Сбросить бит у регистра ODR порта ввода-вывода (y= 0 .. 15)

  • 0: не оказывает влияние на соответствующий бит ODRx
  • 1: Сбрасывает в ноль соответствующий бит ODRx

BSy: Установить бит у регистра ODR порта ввода-вывода (y= 0 .. 15)

  • 0: не оказывает влияние на соответствующий бит ODRx
  • 1: Устанавливает в единицу соответствующий бит ODRx

Port bit reset register (GPIOx_BRR)

Рис. 6. Регистр BRR

С помощью этого регистра можно сбросить любой бит регистра ODR без операций чтение-модификация-запись.

BRy: Сбросить бит у регистра ODR порта ввода-вывода (y= 0 .. 15)

  • 0: не оказывает влияние на соответствующий бит ODRx
  • 1: Сбрасывает в ноль соответствующий бит ODRx

Port configuration lock register (GPIOx_LCKR)

Рис. 7. Регистр LCKR

Этот регистр используется для блокировки конфигурационных битов порта после записи корректной последовательности в 16 бит (LCKK) регистра. Значения битов  используется для блокировки конфигурации GPIO. Во время блокирующей последовательности в LCKK значения LCKR не должны меняться. Когда блокирующая последовательность была записана, конфигурация выбранных портов ввода/вывода может быть изменена только после сброса микроконтроллера. Каждый LCKy бит блокирует возможность изменения четырех битов конфигурации порта (CRL, CRH).

LCKK: Ключ блокировки.

  • 0: Блокировка конфигурации порта не активна.
  • 1: Блокировка конфигурации порта активна. GPIOx_LCKR заблокирован до следующего сброса микроконтроллера.

Для блокировки необходимо выполнить следующую последовательность:

  • Записать 1
  • Записать 0
  • Записать 1
  • Прочитать 0
  • Прочитать 1 (эта операция чтения не является обязательной, а всего лишь подтверждает успешность установки блокировки)

LCKy: Эти биты могут быть прочитаны и записаны, но запись можно произвести только если бит LCKK равен нулю.

  • 0: Конфигурация пина номер y не заблокирована
  • 1: Конфигурация пина номер y заблокирована

Перенаправить printf на порт SWO в STM32

http://www.dashashi.com/index.php/2014/03/1488

printf очень часто используется при программировании из командной строки.Хотя это старая функция, она мощная и долговечная.

51 и другие 8-битные однокристальные микрокомпьютеры имеют относительно небольшой стек из-за относительно небольшого объема ОЗУ, и запускать printf затруднительно.

Но на 32-битном однокристальном микрокомпьютере STM32 очень легко запускать printf, а в качестве метода отладки printf очень удобен и интуитивно понятен.

Более распространенный метод — перенаправить printf на последовательный порт, но для этого требуется внешний последовательный кабель, что вызывает больше проблем.

Фактически, порт SWO STM32 может выводить данные асинхронно, и никакого внешнего оборудования не требуется.

Поддерживаются отладчики с портом SWO, такие как ST-LINK / J-Link.

Возьмем для примера плату разработки STM32F4Discovery + GCC.

Согласно описанному здесь методу printf также может быть нацелен на другие периферийные устройства.

PS: IAR поставляется с функцией printf через SWO в параметрах компиляции, поэтому нет необходимости в дополнительных настройках.

http://community.silabs.com/t5/Microcontroller-How-to-Guides/SWO-printf-in-IAR/td-p/98257

Сначала поговорим о том, как выводить информацию в порт SWO, и это будет сделано одним предложением.

ITM_SendChar(ch);

Это встроенная функция, определенная в core_cm4.h (или core_cm3.h, если это серия F1).

Но нет необходимости специально включать этот файл заголовка, core_cm4.h включается косвенно через #include «stm32f4xx.h».

Но если говорить об этом, ITM — это, строго говоря, функция, предоставляемая Cortex-M, а не STM32.

Используйте эту функцию для вывода информации на порт SWO, а затем откройте утилиту St-Link,

Найдите ST-LINK → Printf через SWO Viewer в меню, и появится всплывающее окно,

Установите Системные часы на частоту ядра микроконтроллера и нажмите «Пуск», чтобы просмотреть информацию о выходе.

Следующим шагом является перенаправление вывода строки функцией printf.

Поскольку периферийные функции однокристального микрокомпьютера меняются в соответствии с требованиями, компилятор не может определить периферийные ресурсы, необходимые для printf.

Таким образом, он просто оставил интерфейс, то есть функцию _write,

Конечно, помимо функции _write, есть и другие функции, такие как _read, но здесь она нам не нужна.

Его объявление — int _write (int fd, char * ptr, int len);

Что касается функции _write, то для простоты все функции включают вывод строк.

Например, printf и putchar () в конечном итоге перейдут к функции _write, где fd — это идентификатор файла, что сложнее сказать.

Здесь мы получаем только STDOUT_FILENO и STDERR_FILENO,

Первая — это предопределенная переменная идентификатора файла стандартного вывода, а вторая — предопределенная переменная идентификатора файла вывода ошибок.

Вторая переменная ptr — это первый адрес выводимой строки, а len — это длина вывода.

Когда мы вызываем функцию printf, библиотека времени выполнения C преобразует входную переменную в конечную строку, которую необходимо вывести.

Затем вызовите функцию _write, чтобы вывести результат. Наша задача — реализовать функцию _write.

Создайте новый файл _write.c со следующим содержимым:

#include <stdio.h>
#include <unistd.h>

#include "stm32f10x.h"

#ifdef _DEBUG

int _write(int fd, char* ptr, int len)
{
if (fd == STDOUT_FILENO || fd == STDERR_FILENO)
{
int i = ;
while(i<len)
ITM_SendChar(ptr);
}
return len;
}

#endif

Эффект от добавления #ifdef _DEBUG заключается в игнорировании следующих вещей, когда _DEBUG не определен.

Поскольку эта вещь в основном используется на этапе отладки, она не используется в версии RELEASE и все равно будет влиять на скорость.

В остальном все очень просто — объяснять не нужно.

Прямая компиляция может пройти, но ссылка будет сообщать об ошибке, указывая на то, что не удается найти набор функций, таких как _read.

Добавьте libnosys.a (*) после libgcc.a (*) под сценарием ссылки, и никаких ошибок не будет.

Конкретная причина связана с реализацией библиотеки newlib, используемой Cortex-M3, поэтому я не буду подробно ее раскрывать.

Ладно, ну вообще-то я не знаю.

Если вы хотите найти информацию для последовательного порта, вы можете напрямую изменить ITM_SendChar на соответствующую функцию последовательного порта,

Вы также можете использовать DMA, чтобы сначала скопировать данные в буфер DMA, позволить DMA автоматически передавать данные и улучшить скорость отклика.

Стандартные функции ввод/вывод

Ввод и вывод информации осуществляется через функции стандартной библиотеки. Прототипы рассматриваемых функций находятся в файле stdio.h. Эта библиотека содержит функции:

— printf() — для вывода информации;

— scanf() — для ввода информации.

Чтобы понять, как настроить ввод/вывод информации рассмотрим подробнее данные функции. Структура стандартных функций ввода/вывода в Keil показана на рисунке 1.


Увеличить фото

Рисунок 1 — Структура стандартных функций ввода/вывода в Keil

Функции, которые доступны пользователю, находятся в самом верху данной структуры и называются высокоуровневыми (High-Level Functions). К таким функциям как раз и относятся printf() и scanf(). При их вызове обработка вводимой информации реализуется с помощью вызова низкоуровневых функций (Low-Level Functions), к ним относятся такие функции как fputc() и fgetc(). Данные функции, в свою очередь, вызывают системные функции, которые напрямую работают с периферией МК (System I/O Functions), например, с UART или CAN, перенаправляя на них поток данных. Для ввода/вывода информации по UART необходимо будет реализовать эти системные функции.

При желании можно не использовать Retarget.c от Keil, а самостоятельно объявить функции fputc() и fgetc(), в которых реализовать ввод/вывод по интересующему интерфейсу МК. Однако, в дополнение к функциям fputc() и fgetc() необходимо также объявить структуры:

Если этого не сделать, то низкоуровневые библиотеки Си будут реализовывать механизм semihosting’a, и в начале программы будет вызвана инструкция BKPT, переводящая процессор в режим debug. Выполнение программы при этом останавливается.

Рейтинг
( Пока оценок нет )
Понравилась статья? Поделиться с друзьями:
Все про сервера
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: