Микроконтроллеры STM32 построены на основе архитектуры ARM Cortex M. В настоящее время они становятся все более популярными благодаря их высокой производительности и относительно невысокой стоимости. Ранее мы рассматривали программирование платы STM32F103C8, также известной под названием STM32 Blue Pill ("синяя таблетка") с помощью Arduino IDE как с использованием внешнего FTDI программатора, так и через USB разъем.
Программирование платы STM32 с помощью Arduino IDE достаточно простое и открывает возможность использования множества библиотек, разработанных для платформы Arduino. В этом случае вам нет необходимости глубоко погружаться в изучение архитектуры ARM. Но в этой статье мы попробуем подняться на более высокий уровень в программировании плат STM32, который позволит нам не только улучшить структуру нашего программного кода, но также сэкономить объем памяти за счет неиспользования не нужных нам библиотек.
Для этого компания STMicroelectronics разработала инструмент под названием STM32Cube MX, который автоматически формирует базовый код для нашей программы исходя из используемой платы STM32 и подключенных к ней периферийных устройств. Таким образом, у нас отпадает необходимость написания кода для основных драйверов и периферийных устройств. В дальнейшем этот автоматически сгенерированный код можно отредактировать в программной среде Keil uVision. Законченную версию программы можно загрузить в плату STM32 с помощью программатора ST-Link, разработанного компанией STMicroelectronics.
В данной статье мы рассмотрим программирование платы STM32F103C8 (Blue Pill) с помощью Keil uVision и STM32CubeMX. В качестве периферийных устройств, подключенных к плате, будут использоваться кнопка и светодиод. Сначала мы автоматически сгенерируем код программы с помощью инструмента STM32Cube MX, а затем отредактируем его в программе Keil uVision.
Необходимые компоненты
Аппаратное обеспечение
- Плата разработки STM32F103C8 (STM32 Blue Pill) (купить на AliExpress).
- Программатор ST-LINK V2.
- Кнопка.
- Светодиод (купить на AliExpress).
- Макетная плата.
- Соединительные провода.
Программное обеспечение
- Инструмент генерации кода STM32CubeMX.
- Keil uVision 5.
- Драйверы для ST-Link V2.
ST-LINK V2
ST-LINK V2 представляет собой программатор со встроенным дебаггером для микроконтроллеров STM8 и STM32. С его помощью можно также загружать программный код для нашей платы отладки STM32F103C8. Для взаимодействия с микроконтроллерами STM8 и STM32 программатор ST-LINK V2 содержит интерфейсы SWIM (single wire interface module) и JTAG/ SWD (serial wire debugging). Программатор работает через USB интерфейс и может работать с такими интегрированными средами разработки как Atollic, IAR, Keil или TASKING
Внешний вид программатора ST-LINK V2 показан на следующем рисунке.
Показанный на рисунке программатор ST-LINK V2 поддерживает полный диапазон интерфейсов отладки SWD, который представляет собой простой 4-х проводный интерфейс и отличается хорошей стабильностью работы. Программатор ST-LINK V2 доступен во множестве цветовых решений и изготавливается из алюминиевого сплава. Для индикации режимов работы программатор содержит в своем составе светодиод синего цвета. Названия контактов подписаны на корпусе программатора.
Программатор ST-LINK V2 может подключаться к программному обеспечению Keil и с его помощью загружать код программы в микроконтроллеры STM32. Распиновка программатора ST-LINK V2 показана на следующем рисунке.
Примечание: во время первого подключения программатора ST-LINK V2 к компьютеру необходимо установить драйверы для работы с ним, которые можно скачать по следующей ссылке. Выбирайте драйвер, подходящий для вашей операционной системы.
STM32CubeMX
Инструмент STM32CubeMX представляет собой часть экосистемы STMCube, разработанной компанией STMicroelectronics. Данный инструмент позволяет значительно облегчить разработку программного кода для микроконтроллеров STM32. STM32CubeMX включает графическую оболочку и позволяет производить формирование программного кода на языке C с помощью графических инструментов. Далее этот программный код может быть использован в различных программах: keil uVision, GCC, IAR и т.д. Скачать STM32CubeMX можно по следующей ссылке.
Особенности STM32CubeMX:
- эффективное решающее устройство;
- помощник синхронизации;
- калькулятор потребляемой мощности;
- утилиты, осуществляющие конфигурацию периферийных устройств микроконтроллера, таких как контакты GPIO, USART и т.д.
- утилиты, осуществляющие программную конфигурацию стека протоколов TCP/IP, USB и т.д.
Схема проекта
Схема подключения светодиода, кнопки и программатора ST-LINK V2 к плате STM32F103C8 (Blue Pill) представлена на следующем рисунке.
Плата STM32 Blue Pill в нашем проекте получает питание от программатора ST-LINK, который, в свою очередь, подключен к USB порту компьютера. Поэтому нам не нужно отдельно подавать питание на плату STM32 Blue Pill. Схема соединений между платой STM32F103C8 и программатором ST-LINK V2 приведена в следующей таблице.
Плата STM32F103C8 | ST-LINK V2 |
GND | GND |
SWCLK | SWCLK |
SWDIO | SWDIO |
3V3 | 3.3V |
Светодиод в нашей схеме используется для индикации нажатия кнопки. Анод светодиода подключен к контакту PC13 платы STM32 Blue Pill, а катод подключен к общему проводу (земле).
Кнопка подключена к контакту PA1 платы STM32 Blue Pill с помощью подтягивающего резистора 10 кОм.
Внешний вид собранной конструкции проекта показан на следующем рисунке.
Создание и загрузка программы в плату STM32 с помощью Keil uVision и ST-Link
Выполните следующую последовательность шагов
Шаг 1. Первым делом установите все необходимые драйверы для программатора ST-LINK V2, программы STM32CubeMX и Keil uVision, а также необходимые пакеты для платы STM32F103C8.
Шаг 2. Запустите STM32CubeMX.
Шаг 3. В нем нажмите на New Project (новый проект).
Шаг 4. После этого выполните поиск и выберите микроконтроллер STM32F103C8.
Шаг 5. На экране появится распиновка платы STM32F103C8, на которой вы можете задать необходимую конфигурацию (режимы работы) контактов. Также вы можете выбрать контакты для подключения периферийных устройств.
Шаг 6. Также вы можете нажать на один из контактов в представленной распиновке, после чего появится список контактов, в котором вы можете задать их конфигурацию.
Шаг 7. Для нашего проекта мы выбрали для контакта PA1 режим работы GPIO INPUT (на ввод данных), для контакта PC13 – GPIO OUTPUT, для отладки (debug) SYS – SERIAL WIRE (к этим контактам мы будем подключать контакты ST-LINK SWCLK & SWDIO. Выбранные и сконфигурированные (selected & configured) контакты окрашиваются в зеленый цвет как показано на следующем рисунке.
Шаг 8. Далее под вкладкой конфигурации (Configuration tab) выберите GPIO чтобы установить конфигурацию (режимы работы) для контактов, которые мы выбрали на предыдущем шаге.
Шаг 9. Затем в окне настройки конфигурации контактов (pin configuration box) мы можем настроить пользовательские метки для используемых нами контактов, то есть дать этим контактам осмысленные имена.
Шаг 10. После этого выберите пункт меню Project >> Generate Code.
Шаг 11. Появится диалоговое окно с настройками проекта. В этом окне вам необходимо ввести имя проекта и его местоположение, а также используемый инструментарий. Мы используем программу Keil, поэтому в качестве интегрированной среды разработки (IDE) выберите MDK-ARMv5.
Шаг 12. Далее под вкладкой генерации кода (Code Generator tab) выберите пункт "Copy only the necessary library files" и затем нажмите OK.
Шаг 13. Появится диалоговое окно генерации кода. Выберите Open Project чтобы автоматически открыть генерацию кода в Keil uvsion.
Шаг 14. Откроется инструмент Keil uVision с нашим автоматически сгенерированным кодом в STM32CubeMx и нашим введенным именем проекта с необходимыми подключенными библиотеками и кодами для контактов, работу которых мы настроили ранее.
Шаг 15. Теперь нам необходимо настроить логику работы программы. Например, включать светодиод (контакт PC13) при нажатии кнопки (контакт PA1) и выключать светодиод при отпускании кнопки. Поэтому выберите файл main.c чтобы включить в него необходимые фрагменты кода.
Шаг 16. Добавьте следующий фрагмент кода в цикл while(1).
1 2 3 4 5 6 7 8 9 10 11 12 |
while (1) { if(HAL_GPIO_ReadPin(BUTN_GPIO_Port,BUTN_Pin)==0) //=> DETECTS Button is Pressed { HAL_GPIO_WritePin(LEDOUT_GPIO_Port,LEDOUT_Pin,1); //To make output high when button pressesd } else { HAL_GPIO_WritePin(LEDOUT_GPIO_Port,LEDOUT_Pin,0); //To make output Low when button de pressed } } |
На следующем рисунке выделено цветом куда нужно вставить наш код.
Шаг 17. После того, как вы завершите редактирование кода, нажмите иконку Options for Target и в открывшемся окне на вкладке отладки (debug tab) выберите ST-LINK Debugger.
Также нажмите на кнопку Settings (настройки) и на вкладке Flash Download выберите пункт Reset and Run, после чего нажмите ‘ok’.
Шаг 18. Затем нажмите на иконку Rebuild чтобы заново скомпилировать все наши файлы.
Шаг 19. После этого вы можете подключить программатор ST-LINK к компьютеру и нажать на иконку DOWNLOAD или клавишу F8 чтобы загрузить в плату STM32F103C8 код программы, который мы сначала автоматически сгенерировали, а потом вручную отредактировали.
Шаг 20. Вы должны заметить индикацию загрузки программы внизу окна программы keil uVision.
Для тестирования работы нашего проекта нажмите кнопку в собранной конструкции проекта, при этом светодиод должен загореться. При отпускании кнопки светодиод должен погаснуть.
Исходный код программы (скетча)
Как мы уже рассматривали ранее, в автоматический сгенерированный STM32CubeMX код программы мы должны добавить следующие строчки кода (в файл main.c):
1 2 3 4 5 6 7 8 9 10 11 12 |
while (1) { if(HAL_GPIO_ReadPin(BUTN_GPIO_Port,BUTN_Pin)==0) //=> обнаруживаем нажатие кнопки { HAL_GPIO_WritePin(LEDOUT_GPIO_Port,LEDOUT_Pin,1); //включаем светодиод если кнопка нажата } else { HAL_GPIO_WritePin(LEDOUT_GPIO_Port,LEDOUT_Pin,0); //выключаем светодиод когда кнопка отжата } } |
Полный получившийся у нас код программы выглядит следующим образом.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
/** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** ** This notice applies to any and all portions of this file * that are not between comment pairs USER CODE BEGIN and * USER CODE END. Other portions of this file, whether * inserted by the user or by software development tools * are owned by their respective copyright owners. * * COPYRIGHT(c) 2018 STMicroelectronics * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of STMicroelectronics nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "stm32f1xx_hal.h" /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* Private variables ---------------------------------------------------------*/ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); /* USER CODE BEGIN PFP */ /* Private function prototypes -----------------------------------------------*/ /* USER CODE END PFP */ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * * @retval None */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration----------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { if(HAL_GPIO_ReadPin(BUTN_GPIO_Port,BUTN_Pin)==0) //=> Button is Pressed { HAL_GPIO_WritePin(LEDOUT_GPIO_Port,LEDOUT_Pin,1); } else //=>Button is released { HAL_GPIO_WritePin(LEDOUT_GPIO_Port,LEDOUT_Pin,0); } /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct; RCC_ClkInitTypeDef RCC_ClkInitStruct; /**Initializes the CPU, AHB and APB busses clocks */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = 16; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); } /**Initializes the CPU, AHB and APB busses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); } /**Configure the Systick interrupt time */ HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000); /**Configure the Systick */ HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); /* SysTick_IRQn interrupt configuration */ HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0); } /** Configure pins as * Analog * Input * Output * EVENT_OUT * EXTI */ static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(LEDOUT_GPIO_Port, LEDOUT_Pin, GPIO_PIN_RESET); /*Configure GPIO pin : LEDOUT_Pin */ GPIO_InitStruct.Pin = LEDOUT_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(LEDOUT_GPIO_Port, &GPIO_InitStruct); /*Configure GPIO pin : BUTN_Pin */ GPIO_InitStruct.Pin = BUTN_Pin; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(BUTN_GPIO_Port, &GPIO_InitStruct); } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @param file: The file name as string. * @param line: The line in file as a number. * @retval None */ void _Error_Handler(char *file, int line) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ while(1) { if(HAL_GPIO_ReadPin(BUTN_GPIO_Port,BUTN_Pin)==0) //=> DETECTS Button is Pressed { HAL_GPIO_WritePin(LEDOUT_GPIO_Port,LEDOUT_Pin,1); //To make output high when button pressesd } else { HAL_GPIO_WritePin(LEDOUT_GPIO_Port,LEDOUT_Pin,0); //To make output Low when button de pressed } } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t* file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ /** * @} */ /** * @} */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |