上上周写了S32K148的 GPIO
, 定时器
, 串口
, CAN
, 以太网
的系列博客:
https://blog.csdn.net/weifengdq/article/category/9369425
上周写了RISC-V之GD32VF103的 GPIO
, 定时器
, 串口
, DAC
, CAN发送
系列博客:
https://blog.csdn.net/weifengdq/article/category/9465635
本周工作任务稍重, 就整理点STM32的系列博客, 节省时间. 相信不少人是从微雪课堂的 STM32CubeMX系列教程 入坑的, 这套教程写的不错, 基于STM32CubeMX 4.x都可以用, 不过最新的STM32CubeMX已经走到了5.4.0版本(2019年11月), 界面稍微不一样, 有些接口也稍微变动.
这里以STM32CubeMX5.4.0为例, 片子就不分STM32L/F/G/H之类的, 基本通用, 手头有什么板子就用什么吧, 刚好手边有块NUCLEO-F767ZI. 至于IDE, 还用Keil 5.x, 毕竟人多坑少.
众所周知, STM32编程方式经历了寄存器, 标准库, HAL/LL库. 现在新出的片子基本都不支持标准库了, HAL确是万能的, 所以, 来吧, 上车.
ST官网的STM32CubeMX下载, 注册一个ST的帐号就可以愉快的下载了, 用EagleGet之类的工具下载的还是蛮快的. 如果下载比较慢, 不慌, stmcu.com.cn 下载, 有时候版本可能落后一丁点, 但更新还算及时.
下载安装完后打开, 然后Help -> Manage embedded software packages, 用哪个系列就把哪个系列的包给装上, 如本文用STM32F7系列, 就把最新的1.15的包给装上:
STM32CubeMX的配置就这么多, 接下来是Keil.
Keil 5.x的安装和破解就略了, 主要注意下 安装STM32的器件支持包:
用哪个系列就下哪个系列, 下来是一个.pack包, 双击安装即可, 会自动找到你keil的位置.
每块NUCLEO板子上面都是一个调试下载用的ST-Link, 插到联网的Win10应该会自动安装驱动, 没有也不要担心, 设备管理器右键本地找驱动, 驱动就在Keil的安装目录下, 如我的: C:\Keil_v5\ARM\STLink\USBDriver
.
NUCLEO-F767ZI 有 3个用户LED 和 1个按键, 引脚对应关系如下:
Name | PIN |
---|---|
LED1 (Green, LD1) | PB0 |
LED2 (Blue, LD2) | PB7 |
LED3 (Red, LD3) | PB14 |
BTN1 (User Button, B1) | PC13 |
打开STM32CubeMX, 通用一些, 这里不从板子创建, 选择MCU创建. 点击 ACCESS TO MCU SELECTOR
:
输入STM32F767ZI, 双击:
先配置调试下载端口为两根线的SWD方式, 如果不配置下载端口的话, 有可能会出现个别MCU只能下载一次的情况, 虽然可以救回来, 稍微复杂些罢了, 索性每次都配好没有后顾之忧, 可以看到PA13和PA14变绿:
RCC设置, 选择HSE(外部高速时钟)为Crystal/Ceramic Resonator(晶振/陶瓷谐振器), 一般外部有晶振的这么配就可以, NUCLEO-F767ZI的时钟是ST-LINK的MCO直接过来的8M时钟, 这么配也能工作:
直接在图上找到PB0, 单击, 弹出的菜单中选择GPIO_Output:
同样的方式, 配置另外两个LED的引脚为GPIO_Output, 配置按键的引脚为GPIO_Input.
可以引脚PB0上右击, 点击Enter User Label, 输入用户标签LED1, 也可以点击GPIO进行配置:
可以看到可以配置初始的输出电平是高还是低, GPIO模式这里默认推挽输出, 不上拉也不下拉, 最大输出速率低即可, 这里只改User Label
为LED1, 同样的方式命名PB7为LED2, PB14为LED3, PC13为BTN1.
接下来是时钟树的配置, 外部8M时钟, 所以选择HSE
, Input frequency
输入8
, 选上必经之路PLLCLK
, 然后HCLK
中提示216MHz max
, 那就敲入216, 打个回车, 会发现整个时钟树自己就开始自动配置了(最好联网), 很方便:
Project Manager -> Project里面填入工程名, 选择工程位置, IDE选MDK-ARM:
Code Generator里面勾选Copy only the necessary library files
, Generate peripheral initialization as a pair of .c/.h files per periphral
:
点击上图中右上角的GENERATE CODE
, 弹出如下窗口, 选择打开工程, 就可以看到keil工程自动打开了:
Keil配置Debug为ST-link, 并设置下载后自动复位运行:
如果工程较大, 嫌编译速度过慢, 可以去掉Output中的Browse Information的勾选:
这样, 重新编译会快很多, 坏处是没办法调转到定义了.
main, while中填入:
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
HAL_GPIO_TogglePin(LED3_GPIO_Port, LED3_Pin);
HAL_Delay(1000);
}
/* USER CODE END 3 */
注意代码写到 /* USER CODE BEGIN */
和 /* USER CODE END 3 */
, 这样STM32CubeMX下载自动生成代码不会覆盖自己写的. 编译, 下载, 可以看到板子上的3颗灯在闪烁了:
这里用到了GPIO的翻转, 事实上, 读写的函数也可以在stm32f7xx_hal_gpio.h或者.c中找到:
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);
void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
至于 HAL_Delay(1000);
就是延时1s的意思.
板子左下角的用户按键默认是低电平, 按下是高电平, 稍微修改下代码:
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(HAL_GPIO_ReadPin(BTN1_GPIO_Port, BTN1_Pin) == GPIO_PIN_SET) { //press button
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET); //led on
HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET);
} else {
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET); //led off
HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET);
}
}
/* USER CODE END 3 */
编译下载, 可以看到默认LED灭, 按下按键, 3个LED全亮; 松开按键, 3个LED全灭.
可以用按键释放的下降沿中断判断按键的按下, 关闭Keil工程, 回到STM32CubeMX工程, 单击PC13选择GPIO_EXTI13
, 选择下降沿中断:
注意GPIO mode
中还有Event
, 有兴趣的可以搜下 stm32 interrupt 和event的区别
.
中断的使能和优先级在NVIC中配置:
点击 Generate Code
重新生成代码, 打开工程. 可以发现:
startup_stm32f767xx.s
文件中可以找到中断向量表 DCD EXTI15_10_IRQHandler ; External Line[15:10]s
.stm32f7xx_it.c
中找到了函数 void EXTI15_10_IRQHandler(void)
调用了 HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
HAL_GPIO_EXTI_IRQHandler
, 发现清除中断标志位后调用了 __weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
这个函数即可.删掉上面while(1)
中写的代码, 在stm32f7xx_it.c
中添加:
/* USER CODE BEGIN 1 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if(GPIO_Pin == BTN1_Pin) {
//if(HAL_GPIO_ReadPin(BTN1_GPIO_Port, BTN1_Pin) == GPIO_PIN_RESET) {
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
HAL_GPIO_TogglePin(LED3_GPIO_Port, LED3_Pin);
//}
}
}
/* USER CODE END 1 */
编译下载, 按下按键可以看到LED翻转, 当然, 这里没有消抖, 体验极差.
https://download.csdn.net/download/weifengdq/11957133
欢迎扫描二维码关注本人微信公众号, 及时获取或者发送给我最新消息: