“NBDK-L4:基础实验教程”的版本间的差异
(→实验19-窗口看门狗) |
|||
第2,864行: | 第2,864行: | ||
[[文件:NBDK-SCH-LED.png|边框|居中|无框|386x386像素]] | [[文件:NBDK-SCH-LED.png|边框|居中|无框|386x386像素]] | ||
''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span>'' | ''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span>'' | ||
+ | === 实验准备 === | ||
+ | # 使用miniUSB线及10pin排线,通过Jlink仿真器连接PC端和开发板。 | ||
+ | # 使用miniUSB线,连接PC与开发板USB接口。 | ||
+ | # 使用Keil打开基础实验的实验18-独立看门狗。 | ||
+ | # 下载程序,并完成功能测试。 | ||
+ | |||
+ | === 实验验证 === | ||
+ | 下载完成后,可以看到LED保持常亮,说明我们的程序没有被复位。而当我们注释掉HAL_WWDG_EarlyWakeupCallback()回调函数中的喂狗函数HAL_WWDG_Refresh(hwwdg),重新编译下载,可以看到LED灯周期闪烁,说明程序一直被复位(因为没有喂狗)。 | ||
+ | |||
+ | === 源码详解 === | ||
+ | 本节中的源码说明,仅针对此例程中的重要功能,详细的源码介绍请大家参照代码后的注释。 | ||
+ | |||
+ | ==== stm32l4xx_hal_conf.h ==== | ||
+ | 此文件位于“实验16-RTC实时时钟\Inc”路径中,主要用途是选择使能此例程使用到的库文件。 | ||
+ | |||
+ | 此例程我们主要给大家展示STM32L4的窗口看门狗功能,所以我们宏定义中打开WWDG相关的。<syntaxhighlight lang="c" line="1" start="103"> | ||
+ | // 使能的宏 | ||
+ | #define HAL_MODULE_ENABLED // 芯片 | ||
+ | #define HAL_FLASH_MODULE_ENABLED // Flash | ||
+ | #define HAL_PWR_MODULE_ENABLED // 电源 | ||
+ | #define HAL_RCC_MODULE_ENABLED // 时钟 | ||
+ | #define HAL_CORTEX_MODULE_ENABLED // NVIC | ||
+ | |||
+ | #define HAL_GPIO_MODULE_ENABLED // GPIO | ||
+ | #define HAL_DMA_MODULE_ENABLED // DMA | ||
+ | #define HAL_UART_MODULE_ENABLED // UART | ||
+ | #define HAL_SPI_MODULE_ENABLED // SPI | ||
+ | #define HAL_WWDG_MODULE_ENABLED // 窗口看门狗 | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ==== main.c ==== | ||
+ | main函数,我们的例程由此处开始执行,首先调用HAL_Init()函数初始化我们的模块,接着调用SystemClock_Config()函数初始化此例程用到的时钟,具体有哪些时钟被初始化,在gyu_util.c部分有详细说明。 | ||
+ | |||
+ | 接下来我们初始化了LED,设置LED点亮,在LED点亮之前我们延时500ms,用来展示程序是否被窗口看门狗重启。 | ||
+ | |||
+ | 最后我们初始化窗口看门狗。 | ||
+ | |||
+ | <syntaxhighlight lang="c++" line="1" start="32"> | ||
+ | int main(void) | ||
+ | { | ||
+ | /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ | ||
+ | // 重置所有外设、flash界面以及系统时钟 | ||
+ | HAL_Init(); | ||
+ | |||
+ | // 配置系统时钟(包含振荡器、系统时钟、总线时钟等等) | ||
+ | SystemClock_Config(); | ||
+ | |||
+ | // 初始化LED引脚 | ||
+ | LED_Init(); | ||
+ | |||
+ | // 延时500ms,展示软件是否重启(重启则LED闪烁,不重启则LED常亮) | ||
+ | HAL_Delay(500); | ||
+ | LED_SET(GPIO_PIN_SET); | ||
+ | |||
+ | // 初始化窗口看门狗 | ||
+ | MX_WWDG_Init(); | ||
+ | |||
+ | // | ||
+ | while (1) | ||
+ | { | ||
+ | |||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | ==== gyu_util.c ==== | ||
+ | 请参照实验01中的介绍。 | ||
+ | |||
+ | 基础实验中的其他例程,大部分都是使用的相同的时钟配置函数,有特殊的时钟使用,将会在对应例程的源码详解中做针对性说明。 | ||
+ | ==== gyu_iwdg.c ==== | ||
+ | 窗口看门狗初始化函数,初始化窗口看门狗时钟8分频(时钟来源PCLK1 80MHz),配置上窗口值0x50,下窗口值默认0x40,并且使能唤醒中断(可以在唤醒中断中喂狗)。 | ||
+ | |||
+ | 利用窗口看门狗的喂狗时间计算公式,我们分别得到上下窗口值的时间: | ||
+ | |||
+ | 上窗口(0x50):33.17ms | ||
+ | |||
+ | 下窗口(0x40):26.21ms | ||
+ | |||
+ | <syntaxhighlight lang="c++" line="1" start="46"> | ||
+ | void MX_WWDG_Init(void) | ||
+ | { | ||
+ | hwwdg.Instance = WWDG; // 配置为WWDG | ||
+ | hwwdg.Init.Prescaler = WWDG_PRESCALER_8; // 8分频,WDGTB = 3 | ||
+ | hwwdg.Init.Window = 0x50; // 上窗口值为0x50 | ||
+ | hwwdg.Init.Counter = 0x7F; // 计数CNT为0x7F(最大值) | ||
+ | hwwdg.Init.EWIMode = WWDG_EWI_ENABLE; // 使能唤醒中断 | ||
+ | if (HAL_WWDG_Init(&hwwdg) != HAL_OK) // 初始化WWDG,出错则进入错误处理函数 | ||
+ | { | ||
+ | _Error_Handler(__FILE__, __LINE__); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight>窗口看门狗初始化,使能窗口看门狗时钟,使能中断。<syntaxhighlight lang="c++" line="1" start="67"> | ||
+ | void HAL_WWDG_MspInit(WWDG_HandleTypeDef *hwwdg) | ||
+ | { | ||
+ | __HAL_RCC_WWDG_CLK_ENABLE(); //使能窗口看门狗时钟 | ||
+ | |||
+ | HAL_NVIC_SetPriority(WWDG_IRQn, 10, 0); //抢占优先级10,子优先级为0 | ||
+ | HAL_NVIC_EnableIRQ(WWDG_IRQn); //使能窗口看门狗中断 | ||
+ | } | ||
+ | </syntaxhighlight>窗口看门狗唤醒中断回调函数,我们可以在这个函数中去调用喂狗的函数。<syntaxhighlight lang="c++" line="1" start="83"> | ||
+ | void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef* hwwdg) | ||
+ | { | ||
+ | HAL_WWDG_Refresh(hwwdg); // 喂狗 | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | == 实验20-外部Flash == | ||
+ | 窗口看门狗实验,STM32L4内部自带两个看门狗,分别叫做“独立看门狗(IWDG)”以及“窗口看门狗(WWDG)”。这一章节给大家介绍一下窗口看门狗的使用,窗口看门狗相对于独立看门狗的好处在于喂狗的时间精确可控,但是需要使用PCLK1的时钟,而不是独立的时钟源。 | ||
+ | |||
+ | === STM32L476 SPI简介 === | ||
+ | SPI接口可用于使用SPI协议与外部设备通信。SPI模式可由软件选择。 器件复位后,默认选择SPI Motorola模式。 | ||
+ | |||
+ | 串行外设接口(SPI)协议支持与外部设备的半双工,全双工和单工同步串行通信。 接口可以配置为主设备,在这种情况下,它为外部从设备提供通信时钟(SCK)。 该接口还能够在多主机配置中运行。 | ||
+ | |||
+ | === 外部Flash W25Q80简介 === | ||
+ | ''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span>'' | ||
+ | === 硬件设计 === | ||
+ | 选择STM32L4 SPI1引脚(PA5、PA6、PA7),选择PB2作为flash芯片W25Q80的CSN片选引脚。 | ||
+ | [[文件:NBDK-SCH-FLASH.png|边框|居中|无框|638x638像素]] | ||
+ | ''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span>'' | ||
=== 实验准备 === | === 实验准备 === | ||
# 使用miniUSB线及10pin排线,通过Jlink仿真器连接PC端和开发板。 | # 使用miniUSB线及10pin排线,通过Jlink仿真器连接PC端和开发板。 |
2019年2月20日 (三) 13:49的版本
目录
1 教程介绍
1.1 工程简介
NBDK-L4开发板基础实验包含如下,在这里给大家简单说明一下每个例程中讲解的内容及关键节点。
实验名称 | 内容简介 | 功能 |
---|---|---|
实验01-led点灯 | 驱动LED点亮 | GPIO推挽输出 |
实验02-振动马达 | 驱动马达振动 | GPIO推挽输出 |
实验03-蜂鸣器 | 驱动蜂鸣器发声 | GPIO推挽输出 |
实验04-按键中断 | 按键控制 | GPIO外部中断 |
实验05-光敏二极管 | 采集光敏二极管数据 | ADC采集 |
实验06-DAC模拟输出 | 引脚输出模拟电平 | DAC输出 |
实验07-温湿度采集 | 采集SHT20温湿度数据 | I2C接口使用、SHT20使用 |
实验08-RGB | 定时器TIM2 PWM输出 | |
实验09-红外接收 | 红外接收器获取遥控器按键信息 | 定时器TIM3捕获模式 |
实验10-串口打印 | 串口格式化输出数据 | 串口使用,printf使用 |
实验11-串口中断 | ||
实验12-串口DMA | ||
实验13-TFT显示屏 | ||
实验14-二维码显示 | ||
实验15-RNG随机发生器 | ||
实验16-RTC实时时钟 | ||
1.2 工程目录简介
大家打开任意一个基础例程,都会看到如下的4个目录(Drivers、Inc、MDK-ARM、Src)及clean.bat文件。
其中clean.bat是用于清除工程编译生成的中间文件。例如我们想拷贝一个编译过的工程,工程有200M左右大小,我们点击clean.bat清除一下编译生成的中间文件,则工程大概会缩小到100M左右,此时工程只剩下了库文件、用户文件,以及编译生成的hex文件。
从上图可以看到,四个主目录下分别包含的一些文件,这边给大家简单的介绍一下这边文件大概的功能。
Drivers:
STM32驱动文件目录,也就是大家常说的hal库,里面包含了hal(硬件抽象层)相关的文件。
主要就是有RCC时钟、Flash内存,以及大家常用的外设(例如uart、spi、adc等等)的一些库文件。
Inc:
用户.h头文件,用户文件的头文件一般都放到这边,也可自己另存其他位置,但是需要在keil中添加头文件所在的路径。
添加新路径的方式如下图所示,可以看到../Inc这个路径已经事先添加进去了。
MDK-ARM:
工程目录,主要是两个工程文件“.uvoptx”以及".uvprojx"(keil打开的是这个)。剩下的文件比较重要的是Output目录下编译生成的“.hex”文件。
Src:
用户.c文件,用户自己开发的一些驱动文件(外设驱动等等),以及main文件所在的目录。
1.3 常用文件简介
针对试验工程中的常见文件,我们以开发者的方式来给大家做一个简单的介绍。具体每个文件中的源码的详细说明,大家可以参照每个试验下的源码详解。
文件名 | 说明 |
---|---|
stm32l4xx_hal_conf.h | 路径位于Inc目录下,里面主要是一些宏定义,用于选择本工程所使用的库文件。这边选择的库文件,就是目录简介中提到的HAL库。 |
main.c: | main()所在的文件,keil中我们配置了run to main(),也就是说工程从main()函数开始执行,所以main.c就是我们工程的主文件。 |
stm32l4xx_hal_msp.c | msp(main stack pointer)主栈堆指针初始化的文件。我们重定义外设引脚选择的时候,STM32Cube生成的硬件引脚重定义函数默认也位于此文件下,但是为了方便,我们一般将其复制到各自的驱动文件下。 |
stm32l4xx_it.c | 中断配置文件,用于存放工程的中断。STM32Cube生成的中断函数默认位于此文件下,同样为了方便起见,我们一般将各自的中断函数放到各自的驱动文件下。 |
gyu_util.c | 从STM32Cube生成的main文件中独立出来的部分。主要用于处理工程的时钟选择,包含系统时钟、总线时钟以及外设时钟。 |
stm32l4xx_hal_xx | HAL库文件。 |
gyu_xx | 由谷雨物联编写的文件,大部分是外设的驱动文件。 |
2 实验01-led点灯
第一个实验我们给大家带来的是最简单的外设控制,也就是 IO 口操作,通过这个实验我们可以了解到如何让STM32L476RC的一个 IO 输出高低电平,并以此控制 LED 的点亮和熄灭。
2.1 STM32L476 IO简介
每个GPIO引脚都可以通过软件配置为输出(推挽或漏极开路),输入(带或不带上拉或下拉)或外设备用功能。 大多数GPIO引脚与数字或模拟备用功能共用。 由于它们在AHB2总线上的映射,可以实现快速I / O切换。 如果需要,可以锁定I / O备用功能配置序列,以避免虚假写入I / O寄存器。
经过上一段对GPIO口模式的说明,在这里对它的工作模式进行一个小结,它一共有八种组合,即有八种可配置的工作模式,分别是:
- 输入浮空
- 输入上拉
- 输入下拉
- 模拟
- 带上拉或下拉的开漏输出
- 带上拉或下拉的推挽输出
- 带上拉或下拉的复用功能推挽
- 带上拉或下拉的复用功能开漏
2.2 硬件设计
选择STM32L4引脚PA15作为LED的控制引脚,PA15高电平时点亮LED。
2.3 实验准备
- 使用miniUSB线及10pin排线,通过Jlink仿真器连接PC端和开发板。
- 使用Keil打开基础实验的实验01-led点灯。
- 下载程序,并完成功能测试。
2.4 实验验证
下载完成后,可以看到开发板上的LED灯周期闪烁,点亮及熄灭的周期时间为500ms。
2.5 源码详解
本节中的源码说明,仅针对此例程中的重要功能,详细的源码介绍请大家参照代码后的注释。
2.5.1 stm32l4xx_hal_conf.h
此文件位于“实验01-led点灯\Inc”路径中,主要用途是选择使能此例程使用到的库文件,一般情况下,我们默认需要使用的为前5个,包含芯片、flash、电源、时钟以及NVIC。
此例程因为我们需要展示IO的使用,所以我们额外使能 HAL_GPIO_MODULE_ENABLED。
103 // 使能的宏
104 #define HAL_MODULE_ENABLED // 芯片
105 #define HAL_FLASH_MODULE_ENABLED // Flash
106 #define HAL_PWR_MODULE_ENABLED // 电源
107 #define HAL_RCC_MODULE_ENABLED // 时钟
108 #define HAL_CORTEX_MODULE_ENABLED // NVIC
109
110 #define HAL_GPIO_MODULE_ENABLED // GPIO
2.5.2 main.c
main函数,我们的例程由此处开始执行,首先调用HAL_Init()函数初始化我们的模块,接着调用SystemClock_Config()函数初始化此例程用到的时钟,具体有哪些时钟被初始化,在gyu_util.c部分有详细说明。
接下来我们初始化LED引脚配置,并且在while()循环中周期点亮、熄灭LED。
31 int main(void)
32 {
33 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
34 // 重置所有外设、flash界面以及系统时钟
35 HAL_Init();
36
37 // 配置系统时钟(包含振荡器、系统时钟、总线时钟等等)
38 SystemClock_Config();
39
40 // 初始化LED引脚
41 LED_Init();
42
43 //
44 while (1)
45 {
46 LED_SET(GPIO_PIN_SET); // 设置LED引脚(PA15)输出高电平,LED点亮
47 HAL_Delay(500); // 延时500ms
48 LED_SET(GPIO_PIN_RESET); // 设置LED引脚(PA15)输出低电平,LED熄灭
49 HAL_Delay(500); // 延时500ms
50 }
51 }
2.5.3 gyu_util.c
时钟初始化函数,用于配置我们模块运行的系统时钟、AHB高性能总线时钟、APB外设总线时钟以及单个外设的时钟。
主要包含了三个部分的初始化配置。
- 内部或者外部振荡器选择,也就是选择时钟信号的来源,是内部振荡,还是外部晶振。
- 时钟配置,选择系统、AHB总线及APB总线的时钟来源。
- 外设时钟配置,选择外设时钟来源。
为了给大家比较全面的展示各个时钟,我们振荡器选择HSI(内部16MHz高频)、HSE(外部8MHz高频)以及LSE(外部32.768KHz低频)三个。选择HSE作为PLL(锁相回路)时钟源,配置PLLCLK为80MHz。配置系统时钟SYSCLK、AHB高性能总线、APB外设总线(APB1及APB2)为80MHz。另外我们还分别配置了ADC、UART以及I2C的外设时钟。
基础实验中的其他例程,大部分都是使用的相同的时钟配置函数,有特殊的时钟使用,将会在对应例程的源码详解中做针对性说明。
49 void SystemClock_Config(void)
50 {
51 RCC_OscInitTypeDef RCC_OscInitStruct; // 定义RCC内部/外部振荡器结构体
52 RCC_ClkInitTypeDef RCC_ClkInitStruct; // 定义RCC系统,AHB和APB总线时钟配置结构体
53 RCC_PeriphCLKInitTypeDef PeriphClkInit; // 定义RCC扩展时钟结构体
54
55 // 配置LSE驱动器功能为低驱动能力
56 __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_LOW);
57
58 // 初始化CPU,AHB和APB总线时钟
59 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSE
60 |RCC_OSCILLATORTYPE_LSE; // 设置需要配置的振荡器为HSI、HSE、LSE
61 // 配置HSE
62 RCC_OscInitStruct.HSEState = RCC_HSE_ON; // 激活HSE时钟(开发板外部为8MHz)
63 // 配置LSE
64 RCC_OscInitStruct.LSEState = RCC_LSE_ON; // 激活LSE时钟(32.768KHz,低驱动)
65 // 配置HSI
66 RCC_OscInitStruct.HSIState = RCC_HSI_ON; // 激活HSI时钟
67 RCC_OscInitStruct.HSICalibrationValue = 16; // 配置HSI为16MHz
68 // 配置PLL
69 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // 打开PLL
70 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; // 选择HSE时钟作为PLL入口时钟源,8MHz
71 RCC_OscInitStruct.PLL.PLLM = 1; // 配置PLL VCO输入分频为1,8/1 = 8MHz
72 RCC_OscInitStruct.PLL.PLLN = 20; // 配置PLL VCO输入倍增为20,8MHz*20 = 160MHz
73 RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7; // SAI时钟7分频,160/7 = 22.857143MHz
74 RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; // SDMMC、RNG、USB时钟2分频,160/2 = 80MHz
75 RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; // 系统主时钟分区2分频,160/2 = 80MHz
76 // RCC时钟配置,出错则进入错误处理函数
77 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
78 {
79 _Error_Handler(__FILE__, __LINE__);
80 }
81
82 // 初始化CPU,AHB和APB总线时钟
83 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
84 |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; // 需要配置的时钟HCLK、SYSCLK、PCLK1、PCLK2
85 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 配置系统时钟为PLLCLK输入,80MHz
86 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB时钟为系统时钟1分频,80/1 = 80MHz
87 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; // APB1时钟为系统时钟1分频,80/1 = 80MHz
88 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // APB2时钟为系统时钟1分频,80/1 = 80MHz
89 // RCC时钟配置,出错则进入错误处理函数
90 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) // HCLK=80MHz,Vcore=3.3V,所以选择SW4(FLASH_LATENCY_4)
91 {
92 _Error_Handler(__FILE__, __LINE__);
93 }
94
95 // 初始化外设时钟
96 PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_USART2
97 |RCC_PERIPHCLK_LPUART1|RCC_PERIPHCLK_LPTIM1
98 |RCC_PERIPHCLK_I2C2|RCC_PERIPHCLK_ADC; // 需要初始化的外设时钟:USART1、USART2、LPUART1、LPTIM1、I2C2、ADC
99 PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2; // 配置串口USART1时钟为PCLK2,80MHz
100 PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1; // 配置串口USART2时钟为PCLK1,80MHz
101 PeriphClkInit.Lpuart1ClockSelection = RCC_LPUART1CLKSOURCE_HSI; // 配置LPUART时钟为HSI,16MHz
102 PeriphClkInit.I2c2ClockSelection = RCC_I2C2CLKSOURCE_PCLK1; // 配置I2C2时钟为PCLK1,80MHz
103 PeriphClkInit.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSE; // 配置LPTIM1时钟为LSE,32.768KHz
104 PeriphClkInit.AdcClockSelection = RCC_ADCCLKSOURCE_PLLSAI1; // 配置ADC时钟为PLLSAI1,现在为80MHz,下面会重新定义
105 PeriphClkInit.PLLSAI1.PLLSAI1Source = RCC_PLLSOURCE_HSE; // 配置PLLSAI1时钟为HSE,8MHz
106 PeriphClkInit.PLLSAI1.PLLSAI1M = 1; // 配置PLLSAI1分频为1
107 PeriphClkInit.PLLSAI1.PLLSAI1N = 8; // 配置PLLSAI1倍增为8
108 PeriphClkInit.PLLSAI1.PLLSAI1P = RCC_PLLP_DIV7; // SAI时钟7分频,64/7 = 9.142857MHz
109 PeriphClkInit.PLLSAI1.PLLSAI1Q = RCC_PLLQ_DIV2; // SDMMC、RNG、USB时钟2分频,64/2 = 32MHz
110 PeriphClkInit.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV2; // 系统主时钟分区2分频,64/2 = 32MHz
111 PeriphClkInit.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_ADC1CLK; // 配置PLLSAI1输出为ADC1时钟,也就是配置ADC1时钟,32MHz
112 // 外设时钟配置,出错则进入错误处理函数
113 if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
114 {
115 _Error_Handler(__FILE__, __LINE__);
116 }
117
118 // 配置内部主稳压器输出电压,配置为稳压器输出电压范围1模式,也就是:典型输出电压为1.2V,系统频率高达80MHz
119 if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
120 {
121 _Error_Handler(__FILE__, __LINE__);
122 }
123
124 // 配置系统定时器中断时间,配置为HCLK的千分频
125 HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
126
127 // 配置系统定时器,配置为HCLK
128 HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
129
130 // 系统定时器中断配置,设置系统定时器中断优先级最高(为0),且子优先级最高(为0)
131 HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
132 }
2.5.4 gyu_led.c
此文件用于配置LED控制引脚,在LED_Init()函数中我们初始化PA15为推挽输出,并且使能GPIOA时钟,初始化PA15默认输出低电平。
31 void LED_Init(void)
32 {
33 GPIO_InitTypeDef GPIO_InitStructure; // 定义引脚参数结构体
34
35 __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
36
37 GPIO_InitStructure.Pin= GPIO_PIN_15; // 引脚编号为15
38 GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
39 GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; // 低频率
40 GPIO_InitStructure.Pull = GPIO_PULLDOWN; // 下拉
41 HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化PA15
42
43 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET); // 设置PA15默认输出低电平
44 }
LED_SET()函数留给大家控制LED灯点亮或者熄灭,参数可选为GPIO_PIN_RESET(低电平)或者GPIO_PIN_SET(高电平)。
54 void LED_SET(GPIO_PinState pinSate)
55 {
56 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, pinSate); // 设置PA15输出
57 }
3 实验02-振动马达
振动马达实验通过控制 GPIO 引脚输出高低电平,用于控制马达振动或停止 。
3.1 STM32L476 IO简介
每个GPIO引脚都可以通过软件配置为输出(推挽或漏极开路),输入(带或不带上拉或下拉)或外设备用功能。 大多数GPIO引脚与数字或模拟备用功能共用。 由于它们在AHB2总线上的映射,可以实现快速I / O切换。 如果需要,可以锁定I / O备用功能配置序列,以避免虚假写入I / O寄存器。
经过上一段对GPIO口模式的说明,在这里对它的工作模式进行一个小结,它一共有八种组合,即有八种可配置的工作模式,分别是:
- 输入浮空
- 输入上拉
- 输入下拉
- 模拟
- 带上拉或下拉的开漏输出
- 带上拉或下拉的推挽输出
- 带上拉或下拉的复用功能推挽
- 带上拉或下拉的复用功能开漏
3.2 硬件设计
选择STM32L4引脚PC7作为马达的控制引脚,PC7高电平时马达起振。
3.3 实验准备
- 使用miniUSB线及10pin排线,通过Jlink仿真器连接PC端和开发板。
- 使用Keil打开基础实验的实验02-振动马达。
- 下载程序,并完成功能测试。
3.4 实验验证
下载完成后,按下开发板上按键S1,马达起振,按下S3,马达停止。
3.5 源码详解
本节中的源码说明,仅针对此例程中的重要功能,详细的源码介绍请大家参照代码后的注释。
3.5.1 stm32l4xx_hal_conf.h
此文件位于“实验02-振动马达\Inc”路径中,主要用途是选择使能此例程使用到的库文件,一般情况下,我们默认需要使用的为前5个,包含芯片、flash、电源、时钟以及NVIC。
此例程因为我们需要展示IO的使用,所以我们额外使能 HAL_GPIO_MODULE_ENABLED。
103 // 使能的宏
104 #define HAL_MODULE_ENABLED // 芯片
105 #define HAL_FLASH_MODULE_ENABLED // Flash
106 #define HAL_PWR_MODULE_ENABLED // 电源
107 #define HAL_RCC_MODULE_ENABLED // 时钟
108 #define HAL_CORTEX_MODULE_ENABLED // NVIC
109
110 #define HAL_GPIO_MODULE_ENABLED // GPIO
3.5.2 main.c
main函数,我们的例程由此处开始执行,首先调用HAL_Init()函数初始化我们的模块,接着调用SystemClock_Config()函数初始化此例程用到的时钟,具体有哪些时钟被初始化,在gyu_util.c部分有详细说明。
接下来是初始化按键,有关按键的部分会在04-按键实验中给大家讲解。
这边我们主要关注的是马达的初始化,其实是和LED实验一样的,就是初始化一下马达控制IO。
36 int main(void)
37 {
38 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
39 // 重置所有外设、flash界面以及系统时钟
40 HAL_Init();
41
42 // 配置系统时钟(包含振荡器、系统时钟、总线时钟等等)
43 SystemClock_Config();
44
45 // 初始化按键引脚
46 MX_KEY_Init();
47
48 //注册按钮回调函数
49 KEY_RegisterCb(AppKey_cb);
50
51 // 初始化马达
52 Motor_Init();
53
54 //
55 while (1)
56 {
57 KEY_Poll(); // 按键轮训,监测是否有按键被按下
58 }
59 }
在按键的处理回调函数中,我们可以看到,按键S1(UP)按下后,设置马达引脚高电平,按键S3(DOWN)按下后,设置马达引脚低电平
69 void AppKey_cb(uint8_t key)
70 {
71 // 如果有相应按键被按下,则串口打印调试信息
72 if(key & KEY_UP)
73 {
74 Motor_SET(GPIO_PIN_SET);
75 }
76 if(key & KEY_LEFT)
77 {
78 //
79 }
80 if(key & KEY_DOWN)
81 {
82 Motor_SET(GPIO_PIN_RESET);
83 }
84 if(key & KEY_RIGHT)
85 {
86 //
87 }
88 }
3.5.3 gyu_util.c
请参照实验01中的介绍。
基础实验中的其他例程,大部分都是使用的相同的时钟配置函数,有特殊的时钟使用,将会在对应例程的源码详解中做针对性说明。
3.5.4 gyu_motor.c
马达引脚初始化函数,初始化PC7推挽输出低电平。
31 void Motor_Init(void)
32 {
33 GPIO_InitTypeDef GPIO_InitStructure; // 定义引脚参数结构体
34
35 __HAL_RCC_GPIOC_CLK_ENABLE(); // 使能GPIOC时钟
36
37 GPIO_InitStructure.Pin= GPIO_PIN_7; // 引脚编号为7
38 GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
39 GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; // 低频率
40 GPIO_InitStructure.Pull = GPIO_PULLUP; // 上拉
41 HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); // 初始化PC7
42
43 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_RESET); // 设置PC7默认输出低电平
44 }
马达引脚电平设置函数,设置为高电平,马达起振,设置低电平,马达停止。
54 void Motor_SET(GPIO_PinState pinSate)
55 {
56 HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, pinSate); // 设置PC7输出
57 }
4 实验03-蜂鸣器
蜂鸣器实验通过控制 GPIO 引脚输出高低电平,用于控制蜂鸣器发出蜂鸣声或者停止发声 。
4.1 STM32L476 IO简介
每个GPIO引脚都可以通过软件配置为输出(推挽或漏极开路),输入(带或不带上拉或下拉)或外设备用功能。 大多数GPIO引脚与数字或模拟备用功能共用。 由于它们在AHB2总线上的映射,可以实现快速I / O切换。 如果需要,可以锁定I / O备用功能配置序列,以避免虚假写入I / O寄存器。
经过上一段对GPIO口模式的说明,在这里对它的工作模式进行一个小结,它一共有八种组合,即有八种可配置的工作模式,分别是:
- 输入浮空
- 输入上拉
- 输入下拉
- 模拟
- 带上拉或下拉的开漏输出
- 带上拉或下拉的推挽输出
- 带上拉或下拉的复用功能推挽
- 带上拉或下拉的复用功能开漏
4.2 硬件设计
选择STM32L4引脚PB2作为蜂鸣器的控制引脚,PB2高电平时蜂鸣器发出蜂鸣声。
4.3 实验准备
- 使用miniUSB线及10pin排线,通过Jlink仿真器连接PC端和开发板。
- 使用Keil打开基础实验的实验03-蜂鸣器。
- 下载程序,并完成功能测试。
4.4 实验验证
下载完成后,按下开发板上按键S1,蜂鸣器发声,按下S3,蜂鸣器停止。
4.5 源码详解
本节中的源码说明,仅针对此例程中的重要功能,详细的源码介绍请大家参照代码后的注释。
4.5.1 stm32l4xx_hal_conf.h
此文件位于“实验03-蜂鸣器\Inc”路径中,主要用途是选择使能此例程使用到的库文件,一般情况下,我们默认需要使用的为前5个,包含芯片、flash、电源、时钟以及NVIC。
此例程因为我们需要展示IO的使用,所以我们额外使能 HAL_GPIO_MODULE_ENABLED。
103 // 使能的宏
104 #define HAL_MODULE_ENABLED // 芯片
105 #define HAL_FLASH_MODULE_ENABLED // Flash
106 #define HAL_PWR_MODULE_ENABLED // 电源
107 #define HAL_RCC_MODULE_ENABLED // 时钟
108 #define HAL_CORTEX_MODULE_ENABLED // NVIC
109
110 #define HAL_GPIO_MODULE_ENABLED // GPIO
4.5.2 main.c
main函数,我们的例程由此处开始执行,首先调用HAL_Init()函数初始化我们的模块,接着调用SystemClock_Config()函数初始化此例程用到的时钟,具体有哪些时钟被初始化,在gyu_util.c部分有详细说明。
接下来是初始化按键,有关按键的部分会在04-按键实验中给大家讲解。
最后我们初始化蜂鸣器引脚,配置蜂鸣器引脚为默认输出低电平。
36 int main(void)
37 {
38 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
39 // 重置所有外设、flash界面以及系统时钟
40 HAL_Init();
41
42 // 配置系统时钟(包含振荡器、系统时钟、总线时钟等等)
43 SystemClock_Config();
44
45 // 初始化按键引脚
46 MX_KEY_Init();
47
48 //注册按钮回调函数
49 KEY_RegisterCb(AppKey_cb);
50
51 //初始化蜂鸣器
52 Buzzer_Init();
53
54 //
55 while (1)
56 {
57 KEY_Poll(); // 按键轮训,监测是否有按键被按下
58 }
59 }
在按键的处理回调函数中,我们可以看到,按键S1(UP)按下后,设置蜂鸣器引脚高电平,按键S3(DOWN)按下后,设置蜂鸣器引脚低电平
69 void AppKey_cb(uint8_t key)
70 {
71 // 如果有相应按键被按下,则串口打印调试信息
72 if(key & KEY_UP)
73 {
74 Motor_SET(GPIO_PIN_SET);
75 }
76 if(key & KEY_LEFT)
77 {
78 //
79 }
80 if(key & KEY_DOWN)
81 {
82 Motor_SET(GPIO_PIN_RESET);
83 }
84 if(key & KEY_RIGHT)
85 {
86 //
87 }
88 }
4.5.3 gyu_util.c
请参照实验01中的介绍。
基础实验中的其他例程,大部分都是使用的相同的时钟配置函数,有特殊的时钟使用,将会在对应例程的源码详解中做针对性说明。
4.5.4 gyu_buzzer.c
蜂鸣器引脚初始化函数,初始化PB2推挽输出低电平。
31 void Buzzer_Init(void)
32 {
33 GPIO_InitTypeDef GPIO_InitStructure; // 定义引脚参数结构体
34
35 __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
36
37 GPIO_InitStructure.Pin= GPIO_PIN_2; // 引脚编号为2
38 GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
39 GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; // 低频率
40 GPIO_InitStructure.Pull = GPIO_PULLUP; // 上拉
41 HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); // 初始化PB2
42
43 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET); // 设置PB2默认输出低电平
44 }
蜂鸣器引脚电平设置函数,设置为高电平,蜂鸣器发出蜂鸣声,设置低电平,蜂鸣器停止。
54 void Buzzer_SET(GPIO_PinState pinSate)
55 {
56 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, pinSate); // 设置PB2输出
57 }
5 实验04-按键中断
按键中断实验,是通过外部引脚中断来判断是否有按键被按下,按键部分的代码这个在马达以及蜂鸣器实验中其实已经展示过了,这边给大家做一个详细的讲解。我们分别选择PC0、PC1、PC2、PC3这4个引脚作为我们的按键引脚,对应EXTI line0、EXTI line1、EXTI line2、EXTI line3。
5.1 STM32L476 外部中断简介
首先我们看一下外部中断/事件的GPIO映射图。
由上面的映射图可以知道,多个GPIO引脚(GPIOA、GPIOB、GPIOC、GPIOD等等的GPIO_Pin_0)都会触发同一个中断线(EXTI line0)。也就是说,当EXTI0被触发时,我们无法判断他是PA0触发,还是PB0触发,因此大家在设计自己的硬件的时候,需要选择合适的中断引脚。
源码中我们配置外部中断的步骤如下:
1.使能GPIO时钟
2.GPIO初始化,配置GPIO的边沿触发条件
3.设置EXTI线,配置GPIO与EXTI的关系
4.中断向量初始化
5.2 硬件设计
选择STM32L4引脚PC0、PC1、PC2、PC3作为按键的控制引脚。
5.3 实验准备
- 使用miniUSB线及10pin排线,通过Jlink仿真器连接PC端和开发板。
- 将SW1拨到DBG端,SW2拨到MCU。
- 使用Keil打开基础实验的实验04-按键中断。
- 使用Xshell打开Jlink虚拟出的COM口
- 下载程序,并完成功能测试。
5.4 实验验证
下载完成后,分别按下开发板上的S1、S2、S3、S4按键,可以看到Xshell中Jlink虚拟的COM口分别打印如下:
5.5 源码详解
本节中的源码说明,仅针对此例程中的重要功能,详细的源码介绍请大家参照代码后的注释。
5.5.1 stm32l4xx_hal_conf.h
此文件位于“实验04-按键中断\Inc”路径中,主要用途是选择使能此例程使用到的库文件,一般情况下,我们默认需要使用的为前5个,包含芯片、flash、电源、时钟以及NVIC。
此例程我们只要展示的是外部GPIO中断,所以我们额外使能 HAL_GPIO_MODULE_ENABLED。另外为了辅助展示按键信息,我们额外添加了串口相关的DMA、UART这两个宏定义。
103 // 使能的宏
104 #define HAL_MODULE_ENABLED // 芯片
105 #define HAL_FLASH_MODULE_ENABLED // Flash
106 #define HAL_PWR_MODULE_ENABLED // 电源
107 #define HAL_RCC_MODULE_ENABLED // 时钟
108 #define HAL_CORTEX_MODULE_ENABLED // NVIC
109
110 #define HAL_GPIO_MODULE_ENABLED // GPIO
111 #define HAL_DMA_MODULE_ENABLED // DMA
112 #define HAL_UART_MODULE_ENABLED // UART
5.5.2 main.c
main函数,我们的例程由此处开始执行,首先调用HAL_Init()函数初始化我们的模块,接着调用SystemClock_Config()函数初始化此例程用到的时钟,具体有哪些时钟被初始化,在gyu_util.c部分有详细说明。
接下来我们初始化了串口部分,目的是打印按键按下的调试信息。
接下来是初始化按键,并且注册了按键回调函数(回调函数负责的是不同层之间的数据传输)。
在最后的while()循环中,我们调用按键轮训函数,这样一旦有外部中断触发,我们首先会进行一下按键消抖,确认是否为误判。如果判断是正常触发,则认为是有按键按下,此时按键处理文件gyu_key.c中会将按键信息,通过上面说的回调函数,传到应用层(mian.c)中进行处理。
36 int main(void)
37 {
38 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
39 // 重置所有外设、flash界面以及系统时钟
40 HAL_Init();
41
42 // 配置系统时钟(包含振荡器、系统时钟、总线时钟等等)
43 SystemClock_Config();
44
45 // 初始化USART1
46 MX_USART1_UART_Init();
47
48 // 初始化按键引脚
49 MX_KEY_Init();
50
51 //注册按钮回调函数
52 KEY_RegisterCb(AppKey_cb);
53
54 //
55 while (1)
56 {
57 KEY_Poll(); // 按键轮训,监测是否有按键被按下
58 }
59 }
在应用层的按键回调函数中,我们可以看到,当我们分别按下S1、S2、S3、S4按键后,STM32L4会通过串口向外部打印按键信息。
69 void AppKey_cb(uint8_t key)
70 {
71 // 如果有相应按键被按下,则串口打印调试信息
72 if(key & KEY_UP)
73 {
74 printf("key_up press\r\n");
75 }
76 if(key & KEY_LEFT)
77 {
78 printf("key_left press\r\n");
79 }
80 if(key & KEY_DOWN)
81 {
82 printf("key_down press\r\n");
83 }
84 if(key & KEY_RIGHT)
85 {
86 printf("key_right press\r\n");
87 }
88 }
5.5.3 gyu_util.c
请参照实验01中的介绍。
基础实验中的其他例程,大部分都是使用的相同的时钟配置函数,有特殊的时钟使用,将会在对应例程的源码详解中做针对性说明。
5.5.4 gyu_key.c
首先我们看一下按键的初始化函数,在按键初始化函数中我们配置按键引脚的状态,四个按键引脚都被配置为默认上拉,下降沿中断触发。并且开启EXTI0、EXTI1、EXTI2、EXTI3这四个外部中断线。
75 void MX_KEY_Init(void)
76 {
77 // 定义GPIO结构体
78 GPIO_InitTypeDef GPIO_InitStruct;
79
80 // 使能GPIOC引脚时钟(按键引脚:PC0、PC1、PC2、PC3)
81 __HAL_RCC_GPIOC_CLK_ENABLE();
82
83 // 配置按键引脚
84 GPIO_InitStruct.Pin = KEY_LEFT_Pin|KEY_DOWN_Pin|KEY_RIGHT_Pin|KEY_UP_Pin; // 选择PC0、PC1、PC2、PC3
85 GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; // 下降沿中断触发
86 GPIO_InitStruct.Pull = GPIO_PULLUP; // 上拉
87 HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); // 初始化引脚
88
89 // 配置中断优先级,并且使能中断
90 {
91 // 配置PC0的中断,也就是EXTI line0
92 HAL_NVIC_SetPriority(KEY_LEFT_EXTI_IRQn, 10, 0);
93 HAL_NVIC_EnableIRQ(KEY_LEFT_EXTI_IRQn);
94
95 // 配置PC1的中断,也就是EXTI line1
96 HAL_NVIC_SetPriority(KEY_DOWN_EXTI_IRQn, 10, 0);
97 HAL_NVIC_EnableIRQ(KEY_DOWN_EXTI_IRQn);
98
99 // 配置PC2的中断,也就是EXTI line2
100 HAL_NVIC_SetPriority(KEY_RIGHT_EXTI_IRQn, 10, 0);
101 HAL_NVIC_EnableIRQ(KEY_RIGHT_EXTI_IRQn);
102
103 // 配置PC3的中断,也就是EXTI line3
104 HAL_NVIC_SetPriority(KEY_UP_EXTI_IRQn, 10, 0);
105 HAL_NVIC_EnableIRQ(KEY_UP_EXTI_IRQn);
106 }
107 }
如下,是我们在初始化函数中打开的四个中断线。
43 // EXTI line0 中断函数
44 void EXTI0_IRQHandler(void)
45 {
46 HAL_GPIO_EXTI_IRQHandler(KEY_LEFT_Pin);
47 }
48
49 // EXTI line1 中断函数
50 void EXTI1_IRQHandler(void)
51 {
52 HAL_GPIO_EXTI_IRQHandler(KEY_DOWN_Pin);
53 }
54
55 // EXTI line2 中断函数
56 void EXTI2_IRQHandler(void)
57 {
58 HAL_GPIO_EXTI_IRQHandler(KEY_RIGHT_Pin);
59 }
60
61 // EXTI line3 中断函数
62 void EXTI3_IRQHandler(void)
63 {
64 HAL_GPIO_EXTI_IRQHandler(KEY_UP_Pin);
65 }
我们配置好上面所说的引脚外部中断后,一旦有按键被按下,则会触发中断,最终会跑到如下的HAL_GPIO_EXTI_Callback()函数中。我们在这个函数中,判断一下是哪一个中断线触发的中断,并且记录一下按键信息,以及触发的时间(记录触发的时间,是为了进行按键消抖,防止误操作)。
119 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
120 {
121 // 如果是UP键被触发,记录按键任务为KEY_UP,并记录当前时钟
122 if(GPIO_Pin == KEY_UP_Pin)
123 {
124 key_check_press.key_event = KEY_UP;
125 key_check_press.start_tick = HAL_GetTick();
126 }
127 // 如果是LEFT键被触发,记录按键任务为KEY_LEFT,并记录当前时钟
128 if(GPIO_Pin == KEY_LEFT_Pin)
129 {
130 key_check_press.key_event = KEY_LEFT;
131 key_check_press.start_tick = HAL_GetTick();
132 }
133 // 如果是DOWN键被触发,记录按键任务为KEY_DOWN,并记录当前时钟
134 if(GPIO_Pin == KEY_DOWN_Pin)
135 {
136 key_check_press.key_event = KEY_DOWN;
137 key_check_press.start_tick = HAL_GetTick();
138 }
139 // 如果是RIGHT键被触发,记录按键任务为KEY_RIGHT,并记录当前时钟
140 if(GPIO_Pin == KEY_RIGHT_Pin)
141 {
142 key_check_press.key_event = KEY_RIGHT;
143 key_check_press.start_tick = HAL_GetTick();
144 }
145 }
按键轮训函数,其实就是用来处理消抖的函数,我们根据从外部中断回调函数中的获取的时间(也就是中断触发的时间,上一个函数中记录的),对比现在实时的时间,判断是否超过20ms,如果超过20ms,则认为按键被按下。我们记录下按键信息,并且执行向应用层回调的函数。
171 void KEY_Poll(void)
172 {
173 uint8_t key_event = 0;
174
175 // 如果有按键任务
176 if(key_check_press.key_event)
177 {
178 // 获取当前时钟 减去 记录的按键触发时钟,如果大于消抖延时,则继续向下判断
179 if(HAL_GetTick() - key_check_press.start_tick >= KEY_DELAY_TICK )
180 {
181 // 如果按键任务记录为KEY_UP
182 if(key_check_press.key_event & KEY_UP)
183 {
184 // 获取当前KEY_UP引脚电平,如果是低电平,则认为UP按键被按下
185 if(HAL_GPIO_ReadPin(KEY_UP_GPIO_Port,KEY_UP_Pin) == GPIO_PIN_RESET)
186 {
187 key_event |= KEY_UP; // 记录app按键任务
188 }
189 key_check_press.key_event ^= KEY_UP; // 删除按键中断任务
190 }
191
192 // 如果按键任务记录为KEY_LEFT
193 if(key_check_press.key_event & KEY_LEFT)
194 {
195 // 获取当前KEY_LEFT引脚电平,如果是低电平,则认为LEFT按键被按下
196 if(HAL_GPIO_ReadPin(KEY_LEFT_GPIO_Port,KEY_LEFT_Pin) == GPIO_PIN_RESET)
197 {
198 key_event |= KEY_LEFT; // 记录app按键任务
199 }
200 key_check_press.key_event ^= KEY_LEFT; // 删除按键中断任务
201 }
202
203 // 如果按键任务记录为KEY_DOWN
204 if(key_check_press.key_event & KEY_DOWN)
205 {
206 // 获取当前KEY_DOWN引脚电平,如果是低电平,则认为DOWN按键被按下
207 if(HAL_GPIO_ReadPin(KEY_DOWN_GPIO_Port,KEY_DOWN_Pin) == GPIO_PIN_RESET)
208 {
209 key_event |= KEY_DOWN; // 记录app按键任务
210 }
211 key_check_press.key_event ^= KEY_DOWN; // 删除按键中断任务
212 }
213
214 // 如果按键任务记录为KEY_RIGHT
215 if(key_check_press.key_event & KEY_RIGHT)
216 {
217 // 获取当前KEY_RIGHT引脚电平,如果是低电平,则认为RIGHT按键被按下
218 if(HAL_GPIO_ReadPin(KEY_RIGHT_GPIO_Port,KEY_RIGHT_Pin) == GPIO_PIN_RESET)
219 {
220 key_event |= KEY_RIGHT; // 记录app按键任务
221 }
222 key_check_press.key_event ^= KEY_RIGHT; // 删除按键中断任务
223 }
224 }
225 }
226 //如果有记录给app的按键任务,代表真的有按钮按下,则执行回调函数
227 if(key_event && pFkey_cb)
228 {
229 pFkey_cb(key_event);
230 }
231 }
留给应用层调用注册按键回调的函数,用于将轮询后确认的按键信息,传递给应用层使用。
155 void KEY_RegisterCb(key_cb cb)
156 {
157 if(cb != 0)
158 {
159 pFkey_cb = cb;
160 }
161 }
6 实验05-光敏二极管
光敏二极管在不同的光照强度下,它的out引脚输出的电压不同。所以此实验,我们利用STM32L4的ADC功能,去采集光敏二极管的引脚输出电压,以此能够获取到当前环境的光照情况。
6.1 STM32L476 ADC简介
STM32L476一共有3个ADC(ADC1、ADC2、ADC3),3个ADC都可以进行独立的工作,每个ADC都支持12位精度的采样。
每个ADC最多有19个多路复用的通道,每个通道的AD转换都是可以执行单一、连续扫描或者不连续扫描的。
ADC的参考电压取决于VREF+引脚的输入电压,在开发板上,我们接好miniUSB供电,此引脚电压范围在3.2V~3.3V之间,代码中我们默认取3.3V为参考电压。
ADC引脚的输入电压范围:VREF- ≤ VIN ≤ VREF+ ,在开发板上,就是大于0V,小于3.3V。
在这个例程中,我们将以ADC1的通道16(也就是PB1引脚)来采集光敏二极管的输出电压。
6.2 硬件设计
选择STM32L4引脚PB1作为光敏二极管的ADC采集引脚。
6.3 实验准备
- 使用miniUSB线及10pin排线,通过Jlink仿真器连接PC端和开发板。
- 使用miniUSB线,连接PC与开发板USB接口。
- 将SW1拨到DBG端,SW2拨到MCU。
- 使用Keil打开基础实验的实验05-光敏二极管。
- 使用Xshell打开Jlink虚拟出的COM口
- 下载程序,并完成功能测试。
6.4 实验验证
下载完成后,打开COM口,可以看到每隔500ms打印一次采集到的ADC数据。 红色字体部分,是正常的室内光照强度时采集的电压;绿色字体部分,是打开手机手电筒照射光敏二极管时采集的电压。可以看到明显的电压差值。
6.5 源码详解
本节中的源码说明,仅针对此例程中的重要功能,详细的源码介绍请大家参照代码后的注释。
6.5.1 stm32l4xx_hal_conf.h
此文件位于“实验05-光敏二极管\Inc”路径中,主要用途是选择使能此例程使用到的库文件。
此例程我们主要给大家展示STM32L4的ADC功能,所以我们宏定义中打开ADC相关的。
103 // 使能的宏
104 #define HAL_MODULE_ENABLED // 芯片
105 #define HAL_FLASH_MODULE_ENABLED // Flash
106 #define HAL_PWR_MODULE_ENABLED // 电源
107 #define HAL_RCC_MODULE_ENABLED // 时钟
108 #define HAL_CORTEX_MODULE_ENABLED // NVIC
109
110 #define HAL_GPIO_MODULE_ENABLED // GPIO
111 #define HAL_UART_MODULE_ENABLED // UART
112 #define HAL_DMA_MODULE_ENABLED // DMA
113 #define HAL_ADC_MODULE_ENABLED // ADC
6.5.2 main.c
main函数,我们的例程由此处开始执行,首先调用HAL_Init()函数初始化我们的模块,接着调用SystemClock_Config()函数初始化此例程用到的时钟,具体有哪些时钟被初始化,在gyu_util.c部分有详细说明。
接下来我们初始化了串口部分,目的是打印ADC采集电压值。
接下来是ADC引脚初始化。
在最后的while()循环中,我们调用ADC值采集函数,每个500ms采集一次,并且将采集值转换成电压值,格式化打印到串口显示。
33 int main(void)
34 {
35 uint16_t ad_value = 0;
36
37 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
38 // 重置所有外设、flash界面以及系统时钟
39 HAL_Init();
40
41 // 配置系统时钟(包含振荡器、系统时钟、总线时钟等等)
42 SystemClock_Config();
43
44 // 初始化串口USART1
45 MX_USART1_UART_Init();
46
47 // 初始化ADC1
48 MX_ADC1_Init();
49
50 //
51 while (1)
52 {
53 HAL_Delay(500);
54 ad_value = HAL_ADC_Read(); // 获取ADC采集值
55 printf("Adc_value = %.2f\r\n",ad_value*3.3/4095); // 转换ADC采集为真实电压,并且打印到串口显示
56 }
57 }
6.5.3 gyu_util.c
请参照实验01中的介绍。
基础实验中的其他例程,大部分都是使用的相同的时钟配置函数,有特殊的时钟使用,将会在对应例程的源码详解中做针对性说明。
6.5.4 gyu_adc.c
ADC初始化函数,配置12位采样分辨率,配置2.5倍ADC时钟周期的采样频率。
选择ADC通道16(CH16)作为此次的ADC引脚(也就是PB1)。
40 void MX_ADC1_Init(void)
41 {
42 // 定义常规ADC通道结构体
43 ADC_ChannelConfTypeDef sConfig;
44
45 // 初始化ADC
46 hadc1.Instance = ADC1; // ADC寄存器地址,定义为ADC1
47 hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1; // 没有预分频器的ADC异步时钟
48 hadc1.Init.Resolution = ADC_RESOLUTION_12B; // 12位采样分辨率
49 // 初始化ADC,如果失败,则进入错误处理程序
50 if (HAL_ADC_Init(&hadc1) != HAL_OK)
51 {
52 _Error_Handler(__FILE__, __LINE__);
53 }
54
55 // 配置ADC通道
56 sConfig.Channel = ADC_CHANNEL_16; // 配置为ADC通道16
57 sConfig.Rank = ADC_REGULAR_RANK_1; // 指定ADC规格组中的排名
58 sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5; // 采样时间配置为2.5个ADC时钟周期
59 sConfig.SingleDiff = ADC_SINGLE_ENDED; // ADC通道结束设置为单端
60 sConfig.OffsetNumber = ADC_OFFSET_NONE; // 禁用ADC偏移
61 sConfig.Offset = 0; // 定义从原始量中减去的偏移量
62 if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) // 配置ADC通道
63 {
64 _Error_Handler(__FILE__, __LINE__); // 如果出错,进入错误处理
65 }
66
67 }
重新定义ADC的硬件引脚,可以看到,配置PB1为ADC引脚。
77 void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
78 {
79 GPIO_InitTypeDef GPIO_InitStruct;
80 if(adcHandle->Instance==ADC1)
81 {
82 // 使能GPIOB引脚时钟(选择的ADC引脚为PB1)
83 __HAL_RCC_GPIOB_CLK_ENABLE();
84
85 // 使能ADC时钟
86 __HAL_RCC_ADC_CLK_ENABLE();
87
88 // 初始化ADC引脚配置
89 GPIO_InitStruct.Pin = GPIO_PIN_1; // 选择引脚编号1
90 GPIO_InitStruct.Mode = GPIO_MODE_ANALOG_ADC_CONTROL; // 配置为ADC引脚
91 GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上下拉
92 HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); // 初始化PB1引脚
93 }
94 }
用于获取ADC采集值的函数,经过一系列的API函数调用,我们获取到最终的adcValue。
104 uint16_t HAL_ADC_Read(void)
105 {
106 uint16_t ad_value = 0;
107
108 // 启动ADC转换
109 HAL_ADC_Start(&hadc1);
110
111 // 等待ADC转换完成
112 HAL_ADC_PollForConversion(&hadc1, 50);
113
114 // 检查是否已经完成转换
115 if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
116 {
117 // 获取采集到的ADC值
118 ad_value = HAL_ADC_GetValue(&hadc1);
119 }
120
121 return ad_value; // 返回ADC采集值
122 }
7 实验06-DAC模拟输出
此实验我们将会配置DAC1的通道1,作为模拟电压的输出引脚,并且配置一个ADC引脚,采集DAC输出的电压,并将电压值格式化打印到串口显示。
7.1 STM32L476 DAC简介
DAC模块是一个12位电压输出数模转换器。 DAC可配置为8位或12位模式,并可与DMA控制器配合使用。在12位模式下,数据可以左对齐或右对齐。 DAC有两个输出通道,每个通道都有自己的转换器。在双DAC通道模式下,当两个通道组合在一起进行同步更新操作时,可以单独或同时完成转换。
DAC的主要特性如下:
•两个DAC转换器:每个转换器一个输出通道
•12位模式下的左或右数据对齐
•同步更新功能
•噪声波和三角波生成
•双DAC通道,用于独立或同步转换
•每个通道的DMA功能,包括DMA欠载错误检测
•转换的外部触发器
•DAC输出通道缓冲/非缓冲模式
•缓冲偏移校准
•每个DAC输出可以与DAC_OUTx输出引脚断开
•DAC输出连接到片上外设
•采样和保持模式,用于在停止模式下进行低功耗操作
•输入电压参考,VREF +
7.2 硬件设计
选择STM32L4引脚PA4作为模拟输出(DAC)引脚,配置PA7作为ADC引脚。
此实验测试的时候,由于选择的PA4及PA7引脚被显示屏占用,所以我们需要拔下显示屏,然后使用杜邦线短接PA4及PA7引脚。
7.3 实验准备
- 使用miniUSB线及10pin排线,通过Jlink仿真器连接PC端和开发板。
- 使用miniUSB线,连接PC与开发板USB接口。
- 将SW1拨到DBG端,SW2拨到MCU。
- 使用Keil打开基础实验的实验06-DAC模拟输出。
- 使用Xshell打开Jlink虚拟出的COM口
- 下载程序,并完成功能测试。
7.4 实验验证
下载完成后,打开COM口,可以看到每隔500ms打印一次采集到的ADC数据。
我们默认配置DAC引脚输出2000(也就是1.6V)电压,可以看到ADC采集到的数据为1.56V,这个是ADC采集的偏移量导致的。
7.5 源码详解
本节中的源码说明,仅针对此例程中的重要功能,详细的源码介绍请大家参照代码后的注释。
7.5.1 stm32l4xx_hal_conf.h
此文件位于“实验06-DAC模拟输出\Inc”路径中,主要用途是选择使能此例程使用到的库文件。
此例程我们主要给大家展示STM32L4的DAC功能,所以我们宏定义中打开DAC相关的。
103 // 使能的宏
104 #define HAL_MODULE_ENABLED // 芯片
105 #define HAL_FLASH_MODULE_ENABLED // Flash
106 #define HAL_PWR_MODULE_ENABLED // 电源
107 #define HAL_RCC_MODULE_ENABLED // 时钟
108 #define HAL_CORTEX_MODULE_ENABLED // NVIC
109
110 #define HAL_GPIO_MODULE_ENABLED // GPIO
111 #define HAL_UART_MODULE_ENABLED // UART
112 #define HAL_DMA_MODULE_ENABLED // DMA
113 #define HAL_ADC_MODULE_ENABLED // ADC
114 #define HAL_DAC_MODULE_ENABLED // DAC
7.5.2 main.c
main函数,我们的例程由此处开始执行,首先调用HAL_Init()函数初始化我们的模块,接着调用SystemClock_Config()函数初始化此例程用到的时钟,具体有哪些时钟被初始化,在gyu_util.c部分有详细说明。
接下来我们初始化了串口部分,目的是打印采集到DAC输出电压。
接下来分别初始化了ADC以及DAC引脚配置。
在最后的while()循环中,我们设置DAC引脚的输出电压(默认设置输出1.6V),然后我们调用ADC值采集函数采集这个引脚电压,并且每隔500ms将采集值转换成电压值,格式化打印到串口显示。
33 int main(void)
34 {
35 uint16_t ad_value = 0;
36
37 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
38 // 重置所有外设、flash界面以及系统时钟
39 HAL_Init();
40
41 // 配置系统时钟(包含振荡器、系统时钟、总线时钟等等)
42 SystemClock_Config();
43
44 // 初始化串口USART1
45 MX_USART1_UART_Init();
46
47 // 初始化ADC1
48 MX_ADC1_Init();
49
50 // 初始化DAC1
51 MX_DAC1_Init();
52
53 //
54 while (1)
55 {
56 HAL_Delay(500);
57
58 HAL_DAC_Set(2000); // 设置2000DAC输出值(1.6V)
59
60 ad_value = HAL_ADC_Read(); // 获取ADC采集值
61 printf("adc = %d\r\n",ad_value); // 打印ADC采集值
62 printf("Adc_value = %.2fV\r\n",ad_value*3.3/4095); // 转换ADC采集为真实电压,并且打印到串口显示
63 }
64 }
7.5.3 gyu_util.c
请参照实验01中的介绍。
基础实验中的其他例程,大部分都是使用的相同的时钟配置函数,有特殊的时钟使用,将会在对应例程的源码详解中做针对性说明。
7.5.4 gyu_dac.c
DAC初始化函数,配置DAC1通道1(也就是PA4引脚)为DAC输出引脚。
37 void MX_DAC1_Init(void)
38 {
39 hdac1.Instance = DAC1; // 配置为DAC1
40 if (HAL_DAC_Init(&hdac1) != HAL_OK) // 初始化DAC1
41 {
42 _Error_Handler(__FILE__, __LINE__);
43 }
44
45 // DAC通道配置结构体定义
46 DAC_ChannelConfTypeDef sConfig;
47 sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE; //DAC模式
48 sConfig.DAC_Trigger=DAC_TRIGGER_NONE; // 不使用触发功能
49 sConfig.DAC_OutputBuffer=DAC_OUTPUTBUFFER_DISABLE; // DAC1输出缓冲关闭
50 sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_DISABLE; //不连接到片内外设
51
52 // 初始化通道CH1配置
53 if (HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_1) != HAL_OK)
54 {
55 _Error_Handler(__FILE__, __LINE__);
56 }
57
58 HAL_DAC_Start(&hdac1,DAC_CHANNEL_1); //开启DAC通道1
59 }
重新定义DAC的硬件引脚,可以看到,配置PA4为DAC引脚。
70 void HAL_DAC_MspInit(DAC_HandleTypeDef* hdac)
71 {
72 GPIO_InitTypeDef GPIO_InitStruct;
73 if(hdac->Instance==DAC1) // 判断是否是DAC1
74 {
75 __HAL_RCC_DAC1_CLK_ENABLE(); // 使能DAC时钟
76
77 GPIO_InitStruct.Pin = GPIO_PIN_4; // 选择PA4
78 GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;// 配置模拟模式
79 GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上下拉
80 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 初始化PA4引脚配置
81 }
82 }
设置DAC引脚输出电压,value为单元值,不是真正的电压值大小。 模拟电压值(范围:0~4095,对应0~3.3V)。
92 void HAL_DAC_Set(uint16_t value)
93 {
94 HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_1,DAC_ALIGN_12B_R,value); // 配置CH1 12位右对齐模拟输出
95 }
8 实验07-温湿度采集
此实验给大家展示的是利用STM32L476的I2C外设功能,去获取sht20温湿度传感器采集的温湿度数据。并且将获取到的数据转换成真实的温湿度数据,格式化打印到串口显示。
8.1 STM32L476 I2C简介
I2C(内部集成电路)总线接口处理STM32L4和串行I2C总线之间的通信。 它提供多主机功能,并控制所有I2C总线特定的排序,协议,仲裁和定时。 它支持标准模式(Sm),快速模式(Fm)和快速模式加(Fm +)。兼容SMBus(系统管理总线)和PMBus(电源管理总线)。
I2C主要功能:
•I2C总线规范rev03兼容性:
- 从模式和主模式
- 多主机功能
- 标准模式(最高100 kHz)
- 快速模式(最高400 kHz)
- 快速模式加(最高1 MHz)
- 7位和10位寻址模式
- 多个7位从机地址(2个地址,1个带可配置掩码)
- 所有7位地址确认模式
- 一般电话
- 可编程设置和保持时间
- 易于使用的事件管理
- 可选的时钟拉伸
- 软件重置
•具有DMA功能的1字节缓冲区
•可编程模拟和数字噪声滤波器
8.2 硬件设计
选择STM32L4引脚PB13作为I2C SCL引脚,PB14作为I2C SDA引脚。
8.3 实验准备
- 使用miniUSB线及10pin排线,通过Jlink仿真器连接PC端和开发板。
- 使用miniUSB线,连接PC与开发板USB接口。
- 将SW1拨到DBG端,SW2拨到MCU。
- 使用Keil打开基础实验的实验07-温湿度采集。
- 使用Xshell打开Jlink虚拟出的COM口
- 下载程序,并完成功能测试。
8.4 实验验证
下载完成后,打开COM口,可以看到每隔500ms打印一次采集到的温湿度数据。
在采集的过程中,我们将手指按在SHT20上,可以看到温度和湿度都在上升,例如温度,由开始的20.4°C上升到最终的26.7°C。
8.5 源码详解
本节中的源码说明,仅针对此例程中的重要功能,详细的源码介绍请大家参照代码后的注释。
8.5.1 stm32l4xx_hal_conf.h
此文件位于“实验07-温湿度采集\Inc”路径中,主要用途是选择使能此例程使用到的库文件。
此例程我们主要给大家展示STM32L4的I2C功能,所以我们宏定义中打开I2C相关的。
103 // 使能的宏
104 #define HAL_MODULE_ENABLED // 芯片
105 #define HAL_FLASH_MODULE_ENABLED // Flash
106 #define HAL_PWR_MODULE_ENABLED // 电源
107 #define HAL_RCC_MODULE_ENABLED // 时钟
108 #define HAL_CORTEX_MODULE_ENABLED // NVIC
109
110 #define HAL_GPIO_MODULE_ENABLED // GPIO
111 #define HAL_UART_MODULE_ENABLED // UART
112 #define HAL_DMA_MODULE_ENABLED // DMA
113 #define HAL_ADC_MODULE_ENABLED // ADC
114 #define HAL_DAC_MODULE_ENABLED // DAC
115 #define HAL_I2C_MODULE_ENABLED // I2C
8.5.2 main.c
main函数,我们的例程由此处开始执行,首先调用HAL_Init()函数初始化我们的模块,接着调用SystemClock_Config()函数初始化此例程用到的时钟,具体有哪些时钟被初始化,在gyu_util.c部分有详细说明。
接下来我们初始化了串口部分,目的是打印采集到的温湿度数据。
接下来初始化I2C引脚。
在while()循环中,我们每隔500ms采集一次温湿度的值,并且将采集的温湿度值转化成真实值,格式化打印到串口显示。
33 int main(void)
34 {
35 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
36 // 重置所有外设、flash界面以及系统时钟
37 HAL_Init();
38
39 // 配置系统时钟(包含振荡器、系统时钟、总线时钟等等)
40 SystemClock_Config();
41
42 // 初始化串口USART1
43 MX_USART1_UART_Init();
44
45 // 初始化I2C2
46 MX_I2C2_Init();
47
48 //
49 while (1)
50 {
51 HAL_Delay(500);
52 printf("Temp = %.1f\r\n",SHT20_Convert(SHT20_ReadTemp(),1));
53 printf("RH = %.1f%%\r\n",SHT20_Convert(SHT20_ReadRH(),0));
54 }
55 }
8.5.3 gyu_util.c
请参照实验01中的介绍。
基础实验中的其他例程,大部分都是使用的相同的时钟配置函数,有特殊的时钟使用,将会在对应例程的源码详解中做针对性说明。
8.5.4 gyu_i2c.c
在讲解i2c代码之前,我们先给大家讲解一下参数Timing,这个值是通过计算得来的,在STM32芯片手册的P1238页有计算公式说明,我们这边偷懒,利用STM32CUBE里面的配置功能,对应SHT20的I2C参数要求。
我们配置I2C时钟为100KHz,Rise Time 300ns,Fall Time 100ns。最终得出Timing值为0x10D05E82。
初始化I2C引脚,选择的I2C2。
36 void MX_I2C2_Init(void)
37 {
38 hi2c2.Instance = I2C2; // I2C寄存器基础地址,定义为I2C2的
39 hi2c2.Init.Timing = 0x10D05E82; // 指定I2C_TIMINGR寄存器值,此值必须在I2C初始化之前配置
40
41 if (HAL_I2C_Init(&hi2c2) != HAL_OK) // 初始化I2C2
42 {
43 _Error_Handler(__FILE__, __LINE__); // 如果初始化失败,则进入错误处理
44 }
45 }
定义I2C2功能引脚,选择PB13为SCL引脚,PB14为SDA引脚。并且使能GPIOB以及I2C2的时钟。
55 void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)
56 {
57 // 使能GPIOB引脚时钟,因为选择的I2C引脚均在PB上
58 __HAL_RCC_GPIOB_CLK_ENABLE();
59
60 // 定义GPIO结构
61 GPIO_InitTypeDef GPIO_InitStruct;
62
63 // 判断I2C是否选择的是I2C2
64 if(i2cHandle->Instance==I2C2)
65 {
66 // I2C2引脚配置
67 GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14; // 选择PB13为SCL引脚,PB14为SDA引脚
68 GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; // 外设功能为开漏模式
69 GPIO_InitStruct.Pull = GPIO_PULLUP; // 上拉
70 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; // 高速模式
71 GPIO_InitStruct.Alternate = GPIO_AF4_I2C2; // 外设引脚选择I2C2
72 HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); // 初始化IO配置
73
74 // 使能I2C2时钟
75 __HAL_RCC_I2C2_CLK_ENABLE();
76 }
77 }
I2C发送数据的函数。
89 uint8_t HAL_I2C_Send(uint8_t addr , uint8_t *pData, uint16_t len)
90 {
91 // 判断是否存在数据,不存在返回HAL_ERROR
92 if(len == 0 || pData == 0)
93 {
94 return HAL_ERROR;
95 }
96
97 // 发送数据,并返回发送状态
98 return HAL_I2C_Master_Transmit(&hi2c2,addr,pData,len,100);
99 }
I2C接收数据的函数。
111 uint8_t HAL_I2C_Read(uint8_t addr, uint8_t *pData, uint16_t len)
112 {
113 // 判断是否存在数据,不存在返回HAL_ERROR
114 if(len == 0 || pData == 0)
115 {
116 return HAL_ERROR;
117 }
118
119 // 接收数据,并返回接收状态
120 return HAL_I2C_Master_Receive(&hi2c2,addr,pData,len,100);
121 }
8.5.5 gyu_sht20.c
读取SHT20温度的函数,最终返回是温度是采集值,不是真实的温度值。
52 uint16_t SHT20_ReadTemp(void)
53 {
54 uint16_t temp = 0;
55
56 // 发送“读取温度指令”
57 uint8_t cmd = SHT20_MEASURE_TEMP_CMD;
58 HAL_I2C_Send(SHT20_WRITE_ADDR,&cmd,1);
59
60 // 获取温度采集值,3位数据分别为:Data(MSB)、Data(LSB)、CheckSum
61 uint8_t pDATA[3] = {0,0,0};
62 HAL_I2C_Read(SHT20_READ_ADDR,pDATA,3);
63
64 // 计算出真实的采集值,保留14bit(MSB 8bit、LSB 高6bit)
65 temp = pDATA[0];
66 temp <<= 8;
67 temp += (pDATA[1] & 0xfc);
68
69 // 返回温度采集值
70 return temp;
71 }
读取SHT20湿度的函数,最终返回是湿度是采集值,不是真实的湿度值。
81 uint16_t SHT20_ReadRH(void)
82 {
83 uint16_t rh = 0;
84
85 // 发送“读取湿度指令”
86 uint8_t cmd = SHT20_MEASURE_RH_CMD;
87 HAL_I2C_Send(SHT20_WRITE_ADDR,&cmd,1);
88
89 // 获取湿度采集值,3位数据分别为:Data(MSB)、Data(LSB)、CheckSum
90 uint8_t pDATA[3] = {0,0,0};
91 HAL_I2C_Read(SHT20_READ_ADDR,pDATA,3);
92
93 // 计算出真实的采集值,保留12bit(MSB 8bit、LSB 高4bit)
94 rh = pDATA[0];
95 rh <<= 8;
96 rh += (pDATA[1] & 0xf0);
97
98 // 返回湿度采集值
99 return rh;
100 }
SHT20软件复位函数,工程中没有使用此函数。
110 void SHT20_SoftReset(void)
111 {
112 // 发送SHT20软件复位指令
113 uint8_t cmd = SHT20_MEASURE_RH_CMD;
114 HAL_I2C_Send(SHT20_WRITE_ADDR,&cmd,1);
115 }
温湿度转换函数,用于将采集到的温湿度采集值,转化成真实的温湿度值。
127 float SHT20_Convert(uint16_t value,uint8_t isTemp)
128 {
129 float tmp = 0.0;
130 // 判断本次需要转换的值是温度还是湿度
131 if(isTemp)
132 {
133 tmp = -46.85 + (175.72* value)/(1 << 16); // 温度值转换,公式:T = -46.85 + 175.72*(S/2^16)
134 }
135 else
136 {
137 tmp = -6 + (125.0 *value)/(1<<16); // 湿度值转换,公式:RH = -6.00 + 125.00*(S/2^16)
138 }
139 return tmp;
140 }
9 实验08-RGB
10 实验09-红外接收
红外接收实验,是利用开发板上的红外接收传感器,去获取遥控器按下的信号,红外传感器获取到这个信号后,会转成一段PWM波形从它的DATA引脚输出。此时我们利用STM32的定时器捕获功能,就可以获取到这个PWM波形所携带的信息,以此判断遥控器按下的是哪个按键。
10.1 STM32L476 定时器捕获简介
以下部分为TIM2 / TIM3 / TIM4 / TIM5这四个定时器的介绍。
1.通用定时器简介:
通用定时器由一个由可编程预分频器驱动的16位或32位自动重载计数器组成。它们可用于各种目的,包括测量输入信号的脉冲长度(输入捕获)或生成输出波形(输出比较和PWM)。
使用定时器预分频器和RCC时钟控制器预分频器,可以将脉冲长度和波形周期从几微秒调制到几毫秒。定时器完全独立,不共享任何资源。
2.通用定时器功能
•16位(TIM3,TIM4)或32位(TIM2和TIM5)上,下,上/下自动重载计数器。
•16位可编程预分频器,用于分频(也“在运行中”)计数器时钟
频率由1到65535之间的任何因子组成。
•最多4个独立频道:
- 输入捕获
- 输出比较
- PWM生成(边缘和中心对齐模式)
- 单脉冲模式输出
•同步电路,用外部信号控制定时器并互连
几个计时器。
•以下事件的中断/ DMA生成:
- 更新:计数器溢出/下溢,计数器初始化(通过软件或
内部/外部触发器)
- 触发事件(计数器启动,停止,初始化或通过内部/外部触发计数)
- 输入捕获
- 输出比较
•支持增量(正交)编码器和霍尔传感器电路进行定位
目的
•外部时钟或逐周期电流管理的触发输入
3.通用定时器捕获模式
在输入捕捉模式下,捕捉/比较寄存器(TIMx_CCRx)用于在相应ICx信号检测到转换后锁存计数器的值。 发生捕获时,会设置相应的CCXIF标志(TIMx_SR寄存器),如果使能了中断或DMA请求,则可以发送它们。 如果在CCxIF标志已经为高电平时发生捕获,则设置过捕获标志CCxOF(TIMx_SR寄存器)。 CCxIF可以通过软件将其写入0或读取存储在TIMx_CCRx寄存器中的捕获数据来清除。 将其写入0时,CCxOF将被清除。
10.1.1 遥控器协议说明
遥控器使用的协议,被称为NEC码,NEC的编码方式如下所示,这个部分大家需要理解清楚,方便后续代码的阅读。
NEC码高低电平位定义如下:
一个逻辑 0 的传输需要 1.125ms(560us 脉冲+560us 低电平),一个逻辑 1 传输需要 2.25ms(560us脉冲+1680us 低电平)。
NEC数据格式为:
引导码、用户地址码、用户地址反码、数据码、数据反码。
引导码由一个 9ms 的低电平和一个 4.5ms 的高电平组成,用户地址码、用户地址反码、数据码、数据反码均是8 位数据格式。
当按键被持续按下时,每隔108ms重新发送一次此数据,所以我们可以利用计时超过108ms的方式,来计算按键持续按下的次数(代码中是判断的110ms)。
10.2 硬件设计
选择STM32L4引脚PC6用来捕获红外传感器HS0038的DATA引脚输出的PWM波。
10.3 实验准备
- 使用miniUSB线及10pin排线,通过Jlink仿真器连接PC端和开发板。
- 使用miniUSB线,连接PC与开发板USB接口。
- 将SW1拨到DBG端,SW2拨到MCU。
- 使用Keil打开基础实验的实验09-红外线接收。
- 使用Xshell打开Jlink虚拟出的COM口
- 下载程序,并完成功能测试。
10.4 实验验证
下载完成后,我们按下遥控器上的任意按键,可以看到LCD上将显示如下,irBtnVal代表的是键值,irBtnCnt代表是按键被按下的次数,irBtnInfo代表按键的图标或者定义。
10.5 源码详解
本节中的源码说明,仅针对此例程中的重要功能,详细的源码介绍请大家参照代码后的注释。
10.5.1 stm32l4xx_hal_conf.h
此文件位于“实验09-红外线接收\Inc”路径中,主要用途是选择使能此例程使用到的库文件。
此例程我们主要给大家展示STM32L4的I2C功能,所以我们宏定义中打开I2C相关的。
103 // 使能的宏
104 #define HAL_MODULE_ENABLED // 芯片
105 #define HAL_FLASH_MODULE_ENABLED // Flash
106 #define HAL_PWR_MODULE_ENABLED // 电源
107 #define HAL_RCC_MODULE_ENABLED // 时钟
108 #define HAL_CORTEX_MODULE_ENABLED // NVIC
109
110 #define HAL_GPIO_MODULE_ENABLED // GPIO
111 #define HAL_UART_MODULE_ENABLED // UART
112 #define HAL_DMA_MODULE_ENABLED // DMA
113 #define HAL_ADC_MODULE_ENABLED // ADC
114 #define HAL_DAC_MODULE_ENABLED // DAC
115 #define HAL_I2C_MODULE_ENABLED // I2C
10.5.2 main.c
main函数,我们的例程由此处开始执行,首先调用HAL_Init()函数初始化我们的模块,接着调用SystemClock_Config()函数初始化此例程用到的时钟,具体有哪些时钟被初始化,在gyu_util.c部分有详细说明。
接下来我们初始化LCD的SPI控制引脚,LCD背光引脚,并且初始化LCD的图形控制界面。
然后我们在LCD上打印固定的遥控器按键显示格式,也就是"irBtnVal"、"irBtnCnt"、"irBtnInfo"这几个字符串。
接下来我们初始化我们此实验的重点功能,也就是TIM3定时器。
在while()循环中,我们轮询遥控器的按键信息,一旦有遥控器按下,则在LCD的对应位置,打印按键的信息。
34 int main(void)
35 {
36 irInfo_t irkey = {0,0};
37
38 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
39 // 重置所有外设、flash界面以及系统时钟
40 HAL_Init();
41
42 // 配置系统时钟(包含振荡器、系统时钟、总线时钟等等)
43 SystemClock_Config();
44
45 // LCD SPI初始化
46 LCD_GPIO_Init(); // LCD IO控制引脚(例如背光)
47 MX_SPI1_Init(); // LCD SPI控制引脚
48
49 // 图形界面初始化
50 GUI_Init(); // GUI界面初始化
51 GUI_Clear(); // 清屏
52
53 GUI_SetColor(GUI_Crimson); // 红色字体
54 GUI_DispStringAt("irBtnVal:",24,24); // 打印字符串"irBtnVal:"到位置X->24,Y->24
55 GUI_DispStringAt("irBtnCnt:",24,72); // 打印字符串"irBtnCnt:"到位置X->24,Y->96
56 GUI_DispStringAt("irBtnInfo:",24,120); // 打印字符串"irBtnCnt:"到位置X->24,Y->120
57
58 // 初始化TIM3
59 MX_TIM3_Init();
60
61 //
62 while(1)
63 {
64 irkey = IRBNT_POLL(); // 轮训获取IR按键信息
65
66 if(irkey.irBtnVal) // 如果按键信息存在
67 {
68 GUI_DispHexAt(irkey.irBtnVal,144,24,2); // 打印按键值
69 GUI_DispHexAt(irkey.irBtnCnt,144,72,2); // 打印按键计数
70
71 // 打印按键图标或名称
72 GUI_GotoXY(144,120); // 指的光标位置
73 GUI_ClearArea(); // 清除指定位置数据
74 switch(irkey.irBtnVal) // 判断按键值,打印相应图标或名称
75 {
76 case REMOTE_BTN_SWITCH: GUI_DispString("'switch'"); break;
77 case REMOTE_BTN_MENU: GUI_DispString("'menu'"); break;
78 case REMOTE_BTN_MUTE: GUI_DispString("'mute'"); break;
79 case REMOTE_BTN_MODE: GUI_DispString("'mode'"); break;
80 case REMOTE_BTN_PLUS: GUI_DispString("'+'"); break;
81 case REMOTE_BTN_RETURN: GUI_DispString("'return'"); break;
82 case REMOTE_BTN_REWIND: GUI_DispString("'|<<'"); break;
83 case REMOTE_BTN_PAUSE: GUI_DispString("'>||'"); break;
84 case REMOTE_BTN_FASTFORWARD: GUI_DispString("'>>|'"); break;
85 case REMOTE_BTN_0: GUI_DispString("'0'"); break;
86 case REMOTE_BTN_LESS: GUI_DispString("'-'"); break;
87 case REMOTE_BTN_OK: GUI_DispString("'OK'"); break;
88 case REMOTE_BTN_1: GUI_DispString("'1'"); break;
89 case REMOTE_BTN_2: GUI_DispString("'2'"); break;
90 case REMOTE_BTN_3: GUI_DispString("'3'"); break;
91 case REMOTE_BTN_4: GUI_DispString("'4'"); break;
92 case REMOTE_BTN_5: GUI_DispString("'5'"); break;
93 case REMOTE_BTN_6: GUI_DispString("'6'"); break;
94 case REMOTE_BTN_7: GUI_DispString("'7'"); break;
95 case REMOTE_BTN_8: GUI_DispString("'8'"); break;
96 case REMOTE_BTN_9: GUI_DispString("'9'"); break;
97 }
98 }
99 }
100 }
10.5.3 gyu_util.c
请参照实验01中的介绍。
基础实验中的其他例程,大部分都是使用的相同的时钟配置函数,有特殊的时钟使用,将会在对应例程的源码详解中做针对性说明。
10.5.4 gyu_irc
初始化定时器TIM3,首先配置TIM3的时钟为1MHz(也就是1us),我们设置它向上自动装载,并且设置自动装载值为10000,通过计算可以知道装满一次需要10ms。
然后我们需要设置输入捕获的参数,我们配置上升沿下降沿都捕获,并且设置8个时钟周期的滤波(防止误识别)。
最后使能TIM3的中断,并且开始捕获TIM3的通道1(也就是PC6引脚)。
59 void MX_TIM3_Init(void)
60 {
61 htim3.Instance = TIM3; // 通用定时器3
62 htim3.Init.Prescaler = 80-1; // TIM3 80预分频器(APB2总线),80MHz / 80 = 1MHz(1us)
63 htim3.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数器
64 htim3.Init.Period = 10000; // 自动装载值设为10000,装满一次 10000 * 1us = 10ms
65 htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;// 不分频
66 htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; // 自动加载使能
67 if (HAL_TIM_IC_Init(&htim3) != HAL_OK) // 初始化TIM3,出错则进入错误处理函数
68 {
69 _Error_Handler(__FILE__, __LINE__);
70 }
71
72 // 初始化TIM3输入捕获参数
73 TIM_IC_InitTypeDef sConfigIC; // 定义输入捕获结构体(IC:Input capture)
74 sConfigIC.ICPolarity = TIM_ICPOLARITY_BOTHEDGE; // 上升沿下降沿都捕获
75 sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; // 配置为TI1
76 sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; // 不分频
77 sConfigIC.ICFilter = 0x03; // IC1F=0011 8个定时器时钟周期滤波
78 // 初始化TIM3 CH1通道,出错则进入错误处理函数
79 if (HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
80 {
81 _Error_Handler(__FILE__, __LINE__);
82 }
83
84 HAL_TIM_Base_Start_IT(&htim3); // 使能更新中断(也就是TIM_IT_UPDATE)
85 HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1); // 开始捕获TIM3 CH1
86 }
使能GPIOC以及TIM3的时钟,并且配置PC6为TIM3的通道1。
96 void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
97 {
98 GPIO_InitTypeDef GPIO_Initure;
99 __HAL_RCC_TIM3_CLK_ENABLE(); // 使能TIM3时钟
100 __HAL_RCC_GPIOC_CLK_ENABLE(); // 开启GPIOC时钟
101
102 GPIO_Initure.Pin = GPIO_PIN_6; // PC6
103 GPIO_Initure.Mode = GPIO_MODE_AF_PP; // 推挽输出
104 GPIO_Initure.Pull = GPIO_PULLUP; // 上拉
105 GPIO_Initure.Speed = GPIO_SPEED_HIGH; // 高速模式
106 GPIO_Initure.Alternate = GPIO_AF2_TIM3; // PC6配置为TIM3通道1
107 HAL_GPIO_Init(GPIOC, &GPIO_Initure);
108
109 HAL_NVIC_SetPriority(TIM3_IRQn, 10, 0); // 设置TIM3中断优先级
110 HAL_NVIC_EnableIRQ(TIM3_IRQn); // 使能TIM3中断
111 }
定时器周期中断回调函数,TIM3的自动装载值装满一次,进入一次此回调(本工程配置的参数是10ms进入一次)。
我们判断是否已经接收到引导码(根据引导码标志位判断),一旦接收到引导码,我们认为已经开始了一次NEC数据的接收。
如果是接收到引导码之后,第一次进入此函数,那么我们使能记录遥控器按键值的标志位,也就是代表接收到了一次NEC数据(不管数据对错)。
如果进入的次数少于11次,则继续增加计数,当计数值等于11时(也就是从接收到引导码已经过去至少110ms时),我们认为一次NEC的数据获取已经完成,此时清除周期回调的计数值,并且删除引导码标志位(下次再进到这个函数时,只有新的引导码数据到来,才会进行新的数据处理)。
122 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
123 {
124 if(htim->Instance == TIM3)
125 {
126 if(irStatus & IR_STATUS_BootCode) // 如果接收到引导码
127 {
128 irStatus &= ~IR_STATUS_Rising; // 删除上升沿标记(以防止本次出错,确保下次采集流程正确)
129
130 if(tim3Cnt == 0) // 如果是第一次进入(计数为0)
131 {
132 irStatus |= IR_STATUS_BtnInfo; // 记录已经获取到IR按键信号(遥控器按键值)
133 }
134 if((tim3Cnt & 0X0F) < 11) // 进入回调少于11次
135 {
136 tim3Cnt++; // 计数值自加
137 }
138 else // 超过11次,代表一次采集超时(不论是否成功)
139 {
140 irStatus &= ~IR_STATUS_BootCode;// 删除引导码标记
141 tim3Cnt = 0; // 清除计数值
142 }
143 }
144 }
145 }
处理TIM3 CH1(PC6引脚)捕获的数据,这边要记得初始化中我们使能了上升沿下降沿都捕获数据。
当有边沿捕获到来,我们判断此时PC6引脚的电平。
如果此时是高电平,则代表刚刚的是上升沿,此时我们使能上升沿标志位,并且清空定时器计数值。
如果此时是低电平,且上升沿标识位被置位,则我们根据获取到的计数值(也就是上一次高电平的持续时间)来判断本段PWM波代表的含义。
155 void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
156 {
157 // 判断是否为TIM3 CH1捕获产生的回调
158 if((htim->Instance == TIM3) && (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1))
159 {
160 if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_6)) // 获取PC6引脚电平,如果是高电平,则代表是上升沿捕获
161 {
162 __HAL_TIM_SET_COUNTER(&htim3, 0); // 清除TIM3定时器计数值
163 irStatus |= IR_STATUS_Rising; // 标记上升沿捕获
164 }
165 else //如果是低电平,则代表是下降沿触发
166 {
167 tim3Val = HAL_TIM_ReadCapturedValue(&htim3, TIM_CHANNEL_1); // 读取TIM3 CH1定时器计数值
168
169 if(irStatus & IR_STATUS_Rising) // 存在上升沿标记,我们比较定时器计数值
170 {
171 if(tim3Val > 260 && tim3Val < 860) // 高电平持续560us代表bit 0,范围560us±300us
172 {
173 irRecData <<= 1; // 左移一位
174 irRecData |= 0; // bit位赋值0
175 }
176 else if(tim3Val > 1380 && tim3Val < 1980) // 高电平持续1680us代表bit 1,范围1680us±300us
177 {
178 irRecData <<= 1; // 左移一位
179 irRecData |= 1; // bit位赋值1
180 }
181 else if(tim3Val > 2200 && tim3Val < 2800) // 高电平持续2500us代表本次按键结束,范围2500us±300us
182 {
183 irCnt++; // 按键次数新增1
184 tim3Cnt = 0; // 清除计数值
185 }
186 else if(tim3Val > 4200 && tim3Val < 4800) // 高电平持续4500us代表新的按键,范围4500us±300us
187 {
188 irStatus |= IR_STATUS_BootCode; // 标记引导码
189 irCnt = 0; // 有新的按键到来,清除按键计数
190 }
191 }
192
193 irStatus &= ~IR_STATUS_Rising; // 清除上升沿标记
194 }
195 }
196 }
轮询当前的按键信息。如果按键值标识存在,则代表有按键被按下,接着判断地址码以及数据数据正确,并将最终的按键数据添加到irinfo中留给应用层调用。
206 irInfo_t IRBNT_POLL(void)
207 {
208 irInfo_t irinfo = {0,0}; // 定义按键信息结构体
209 uint8_t bcode, dcode; // 定义引导码正反编码
210 uint8_t bvalue, dvalue; // 定义按键值正反编码
211
212 if(irStatus & IR_STATUS_BtnInfo) // 如果获取到按键值
213 {
214 bcode = irRecData >> 24; // 地址码
215 dcode = (irRecData >> 16) & 0xff; // 地址码反编码
216
217 if((bcode == (uint8_t)~dcode) && bcode == REMOTE_DEVICE_ID) // 判断地址码是否正确
218 {
219 bvalue = irRecData >> 8; // 按键值
220 dvalue = irRecData; // 按键值反编码
221
222 if(bvalue == (uint8_t)~dvalue) // 判断按键值是否正确
223 {
224 irinfo.irBtnVal = bvalue; // 将按键值赋给irinfo.irBtnVal
225 }
226 }
227
228 irinfo.irBtnCnt = irCnt; // 将按键此处赋给irinfo.irBtnCnt
229 }
230
231 return irinfo; // 返回按键信息
232 }
11 实验10-串口打印
串口打印实验,给大家展示的是如何配置STM32L476一个有效的硬件串口功能,并且顺带给大家介绍了如何去配置一个格式化打印函数printf()。
11.1 STM32L476 UART简介
USART主要功能:
•全双工异步通信
•NRZ标准格式(标记/空格)
•可配置的过采样方法16或8,以提供速度和速度之间的灵活性
时钟容差
•通用可编程发送和接收波特率高达10 Mbit / s时
时钟频率为80 MHz,过采样为8
•双时钟域允许:
- USART功能和从停止模式唤醒
- 独立于PCLK重新编程的便捷波特率编程
•自动波特率检测
•可编程数据字长(7,8或9位)
•可编程数据顺序,具有MSB优先或LSB优先移位
•可配置的停止位(1或2个停止位)
•同步模式和时钟输出,用于同步通信
•单线半双工通信
•使用DMA进行持续通信
•使用集中式DMA将接收/发送的字节缓冲在保留的SRAM中
•发送器和接收器的独立使能位
•独立的信号极性控制,用于发送和接收
•可交换Tx / Rx引脚配置
•调制解调器和RS-485收发器的硬件流控制
•通信控制/错误检测标志
•奇偶校验控制:
- 传输奇偶校验位
- 检查接收数据字节的奇偶校验
•带有标志的14个中断源
•多处理器通信
如果地址不匹配,USART进入静音模式。
•从静音模式唤醒(通过空闲线路检测或地址标记检测)
11.2 硬件设计
选择STM32L4引脚PA9和PA10作为串口,当我们将拨码开关SW1拨到USB一端时,此串口通过CH340芯片转成USB接口,用于向电脑上打印一些调试信息。
11.3 实验准备
- 使用miniUSB线及10pin排线,通过Jlink仿真器连接PC端和开发板。
- 使用miniUSB线,连接PC与开发板USB接口。
- 将SW1拨到USB端,SW2拨到MCU。
- 使用Keil打开基础实验的实验10-串口打印。
- 使用Xshell打开miniUSB虚拟出的COM口
- 下载程序,并完成功能测试。
11.4 实验验证
下载完成后,我们打开miniUSB虚拟出的COM口,可以看到串口周期性的打印计数值。
11.5 源码详解
本节中的源码说明,仅针对此例程中的重要功能,详细的源码介绍请大家参照代码后的注释。
11.5.1 stm32l4xx_hal_conf.h
此文件位于“实验10-串口打印\Inc”路径中,主要用途是选择使能此例程使用到的库文件。
此例程我们主要给大家展示STM32L4的串口功能,所以我们宏定义中打开UART相关的。
103 // 使能的宏
104 #define HAL_MODULE_ENABLED // 芯片
105 #define HAL_FLASH_MODULE_ENABLED // Flash
106 #define HAL_PWR_MODULE_ENABLED // 电源
107 #define HAL_RCC_MODULE_ENABLED // 时钟
108 #define HAL_CORTEX_MODULE_ENABLED // NVIC
109
110 #define HAL_GPIO_MODULE_ENABLED // GPIO
111 #define HAL_DMA_MODULE_ENABLED // DMA
112 #define HAL_UART_MODULE_ENABLED // UART
11.5.2 main.c
main函数,我们的例程由此处开始执行,首先调用HAL_Init()函数初始化我们的模块,接着调用SystemClock_Config()函数初始化此例程用到的时钟,具体有哪些时钟被初始化,在gyu_util.c部分有详细说明。
接下来我们初始化串口UASRT1。
在while()循环中,我们每隔100ms通过格式化输出"TimeCount = xx:xx:xx"。
34 int main(void)
35 {
36 uint32_t hour = 0;
37 uint32_t minute = 0;
38 uint32_t second = 0;
39
40 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
41 // 重置所有外设、flash界面以及系统时钟
42 HAL_Init();
43
44 // 配置系统时钟(包含振荡器、系统时钟、总线时钟等等)
45 SystemClock_Config();
46
47 // 初始化串口USART1
48 MX_USART1_UART_Init();
49
50 //
51 while (1)
52 {
53 // 模拟时钟计时,这边的1s实际只是100ms
54 HAL_Delay(100); // 100ms延时
55 printf("TimeCount = %02d:%02d:%02d\r\n",hour,minute,second); // 格式化输出"TimeCount = xx:xx:xx"
56
57 // 时分秒计数
58 second++;
59 if(second == 60)
60 {
61 second = 0;
62 minute++;
63 }
64 if(minute == 60)
65 {
66 minute = 0;
67 hour++;
68 }
69 if(hour == 24)
70 {
71 hour = 0;
72 }
73 }
74 }
11.5.3 gyu_util.c
请参照实验01中的介绍。
基础实验中的其他例程,大部分都是使用的相同的时钟配置函数,有特殊的时钟使用,将会在对应例程的源码详解中做针对性说明。
11.5.4 gyu_usartc
串口初始化函数,配置串口协议:波特率115200,数据位8位,停止位1位,无校验位,无流控制。
37 void MX_USART1_UART_Init(void)
38 {
39 // 配置串口参数
40 huart1.Instance = USART1; // UART寄存器基础地址,定义为USART1的
41 huart1.Init.BaudRate = 115200; // 串口波特率为115200
42 huart1.Init.WordLength = UART_WORDLENGTH_8B; // 串口数据位为8位
43 huart1.Init.StopBits = UART_STOPBITS_1; // 串口停止位为1位
44 huart1.Init.Parity = UART_PARITY_NONE; // 串口无校验位
45 huart1.Init.Mode = UART_MODE_TX_RX; // 串口模式,TX和RX作用
46 huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 串口无流控制
47 huart1.Init.OverSampling = UART_OVERSAMPLING_16; // 16位过采样
48 huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE; // 1位过采样禁能
49 huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT; // 没有串口高级功能初始化
50
51 // 串口初始化
52 if (HAL_UART_Init(&huart1) != HAL_OK)
53 {
54 _Error_Handler(__FILE__, __LINE__); // 如果初始化失败,进入错误处理任务
55 }
56
57 }
配置串口硬件,使能GPIOA以及USART1的时钟,配置PA9和PA10为串口的TX及RX引脚。
67 void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
68 {
69 // 定义GPIO结构体
70 GPIO_InitTypeDef GPIO_InitStruct;
71
72 // 判断选择的是否为USART1
73 if(uartHandle->Instance==USART1)
74 {
75 // 使能GPIOA引脚时钟(因为选择的TX和RX分别为PA9和PA10)
76 __HAL_RCC_GPIOA_CLK_ENABLE();
77
78 // 使能USART1时钟
79 __HAL_RCC_USART1_CLK_ENABLE();
80
81 // GPIO配置
82 GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10; // 选择USART1的TX和RX引脚(TX:PA9,RX:PA10)
83 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 推挽输出
84 GPIO_InitStruct.Pull = GPIO_PULLUP; // 上拉
85 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;// 引脚频率5-80MHz
86 GPIO_InitStruct.Alternate = GPIO_AF7_USART1; // 配置为USART1
87 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 初始化引脚
88 }
89 }
配置fputc()函数,用于格式化打印,当我们进行了如下代码配置,就可以调用printf()函数去格式化打印调试信息。
99 #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
100
101 PUTCHAR_PROTOTYPE
102 {
103 // 配置格式化输出到串口USART1
104 HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
105
106 return ch;
107 }
12 实验11-串口中断
13 实验12-串口DMA
14 实验13-TFT显示屏
15 实验14-二维码显示
16 实验15-RNG随机发生器
RNG实验给大家展示一下随机数的生成,在这个实验中,除了main.c以及gyu_rng.c两个关键文件外,我们还需要注意一下gyu_util.c中系统时钟配置,具体的原因,请大家查看下面的工程介绍。
16.1 STM32L476 随机发生器简介
随机发生器定义:
RNG是一个真正的随机数发生器,它基于模拟噪声源连续提供32位熵样本。它可以被应用程序用作活动熵源,以构建符合NIST的确定性随机比特生成器(DRBG)。 RNG真随机数发生器已根据德国AIS-31标准进行了验证。
真随机数发生器生成条件:
- RNG时钟rng_clk = 48 MHz
- AHB时钟rng_hclk = 60 MHz
16.2 硬件设计
使用STM32L476内部RNG随机数发生器。
16.3 实验准备
- 使用miniUSB线及10pin排线,通过Jlink仿真器连接PC端和开发板。
- 使用miniUSB线,连接PC与开发板USB接口。
- 将SW1拨到USB端,SW2拨到MCU。
- 使用Keil打开基础实验的实验15-RNG随机发生器。
- 使用Xshell打开miniUSB虚拟出的COM口。
- 下载程序,并完成功能测试。
16.4 实验验证
下载完成后,我们打开miniUSB虚拟出的COM口,每按下一次S1(btn_up),STM32L476都会向串口打印一个随机数。
16.5 源码详解
本节中的源码说明,仅针对此例程中的重要功能,详细的源码介绍请大家参照代码后的注释。
16.5.1 stm32l4xx_hal_conf.h
此文件位于“实验15-RNG随机发生器\Inc”路径中,主要用途是选择使能此例程使用到的库文件。
此例程我们主要给大家展示STM32L4的随机发生器生成随机数的功能,所以我们宏定义中打开RNG相关的。
103 #define HAL_MODULE_ENABLED // 芯片
104 #define HAL_FLASH_MODULE_ENABLED // Flash
105 #define HAL_PWR_MODULE_ENABLED // 电源
106 #define HAL_RCC_MODULE_ENABLED // 时钟
107 #define HAL_CORTEX_MODULE_ENABLED // NVIC
108
109 #define HAL_GPIO_MODULE_ENABLED // GPIO
110 #define HAL_DMA_MODULE_ENABLED // DMA
111 #define HAL_UART_MODULE_ENABLED // UART
112 #define HAL_SPI_MODULE_ENABLED // SPI
113 #define HAL_RNG_MODULE_ENABLED // RNG随机发生器
16.5.2 main.c
main函数,我们的例程由此处开始执行,首先调用HAL_Init()函数初始化我们的模块,接着调用SystemClock_Config()函数初始化此例程用到的时钟,具体有哪些时钟被初始化,在gyu_util.c部分有详细说明。
接下来我们初始化串口UASRT1、按键、以及TFT显示屏。
接下来是我们这个工程的关键,初始化RNG部分。
在while()循环中,我们调用KEY_Poll()函数去轮询是否有按键被按下。
41 int main(void)
42 {
43 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
44 // 重置所有外设、flash界面以及系统时钟
45 HAL_Init();
46
47 // 配置系统时钟(包含振荡器、系统时钟、总线时钟等等)
48 SystemClock_Config();
49
50 // 初始化USART1
51 MX_USART1_UART_Init();
52
53 // 初始化按键引脚
54 MX_KEY_Init();
55
56 // 注册按钮回调函数
57 KEY_RegisterCb(AppKey_cb);
58
59 // LCD SPI初始化
60 LCD_GPIO_Init(); // LCD IO控制引脚(例如背光)
61 MX_SPI1_Init(); // LCD SPI控制引脚
62
63 // 图形界面初始化
64 GUI_Init(); // GUI界面初始化
65 GUI_Clear(); // 清屏
66
67 // 打印logo到位置X->0,Y->0
68 GUI_DrawBitmap(&bmLogo,0,0);
69
70 // 随机发生器初始化
71 MX_RNG_Init();
72
73 //
74 while(1)
75 {
76 KEY_Poll(); // 按键轮训,监测是否有按键被按下
77 }
78 }
在按键回调函数中,可以看到,每次S1(key_up)按键被按下,都是调用RNG_Get()函数去获取一次随机数。
89 void AppKey_cb(uint8_t key)
90 {
91 // 如果有相应按键被按下,则串口打印调试信息
92 if(key & KEY_UP)
93 {
94 RNG_Get(); // 获取随机数
95 }
96 }
16.5.3 gyu_util.c
此实验因为真随机数发生器的条件限制,我们配置的时钟和之前的例程有稍许不同。
首先是我们的AHB时钟,此实验配置为60MHz,之前的实验都是80MHz。
其次是此实验我们使用到了MSI(并且配置为RCC_MSIRANGE_11,也就是48MHz),在下面的外设功能配置中,我们选择此48MHz时钟作为RNG的时钟。
49 void SystemClock_Config(void)
50 {
51 RCC_OscInitTypeDef RCC_OscInitStruct; // 定义RCC内部/外部振荡器结构体
52 RCC_ClkInitTypeDef RCC_ClkInitStruct; // 定义RCC系统,AHB和APB总线时钟配置结构体
53 RCC_PeriphCLKInitTypeDef PeriphClkInit; // 定义RCC扩展时钟结构体
54
55 // 配置LSE驱动器功能为低驱动能力
56 __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_LOW);
57
58 // 初始化CPU,AHB和APB总线时钟
59 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSE
60 |RCC_OSCILLATORTYPE_LSE|RCC_OSCILLATORTYPE_MSI; // 设置需要配置的振荡器为HSI、HSE、LSE、MSI
61 // 配置HSE
62 RCC_OscInitStruct.HSEState = RCC_HSE_ON; // 激活HSE时钟(开发板外部为8MHz)
63 // 配置LSE
64 RCC_OscInitStruct.LSEState = RCC_LSE_ON; // 激活LSE时钟(32.768KHz,低驱动)
65 // 配置HSI
66 RCC_OscInitStruct.HSIState = RCC_HSI_ON; // 激活HSI时钟
67 RCC_OscInitStruct.HSICalibrationValue = 16; // 配置HSI为16MHz
68 // 配置MSI
69 RCC_OscInitStruct.MSIState = RCC_MSI_ON; // 激活MSI时钟(内部高频,最高可配置48MHz)
70 RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_11; // 配置为48MHz
71 // 配置PLL
72 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // 打开PLL
73 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; // 选择HSE时钟作为PLL入口时钟源,8MHz
74 RCC_OscInitStruct.PLL.PLLM = 1; // 配置PLL VCO输入分频为1,8/1 = 8MHz
75 RCC_OscInitStruct.PLL.PLLN = 15; // 配置PLL VCO输入倍增为20,8MHz*15 = 120MHz
76 RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7; // SAI时钟7分频,120/7 = 17.14MHz
77 RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; // SDMMC、RNG、USB时钟2分频,120/2 = 60MHz
78 RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; // 系统主时钟分区2分频,120/2 = 60MHz
79 // RCC时钟配置,出错则进入错误处理函数
80 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
81 {
82 _Error_Handler(__FILE__, __LINE__);
83 }
84
85 // 初始化CPU,AHB和APB总线时钟
86 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
87 |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; // 需要配置的时钟HCLK、SYSCLK、PCLK1、PCLK2
88 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 配置系统时钟为PLLCLK输入,60MHz
89 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB时钟为系统时钟1分频,60/1 = 60MHz
90 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; // APB1时钟为系统时钟1分频,60/1 = 60MHz
91 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // APB2时钟为系统时钟1分频,60/1 = 60MHz
92 // RCC时钟配置,出错则进入错误处理函数
93 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) // HCLK=60MHz,Vcore=3.3V,所以选择SW2(FLASH_LATENCY_2)
94 {
95 _Error_Handler(__FILE__, __LINE__);
96 }
97
98 // 初始化外设时钟
99 PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_USART2
100 |RCC_PERIPHCLK_LPUART1|RCC_PERIPHCLK_LPTIM1
101 |RCC_PERIPHCLK_I2C2|RCC_PERIPHCLK_ADC
102 |RCC_PERIPHCLK_RNG;// 需要初始化的外设时钟:USART1、USART2、LPUART1、LPTIM1、I2C2、ADC、RNG
103 PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2; // 配置串口USART1时钟为PCLK2,60MHz
104 PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1; // 配置串口USART2时钟为PCLK1,60MHz
105 PeriphClkInit.Lpuart1ClockSelection = RCC_LPUART1CLKSOURCE_HSI; // 配置LPUART时钟为HSI,16MHz
106 PeriphClkInit.I2c2ClockSelection = RCC_I2C2CLKSOURCE_PCLK1; // 配置I2C2时钟为PCLK1,60MHz
107 PeriphClkInit.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSE; // 配置LPTIM1时钟为LSE,32.768KHz
108 PeriphClkInit.AdcClockSelection = RCC_ADCCLKSOURCE_PLLSAI1; // 配置ADC时钟为PLLSAI1,现在为60MHz,下面会重新定义
109 PeriphClkInit.PLLSAI1.PLLSAI1Source = RCC_PLLSOURCE_HSE; // 配置PLLSAI1时钟为HSE,8MHz
110 PeriphClkInit.PLLSAI1.PLLSAI1M = 1; // 配置PLLSAI1分频为1
111 PeriphClkInit.PLLSAI1.PLLSAI1N = 8; // 配置PLLSAI1倍增为8
112 PeriphClkInit.PLLSAI1.PLLSAI1P = RCC_PLLP_DIV7; // SAI时钟7分频,64/7 = 9.142857MHz
113 PeriphClkInit.PLLSAI1.PLLSAI1Q = RCC_PLLQ_DIV2; // SDMMC、USB时钟2分频,64/2 = 32MHz
114 PeriphClkInit.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV2; // 系统主时钟分区2分频,64/2 = 32MHz
115 PeriphClkInit.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_ADC1CLK; // 配置PLLSAI1输出为ADC1时钟,也就是配置ADC1时钟,32MHz
116 PeriphClkInit.RngClockSelection = RCC_RNGCLKSOURCE_MSI;
117
118 // 外设时钟配置,出错则进入错误处理函数
119 if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
120 {
121 _Error_Handler(__FILE__, __LINE__);
122 }
123
124 // 配置内部主稳压器输出电压,配置为稳压器输出电压范围1模式,也就是:典型输出电压为1.2V,系统频率高达80MHz
125 if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
126 {
127 _Error_Handler(__FILE__, __LINE__);
128 }
129
130 // 配置系统定时器中断时间,配置为HCLK的千分频
131 HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
132
133 // 配置系统定时器,配置为HCLK
134 HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
135
136 // 系统定时器中断配置,设置系统定时器中断优先级最高(为0),且子优先级最高(为0)
137 HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
138 }
16.5.4 gyu_rng.c
初始化RNG。
43 void MX_RNG_Init(void)
44 {
45 hrng.Instance = RNG;
46 if (HAL_RNG_Init(&hrng) != HAL_OK)
47 {
48 _Error_Handler(__FILE__, __LINE__);
49 }
50 }
使能RNG时钟,并且配置和使能RNG中断。
60 void HAL_RNG_MspInit(RNG_HandleTypeDef* hrng)
61 {
62 if(hrng->Instance==RNG)
63 {
64 // 使能RNG时钟
65 __HAL_RCC_RNG_CLK_ENABLE();
66
67 // 使能NVIC中断及优先级
68 HAL_NVIC_SetPriority(RNG_IRQn, 10, 0);
69 HAL_NVIC_EnableIRQ(RNG_IRQn);
70 }
71 }
随机数的获取函数,在此处打开中断。此处中断开启,会触发HAL_RNG_ErrorCallback()或者HAL_RNG_ReadyDataCallback()回调函数,当有随机数成功生成时,返回HAL_RNG_ReadyDataCallback()回调。
81 void RNG_Get(void)
82 {
83 HAL_RNG_GenerateRandomNumber_IT(&hrng); // 随机数获取函数(开启中断)
84 }
真随机数成功生成的回调函数,参数random32bit就是生成的随机数,我们利用printf函数将随机数打印到串口。
108 void HAL_RNG_ReadyDataCallback(RNG_HandleTypeDef* hrng, uint32_t random32bit)
109 {
110 printf("%u\r\n",random32bit); // 将随机数打印到串口
111 }
17 实验16-RTC实时时钟
实时时钟实验,给大家展示一下STM32L4的实时计时功能,我们提供给大家设置当前时间、日期、以及星期的函数接口,并且提供了一个闹钟配置的函数接口。这样就可以完成一个简单的老式闹钟的功能展示。
17.1 STM32L476 RTC时钟简介
简介:
RTC提供自动唤醒功能,可管理所有低功耗模式。
实时时钟(RTC)是一个独立的BCD定时器/计数器。 RTC提供具有可编程报警中断的时间时钟/日历。
RTC还包括具有中断功能的周期性可编程唤醒标志。
两个32位寄存器包含秒,分钟,小时(12或24小时格式),日(星期几),日期(星期几),月和年,以二进制编码的十进制格式(BCD)表示。sub-seconds值也以二进制格式提供。
自动执行28,29(闰年),30天和31天月的补偿。还可以执行夏令时补偿。
其他32位寄存器包含可编程报警亚秒,秒,分钟,小时,日和日期。
数字校准功能可用于补偿晶体振荡器精度的任何偏差。
备份域复位后,所有RTC寄存器都受到保护,以防止可能的寄生写访问。
只要电源电压保持在工作范围内,RTC就不会停止,无论器件状态如何(运行模式,低功耗模式或欠复位)。
RTC主要功能:
•日历,包括亚秒,秒,分钟,小时(12或24格式),日(星期几),日期(日期),月和年。
•可通过软件编程的夏令时补偿。
•带中断功能的可编程报警。可以通过日历字段的任意组合触发警报。
•自动唤醒单元生成周期性标志,触发自动唤醒中断。
•参考时钟检测:可以使用更精确的第二个源时钟(50或60 Hz)来提高日历精度。
•使用亚秒移位功能与外部时钟精确同步。
•数字校准电路(周期性计数器校正):在几秒钟的校准窗口中获得0.95 ppm的精度
•用于事件保存的时间戳功能
•具有可配置滤波器和内部上拉的篡改检测事件
•可屏蔽中断/事件:
- 报警A.
- 闹钟B.
- 唤醒中断
- 时间戳
- 篡改检测
•32个备份寄存器
17.2 硬件设计
选择STM32L4内部RTC实时时钟。
17.3 实验准备
- 使用miniUSB线及10pin排线,通过Jlink仿真器连接PC端和开发板。
- 使用miniUSB线,连接PC与开发板USB接口。
- 使用Keil打开基础实验的实验16-RTC实时时钟
- 下载程序,并完成功能测试。
17.4 实验验证
下载完成后,可以看到TFT屏幕上打印当前的实时时间、日期以及星期,且10s后会触发闹钟(表现为TFT打印"Alarm"、蜂鸣器哔一声)。
17.5 源码详解
本节中的源码说明,仅针对此例程中的重要功能,详细的源码介绍请大家参照代码后的注释。
17.5.1 stm32l4xx_hal_conf.h
此文件位于“实验16-RTC实时时钟\Inc”路径中,主要用途是选择使能此例程使用到的库文件。
此例程我们主要给大家展示STM32L4的实时时钟功能,所以我们宏定义中打开RTC相关的。
103 // 使能的宏
104 #define HAL_MODULE_ENABLED // 芯片
105 #define HAL_FLASH_MODULE_ENABLED // Flash
106 #define HAL_PWR_MODULE_ENABLED // 电源
107 #define HAL_RCC_MODULE_ENABLED // 时钟
108 #define HAL_CORTEX_MODULE_ENABLED // NVIC
109
110 #define HAL_GPIO_MODULE_ENABLED // GPIO
111 #define HAL_DMA_MODULE_ENABLED // DMA
112 #define HAL_UART_MODULE_ENABLED // UART
113 #define HAL_SPI_MODULE_ENABLED // SPI
114 #define HAL_RTC_MODULE_ENABLED // RTC实时时钟
17.5.2 main.c
main函数,我们的例程由此处开始执行,首先调用HAL_Init()函数初始化我们的模块,接着调用SystemClock_Config()函数初始化此例程用到的时钟,具体有哪些时钟被初始化,在gyu_util.c部分有详细说明。
接下来我们初始化TFT彩屏相关的SPI控制接口,以及GUI图形界面初始化。并且格式化打印一些内容,字体显示为红色的部分。
最后我们初始化RTC时钟,并且调用实现好的有关RTC的函数,去配置当前的时钟、日期、星期、以及闹钟。
在while()循环当中,持续获取当前的时钟、日期等相关信息,并且打印到TFT彩屏上显示。
当检测到闹钟信息,则在TFT彩屏上打印"Alarm"字样指示闹钟,停止蜂鸣器工作,并且让闹钟标识置为0。
44 int main(void)
45 {
46 HAL_StatusTypeDef status;
47
48 RTC_TimeTypeDef sTime;
49 RTC_DateTypeDef sDate;
50
51 HAL_Init();
52
53 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
54 // 重置所有外设、flash界面以及系统时钟
55 // 配置系统时钟(包含振荡器、系统时钟、总线时钟等等)
56 SystemClock_Config();
57
58 // LCD SPI初始化
59 LCD_GPIO_Init(); // LCD IO控制引脚(例如背光)
60 MX_SPI1_Init(); // LCD SPI控制引脚
61
62 // 图形界面初始化
63 GUI_Init(); // GUI界面初始化
64 GUI_Clear(); // 清屏
65 GUI_SetColor(GUI_Crimson); // 红色字体
66 GUI_SetBkColor(GUI_Gold); // 金色背景
67 // 格式化打印如下的内容
68 GUI_DispStringAt("Time: : :",24,24);
69 GUI_DispStringAt("Date: 20 / /",24,72);
70 GUI_DispStringAt("Week: ",24,120);
71
72 // 初始化蜂鸣器
73 Buzzer_Init();
74
75 // RTC初始化
76 MX_RTC_Init();
77 RTC_TIME_Set(16,12,30); // 设置当前时间 时,分,秒
78 RTC_DATE_Set(2,18,12,25); // 设置当前日期 星期,年,月,日
79 RTC_AlarmA_Set(16,12,40,2); // 设置闹钟 时,分,秒,星期
80
81 //
82 while(1)
83 {
84 GUI_SetColor(GUI_Aqua);
85
86 // 获取时分秒,并且打印到TFT屏
87 status = HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
88 if(status == HAL_OK)
89 {
90 GUI_DispDecAt(sTime.Hours,96,24,2);
91 GUI_DispDecAt(sTime.Minutes,132,24,2);
92 GUI_DispDecAt(sTime.Seconds,168,24,2);
93 }
94
95 // 获取年月日、星期,并且打印到TFT屏
96 status = HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
97 if(status == HAL_OK)
98 {
99 GUI_DispDecAt(sDate.Year,120,72,2);
100 GUI_DispDecAt(sDate.Month,156,72,2);
101 GUI_DispDecAt(sDate.Date,192,72,2);
102 GUI_DispDecAt(sDate.WeekDay,96,120,2);
103 }
104
105 status = HAL_ERROR;
106
107 if(rtcAlarm)
108 {
109 rtcAlarm = 0;
110 Buzzer_SET(GPIO_PIN_RESET); // 关闭蜂鸣器
111 GUI_DispStringAt("AlarmA",24,168); // 显示屏打印闹钟A标志
112 }
113 }
114 }
17.5.3 gyu_util.c
请参照实验01中的介绍。
基础实验中的其他例程,大部分都是使用的相同的时钟配置函数,有特殊的时钟使用,将会在对应例程的源码详解中做针对性说明。
17.5.4 gyu_rtc.c
RTC时钟初始化函数,我们配置为24小时模式,禁止RTC输出,并且初始化RTC时钟。
50 void MX_RTC_Init(void)
51 {
52 hrtc.Instance = RTC; // 配置为RTC
53 hrtc.Init.HourFormat = RTC_HOURFORMAT_24; // 24小时模式
54 hrtc.Init.AsynchPrediv = 127; // 异步预分频器,固定127(0x7F)
55 hrtc.Init.SynchPrediv = 255; // 同步预分频器,固定255(0xFF)
56 hrtc.Init.OutPut = RTC_OUTPUT_DISABLE; // 禁止RTC输出
57
58 // 初始化RTC时钟
59 if (HAL_RTC_Init(&hrtc) != HAL_OK)
60 {
61 _Error_Handler(__FILE__, __LINE__);
62 }
63 }
RTC初始化函数,使能RTC时钟。
73 void HAL_RTC_MspInit(RTC_HandleTypeDef* hrtc)
74 {
75 if(hrtc->Instance==RTC)
76 {
77 __HAL_RCC_RTC_ENABLE(); // 使能RTC时钟
78 }
79 }
设置RTC时钟的函数,用于设置当前的时间(分别设置时分秒)。
91 void RTC_TIME_Set(uint8_t hour, uint8_t min, uint8_t sec)
92 {
93 RTC_TimeTypeDef sTime; // RTC时间结构体
94
95 sTime.Hours = hour; // 时
96 sTime.Minutes = min; // 分
97 sTime.Seconds = sec; // 秒
98 sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; // 不定义AM和PM,因为24小时模式
99 sTime.StoreOperation = RTC_STOREOPERATION_RESET; //
100
101 // 设置时钟
102 if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
103 {
104 _Error_Handler(__FILE__, __LINE__);
105 }
106 }
设置RTC日期、星期的函数,用于设置当前的年月日、以及星期。
119 void RTC_DATE_Set(uint8_t week, uint8_t year, uint8_t monty, uint8_t date)
120 {
121 RTC_DateTypeDef sDate; // RTC日期结构体
122
123 sDate.WeekDay = week; // 星期
124
125 sDate.Year = year; // 年
126 sDate.Month = monty; // 月
127 sDate.Date = date; // 日
128
129 // 设置日期
130 if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
131 {
132 _Error_Handler(__FILE__, __LINE__);
133 }
134 }
闹钟时间设置的函数,用于设置闹钟触发的时分秒。
147 void RTC_AlarmA_Set(uint8_t hour, uint8_t min, uint8_t sec, uint8_t week)
148 {
149 RTC_AlarmTypeDef sAlarm; // RTC闹钟A结构体
150
151 sAlarm.AlarmTime.Hours = hour; // 时
152 sAlarm.AlarmTime.Minutes = min; // 分
153 sAlarm.AlarmTime.Seconds = sec; // 秒
154 sAlarm.AlarmTime.SubSeconds = 0; // 亚秒(这边没有作比较,任意设置)
155
156 sAlarm.AlarmMask = RTC_ALARMMASK_NONE; // 精确到时分秒、日期或者星期
157 sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL; // 亚秒不作比较
158 sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_WEEKDAY; // 比较星期(设置星期或者日期)
159 sAlarm.AlarmDateWeekDay = week; // 星期
160
161 sAlarm.Alarm = RTC_ALARM_A; // 闹钟A
162
163 // 开启闹钟中断
164 if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN) != HAL_OK)
165 {
166 _Error_Handler(__FILE__, __LINE__);
167 }
168
169 HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 10, 0); // 抢占优先级10,子优先级0
170 HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn); // 使能中断
171 }
闹钟回调函数,当RTC实时时钟计时到达设置的闹钟时间,就会触发此回调,我们在回调中打开蜂鸣器,并且置位闹钟的标识。
181 void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
182 {
183 Buzzer_SET(GPIO_PIN_SET); // 蜂鸣器哔一声
184 rtcAlarm = 1;
185 }
18 实验17-定时器中断
定时器中断实验,这个实验大家可以和之前的红外接收实验对照查看,使用的手段是类似的,都是利用定时器的计时功能,不同点在于红外接收用的是捕获,而此实验则是触发中断。
18.1 STM32L476 定时器中断简介
以下部分为TIM2 / TIM3 / TIM4 / TIM5这四个定时器的介绍。
1.通用定时器简介:
通用定时器由一个由可编程预分频器驱动的16位或32位自动重载计数器组成。它们可用于各种目的,包括测量输入信号的脉冲长度(输入捕获)或生成输出波形(输出比较和PWM)。
使用定时器预分频器和RCC时钟控制器预分频器,可以将脉冲长度和波形周期从几微秒调制到几毫秒。定时器完全独立,不共享任何资源。
2.通用定时器功能
•16位(TIM3,TIM4)或32位(TIM2和TIM5)上,下,上/下自动重载计数器。
•16位可编程预分频器,用于分频(也“在运行中”)计数器时钟
频率由1到65535之间的任何因子组成。
•最多4个独立频道:
- 输入捕获
- 输出比较
- PWM生成(边缘和中心对齐模式)
- 单脉冲模式输出
•同步电路,用外部信号控制定时器并互连
几个计时器。
•以下事件的中断/ DMA生成:
- 更新:计数器溢出/下溢,计数器初始化(通过软件或
内部/外部触发器)
- 触发事件(计数器启动,停止,初始化或通过内部/外部触发计数)
- 输入捕获
- 输出比较
•支持增量(正交)编码器和霍尔传感器电路进行定位
目的
•外部时钟或逐周期电流管理的触发输入
3.通用定时器中断触发的条件
配置定时器自动装载值,以及分频系数,使能定时器的中断。当计数值向上或者向下溢出时,定时器中断触发。
计算公式为:Tout=(ARR+1)(PSC+1)/TIMxCLK (us)
18.2 硬件设计
选择STM32L4内部TIM2定时器,展示使用的是开发板上的LED指示灯D3。
18.3 实验准备
- 使用miniUSB线及10pin排线,通过Jlink仿真器连接PC端和开发板。
- 使用miniUSB线,连接PC与开发板USB接口。
- 使用Keil打开基础实验的实验17-定时器中断。
- 下载程序,并完成功能测试。
18.4 实验验证
下载完成后,可以看到LED周期闪烁(1s周期,也就是点亮1s,熄灭1s)。
18.5 源码详解
本节中的源码说明,仅针对此例程中的重要功能,详细的源码介绍请大家参照代码后的注释。
18.5.1 stm32l4xx_hal_conf.h
此文件位于“实验16-RTC实时时钟\Inc”路径中,主要用途是选择使能此例程使用到的库文件。
此例程我们主要给大家展示STM32L4的定时器的中断功能,所以我们宏定义中打开TIM相关的。
103 // 使能的宏
104 #define HAL_MODULE_ENABLED // 芯片
105 #define HAL_FLASH_MODULE_ENABLED // Flash
106 #define HAL_PWR_MODULE_ENABLED // 电源
107 #define HAL_RCC_MODULE_ENABLED // 时钟
108 #define HAL_CORTEX_MODULE_ENABLED // NVIC
109
110 #define HAL_GPIO_MODULE_ENABLED // GPIO
111 #define HAL_DMA_MODULE_ENABLED // DMA
112 #define HAL_UART_MODULE_ENABLED // UART
113 #define HAL_SPI_MODULE_ENABLED // SPI
114 #define HAL_TIM_MODULE_ENABLED // TIM定时器
18.5.2 main.c
main函数,我们的例程由此处开始执行,首先调用HAL_Init()函数初始化我们的模块,接着调用SystemClock_Config()函数初始化此例程用到的时钟,具体有哪些时钟被初始化,在gyu_util.c部分有详细说明。
接下来我们分别初始化了LED及TIM2定时器。
32 int main(void)
33 {
34 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
35 // 重置所有外设、flash界面以及系统时钟
36 HAL_Init();
37
38 // 配置系统时钟(包含振荡器、系统时钟、总线时钟等等)
39 SystemClock_Config();
40
41 // 初始化LED引脚
42 LED_Init();
43
44 // 初始化tim2定时器
45 MX_TIM2_Init();
46
47 //
48 while (1)
49 {
50
51 }
52 }
18.5.3 gyu_util.c
请参照实验01中的介绍。
基础实验中的其他例程,大部分都是使用的相同的时钟配置函数,有特殊的时钟使用,将会在对应例程的源码详解中做针对性说明。
18.5.4 gyu_tim2it.c
TIM2定时器初始化函数,我们配置TIM2时钟不分频(也就是系统时钟80MHZ),设置TIM2预分频为10000-1(也就是TIM2时钟为80M/10000 = 8000Hz),设置自动装载值为8000(也就是说一次装载满的时间为8000*(1/8000Hz) = 1s)。
这边有总结好的公式:Tout=(ARR+1)(PSC+1)/TIMxCLK (us)。ARR代表预分频,PSC代表自动装载值。
47 void MX_TIM2_Init(void)
48 {
49 htim2.Instance = TIM2; // 配置为TIM2
50 htim2.Init.Prescaler = 10000-1; // ARR+1 = 10000
51 htim2.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数
52 htim2.Init.Period = 8000-1; // PSC+1 = 8000
53 htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; // 不分频,80MHz
54
55 // 初始化TIM2,出错则进入错误处理函数
56 if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
57 {
58 _Error_Handler(__FILE__, __LINE__);
59 }
60
61 HAL_TIM_Base_Start_IT(&htim2); // 开启TIM2,并且使能中断
62 }
TIM2初始化函数,使能定时器TIM2时钟,并且使能中断。
72 void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
73 {
74 if(htim->Instance == TIM2)
75 {
76 __HAL_RCC_TIM2_CLK_ENABLE(); //使能定时器TIM2
77 HAL_NVIC_SetPriority(TIM2_IRQn, 10, 0); //设置中断优先级10,子优先级0
78 HAL_NVIC_EnableIRQ(TIM2_IRQn); //使能ITM3中断
79 }
80 }
定时器中断回调函数,当计数值溢出时,触发此回调,我们判断是否是TIM2触发,如果是,则翻转LED的状态。 在此实验中的表现是,LED灯1s改变一次状态(闪烁)。
90 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
91 {
92 if(htim == (&htim2))
93 {
94 LED_TOGGLE(); // 翻转LED状态,1s翻转一次
95 }
96 }
19 实验18-独立看门狗
独立看门狗实验,STM32L4内部自带两个看门狗,分别叫做“独立看门狗(IWDG)”以及“窗口看门狗(WWDG)”。这两个看门狗的使用,将分别在本例程以及下一个例程给大家讲解使用的方式。
19.1 STM32L476 独立看门狗简介
独立看门狗的作用:
独立看门狗外设检测并解决由于软件故障引起的故障,并在计数器达到给定超时值时触发系统复位。
也就是说当程序跑飞的时候,独立看门狗会复位系统。
独立看门狗的时钟:
独立看门狗(IWDG)由其自己的专用低速时钟(LSI)提供时钟,因此即使主时钟发生故障也能保持活动状态。
也就是说独立看门狗完全是独立运行,不受其他部分程序影响。
19.2 硬件设计
选择STM32L4内部独立看门狗,展示使用的是开发板上的LED指示灯D3、以及用于喂狗的按键引脚S2(BTN2 PC2引脚)。
19.3 实验准备
- 使用miniUSB线及10pin排线,通过Jlink仿真器连接PC端和开发板。
- 使用miniUSB线,连接PC与开发板USB接口。
- 使用Keil打开基础实验的实验18-独立看门狗。
- 下载程序,并完成功能测试。
19.4 实验验证
下载完成后,可以看到LED周期闪烁,这代表我们的STM32运行程序一直在被复位。当我们将按键S2(RIGHT)按键持续按下时,这个时候LED停止闪烁(保持常亮的状态),说明程序没有再被复位。
19.5 源码详解
本节中的源码说明,仅针对此例程中的重要功能,详细的源码介绍请大家参照代码后的注释。
19.5.1 stm32l4xx_hal_conf.h
此文件位于“实验16-RTC实时时钟\Inc”路径中,主要用途是选择使能此例程使用到的库文件。
此例程我们主要给大家展示STM32L4的独立看门狗功能,所以我们宏定义中打开IWDG相关的。
103 // 使能的宏
104 #define HAL_MODULE_ENABLED // 芯片
105 #define HAL_FLASH_MODULE_ENABLED // Flash
106 #define HAL_PWR_MODULE_ENABLED // 电源
107 #define HAL_RCC_MODULE_ENABLED // 时钟
108 #define HAL_CORTEX_MODULE_ENABLED // NVIC
109
110 #define HAL_GPIO_MODULE_ENABLED // GPIO
111 #define HAL_DMA_MODULE_ENABLED // DMA
112 #define HAL_UART_MODULE_ENABLED // UART
113 #define HAL_SPI_MODULE_ENABLED // SPI
114 #define HAL_IWDG_MODULE_ENABLED // 独立看门狗
19.5.2 main.c
main函数,我们的例程由此处开始执行,首先调用HAL_Init()函数初始化我们的模块,接着调用SystemClock_Config()函数初始化此例程用到的时钟,具体有哪些时钟被初始化,在gyu_util.c部分有详细说明。
接下来我们初始化了LED,并且初始化了IWDG配置、以及控制IWDG喂狗的S2按键引脚。
然后我们设置LED点亮,在LED点亮之前我们延时500ms,用来展示程序是否被独立看门狗重启。
在while()循环中,我们持续监测IWDG控制喂狗的按键引脚电平,如果为低电平(按键被按下),则调用喂狗的函数(喂狗之后,独立看门狗不会再复位程序)。
32 int main(void)
33 {
34 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
35 // 重置所有外设、flash界面以及系统时钟
36 HAL_Init();
37
38 // 配置系统时钟(包含振荡器、系统时钟、总线时钟等等)
39 SystemClock_Config();
40
41 // 初始化LED引脚
42 LED_Init();
43
44 // 初始化独立看门狗
45 MX_IWDG_Init();
46 // 初始化独立看门狗用于触发喂狗的S2按键引脚
47 IWDG_FeedIO_Init();
48
49 // 延时500ms,展示软件是否重启(重启则LED闪烁,不重启则LED常亮)
50 HAL_Delay(500);
51 LED_SET(GPIO_PIN_SET);
52
53 //
54 while (1)
55 {
56 // 如果按键RIGHT按下,检测为低电平,则喂狗
57 if(!IWDG_FeedIO_Read())
58 {
59 IWDG_Feed(); // 喂狗
60 }
61 }
62 }
19.5.3 gyu_util.c
请参照实验01中的介绍。
基础实验中的其他例程,大部分都是使用的相同的时钟配置函数,有特殊的时钟使用,将会在对应例程的源码详解中做针对性说明。
19.5.4 gyu_iwdg.c
看门狗初始化函数,我们配置IWDG 32分频(也就是32KHz /32 = 1KHz = 1us),配置重载计数值为1000(也就是装载满一次需要1000*1us = 1s)。
或者大家使用公式计算:Tout=(4*(2^prer)*rlr)/32 (ms)。此例程prer = 3,rlr = 1000。
37 void MX_IWDG_Init(void)
38 {
39 hiwdg.Instance = IWDG; // 配置为IWDG
40 hiwdg.Init.Prescaler = IWDG_PRESCALER_32; // 32分频,prer = 3
41 hiwdg.Init.Window = IWDG_WINDOW_DISABLE; //
42 hiwdg.Init.Reload = 1000; // 重载计数1000,rlr = 1000
43 if (HAL_IWDG_Init(&hiwdg) != HAL_OK) // 初始化IWDG,出错则进入错误处理函数
44 {
45 _Error_Handler(__FILE__, __LINE__);
46 }
47 }
独立看门狗喂狗函数,也就是清除计数值。
57 void IWDG_Feed(void)
58 {
59 HAL_IWDG_Refresh(&hiwdg); // 喂狗
60 }
初始化按键S2(PC2引脚)为一般上拉输入。
72 void IWDG_FeedIO_Init(void)
73 {
74 GPIO_InitTypeDef GPIO_InitStructure; // 定义引脚参数结构体
75
76 __HAL_RCC_GPIOC_CLK_ENABLE(); // 使能GPIOC时钟
77
78 GPIO_InitStructure.Pin= GPIO_PIN_2; // 引脚编号为2
79 GPIO_InitStructure.Mode = GPIO_MODE_INPUT; // 输入
80 GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; // 低频率
81 GPIO_InitStructure.Pull = GPIO_PULLUP; // 上拉
82 HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); // 初始化PC2
83 }
检测当前S2引脚电平,并且返回当前电平状态。
94 uint8_t IWDG_FeedIO_Read(void)
95 {
96 return HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_2);
97 }
20 实验19-窗口看门狗
窗口看门狗实验,STM32L4内部自带两个看门狗,分别叫做“独立看门狗(IWDG)”以及“窗口看门狗(WWDG)”。这一章节给大家介绍一下窗口看门狗的使用,窗口看门狗相对于独立看门狗的好处在于喂狗的时间精确可控,但是需要使用PCLK1的时钟,而不是独立的时钟源。
20.1 STM32L476 窗口看门狗简介
系统窗口看门狗(WWDG)用于检测软件故障的发生,通常由外部干扰或不可预见的逻辑条件产生,这会导致应用程序放弃其正常序列。
看门狗电路在编程时间段到期时产生MCU复位,除非程序在T6位清零之前刷新向下计数器的内容。如果在向下计数器达到窗口寄存器值之前刷新7位向下计数器值(在控制寄存器中),也会产生MCU复位。 这意味着必须在有限的窗口中刷新计数器。(这一段的意思,就是说窗口看门狗必须在一段时间内喂狗,早了或者晚了都会复位)。
窗口看门狗喂狗时间的计算公式:Twwdg = (T[6:0]+1)/(Pckl1/4096/(2^WDGTB)) (us)
WWDG时钟从APB时钟预分频,并具有可配置的时间窗口,可对其进行编程以检测异常迟到或早期应用行为。
WWDG最适合需要看门狗在精确定时窗口内作出反应的应用(这句话大概可以理解为,如果不需要精确时间去看门狗复位,就建议使用独立看门狗)。
20.2 硬件设计
选择STM32L4内部窗口看门狗,展示使用的是开发板上的LED指示灯D3。
20.3 实验准备
- 使用miniUSB线及10pin排线,通过Jlink仿真器连接PC端和开发板。
- 使用miniUSB线,连接PC与开发板USB接口。
- 使用Keil打开基础实验的实验18-独立看门狗。
- 下载程序,并完成功能测试。
20.4 实验验证
下载完成后,可以看到LED保持常亮,说明我们的程序没有被复位。而当我们注释掉HAL_WWDG_EarlyWakeupCallback()回调函数中的喂狗函数HAL_WWDG_Refresh(hwwdg),重新编译下载,可以看到LED灯周期闪烁,说明程序一直被复位(因为没有喂狗)。
20.5 源码详解
本节中的源码说明,仅针对此例程中的重要功能,详细的源码介绍请大家参照代码后的注释。
20.5.1 stm32l4xx_hal_conf.h
此文件位于“实验16-RTC实时时钟\Inc”路径中,主要用途是选择使能此例程使用到的库文件。
此例程我们主要给大家展示STM32L4的窗口看门狗功能,所以我们宏定义中打开WWDG相关的。
103 // 使能的宏
104 #define HAL_MODULE_ENABLED // 芯片
105 #define HAL_FLASH_MODULE_ENABLED // Flash
106 #define HAL_PWR_MODULE_ENABLED // 电源
107 #define HAL_RCC_MODULE_ENABLED // 时钟
108 #define HAL_CORTEX_MODULE_ENABLED // NVIC
109
110 #define HAL_GPIO_MODULE_ENABLED // GPIO
111 #define HAL_DMA_MODULE_ENABLED // DMA
112 #define HAL_UART_MODULE_ENABLED // UART
113 #define HAL_SPI_MODULE_ENABLED // SPI
114 #define HAL_WWDG_MODULE_ENABLED // 窗口看门狗
20.5.2 main.c
main函数,我们的例程由此处开始执行,首先调用HAL_Init()函数初始化我们的模块,接着调用SystemClock_Config()函数初始化此例程用到的时钟,具体有哪些时钟被初始化,在gyu_util.c部分有详细说明。
接下来我们初始化了LED,设置LED点亮,在LED点亮之前我们延时500ms,用来展示程序是否被窗口看门狗重启。
最后我们初始化窗口看门狗。
32 int main(void)
33 {
34 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
35 // 重置所有外设、flash界面以及系统时钟
36 HAL_Init();
37
38 // 配置系统时钟(包含振荡器、系统时钟、总线时钟等等)
39 SystemClock_Config();
40
41 // 初始化LED引脚
42 LED_Init();
43
44 // 延时500ms,展示软件是否重启(重启则LED闪烁,不重启则LED常亮)
45 HAL_Delay(500);
46 LED_SET(GPIO_PIN_SET);
47
48 // 初始化窗口看门狗
49 MX_WWDG_Init();
50
51 //
52 while (1)
53 {
54
55 }
56 }
20.5.3 gyu_util.c
请参照实验01中的介绍。
基础实验中的其他例程,大部分都是使用的相同的时钟配置函数,有特殊的时钟使用,将会在对应例程的源码详解中做针对性说明。
20.5.4 gyu_iwdg.c
窗口看门狗初始化函数,初始化窗口看门狗时钟8分频(时钟来源PCLK1 80MHz),配置上窗口值0x50,下窗口值默认0x40,并且使能唤醒中断(可以在唤醒中断中喂狗)。
利用窗口看门狗的喂狗时间计算公式,我们分别得到上下窗口值的时间:
上窗口(0x50):33.17ms
下窗口(0x40):26.21ms
46 void MX_WWDG_Init(void)
47 {
48 hwwdg.Instance = WWDG; // 配置为WWDG
49 hwwdg.Init.Prescaler = WWDG_PRESCALER_8; // 8分频,WDGTB = 3
50 hwwdg.Init.Window = 0x50; // 上窗口值为0x50
51 hwwdg.Init.Counter = 0x7F; // 计数CNT为0x7F(最大值)
52 hwwdg.Init.EWIMode = WWDG_EWI_ENABLE; // 使能唤醒中断
53 if (HAL_WWDG_Init(&hwwdg) != HAL_OK) // 初始化WWDG,出错则进入错误处理函数
54 {
55 _Error_Handler(__FILE__, __LINE__);
56 }
57 }
窗口看门狗初始化,使能窗口看门狗时钟,使能中断。
67 void HAL_WWDG_MspInit(WWDG_HandleTypeDef *hwwdg)
68 {
69 __HAL_RCC_WWDG_CLK_ENABLE(); //使能窗口看门狗时钟
70
71 HAL_NVIC_SetPriority(WWDG_IRQn, 10, 0); //抢占优先级10,子优先级为0
72 HAL_NVIC_EnableIRQ(WWDG_IRQn); //使能窗口看门狗中断
73 }
窗口看门狗唤醒中断回调函数,我们可以在这个函数中去调用喂狗的函数。
83 void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef* hwwdg)
84 {
85 HAL_WWDG_Refresh(hwwdg); // 喂狗
86 }
21 实验20-外部Flash
窗口看门狗实验,STM32L4内部自带两个看门狗,分别叫做“独立看门狗(IWDG)”以及“窗口看门狗(WWDG)”。这一章节给大家介绍一下窗口看门狗的使用,窗口看门狗相对于独立看门狗的好处在于喂狗的时间精确可控,但是需要使用PCLK1的时钟,而不是独立的时钟源。
21.1 STM32L476 SPI简介
SPI接口可用于使用SPI协议与外部设备通信。SPI模式可由软件选择。 器件复位后,默认选择SPI Motorola模式。
串行外设接口(SPI)协议支持与外部设备的半双工,全双工和单工同步串行通信。 接口可以配置为主设备,在这种情况下,它为外部从设备提供通信时钟(SCK)。 该接口还能够在多主机配置中运行。
21.2 外部Flash W25Q80简介
21.3 硬件设计
选择STM32L4 SPI1引脚(PA5、PA6、PA7),选择PB2作为flash芯片W25Q80的CSN片选引脚。
21.4 实验准备
- 使用miniUSB线及10pin排线,通过Jlink仿真器连接PC端和开发板。
- 使用miniUSB线,连接PC与开发板USB接口。
- 使用Keil打开基础实验的实验18-独立看门狗。
- 下载程序,并完成功能测试。
21.5 实验验证
下载完成后,可以看到LED保持常亮,说明我们的程序没有被复位。而当我们注释掉HAL_WWDG_EarlyWakeupCallback()回调函数中的喂狗函数HAL_WWDG_Refresh(hwwdg),重新编译下载,可以看到LED灯周期闪烁,说明程序一直被复位(因为没有喂狗)。
21.6 源码详解
本节中的源码说明,仅针对此例程中的重要功能,详细的源码介绍请大家参照代码后的注释。
21.6.1 stm32l4xx_hal_conf.h
此文件位于“实验16-RTC实时时钟\Inc”路径中,主要用途是选择使能此例程使用到的库文件。
此例程我们主要给大家展示STM32L4的窗口看门狗功能,所以我们宏定义中打开WWDG相关的。
103 // 使能的宏
104 #define HAL_MODULE_ENABLED // 芯片
105 #define HAL_FLASH_MODULE_ENABLED // Flash
106 #define HAL_PWR_MODULE_ENABLED // 电源
107 #define HAL_RCC_MODULE_ENABLED // 时钟
108 #define HAL_CORTEX_MODULE_ENABLED // NVIC
109
110 #define HAL_GPIO_MODULE_ENABLED // GPIO
111 #define HAL_DMA_MODULE_ENABLED // DMA
112 #define HAL_UART_MODULE_ENABLED // UART
113 #define HAL_SPI_MODULE_ENABLED // SPI
114 #define HAL_WWDG_MODULE_ENABLED // 窗口看门狗
21.6.2 main.c
main函数,我们的例程由此处开始执行,首先调用HAL_Init()函数初始化我们的模块,接着调用SystemClock_Config()函数初始化此例程用到的时钟,具体有哪些时钟被初始化,在gyu_util.c部分有详细说明。
接下来我们初始化了LED,设置LED点亮,在LED点亮之前我们延时500ms,用来展示程序是否被窗口看门狗重启。
最后我们初始化窗口看门狗。
32 int main(void)
33 {
34 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
35 // 重置所有外设、flash界面以及系统时钟
36 HAL_Init();
37
38 // 配置系统时钟(包含振荡器、系统时钟、总线时钟等等)
39 SystemClock_Config();
40
41 // 初始化LED引脚
42 LED_Init();
43
44 // 延时500ms,展示软件是否重启(重启则LED闪烁,不重启则LED常亮)
45 HAL_Delay(500);
46 LED_SET(GPIO_PIN_SET);
47
48 // 初始化窗口看门狗
49 MX_WWDG_Init();
50
51 //
52 while (1)
53 {
54
55 }
56 }
21.6.3 gyu_util.c
请参照实验01中的介绍。
基础实验中的其他例程,大部分都是使用的相同的时钟配置函数,有特殊的时钟使用,将会在对应例程的源码详解中做针对性说明。
21.6.4 gyu_iwdg.c
窗口看门狗初始化函数,初始化窗口看门狗时钟8分频(时钟来源PCLK1 80MHz),配置上窗口值0x50,下窗口值默认0x40,并且使能唤醒中断(可以在唤醒中断中喂狗)。
利用窗口看门狗的喂狗时间计算公式,我们分别得到上下窗口值的时间:
上窗口(0x50):33.17ms
下窗口(0x40):26.21ms
46 void MX_WWDG_Init(void)
47 {
48 hwwdg.Instance = WWDG; // 配置为WWDG
49 hwwdg.Init.Prescaler = WWDG_PRESCALER_8; // 8分频,WDGTB = 3
50 hwwdg.Init.Window = 0x50; // 上窗口值为0x50
51 hwwdg.Init.Counter = 0x7F; // 计数CNT为0x7F(最大值)
52 hwwdg.Init.EWIMode = WWDG_EWI_ENABLE; // 使能唤醒中断
53 if (HAL_WWDG_Init(&hwwdg) != HAL_OK) // 初始化WWDG,出错则进入错误处理函数
54 {
55 _Error_Handler(__FILE__, __LINE__);
56 }
57 }
窗口看门狗初始化,使能窗口看门狗时钟,使能中断。
67 void HAL_WWDG_MspInit(WWDG_HandleTypeDef *hwwdg)
68 {
69 __HAL_RCC_WWDG_CLK_ENABLE(); //使能窗口看门狗时钟
70
71 HAL_NVIC_SetPriority(WWDG_IRQn, 10, 0); //抢占优先级10,子优先级为0
72 HAL_NVIC_EnableIRQ(WWDG_IRQn); //使能窗口看门狗中断
73 }
窗口看门狗唤醒中断回调函数,我们可以在这个函数中去调用喂狗的函数。
83 void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef* hwwdg)
84 {
85 HAL_WWDG_Refresh(hwwdg); // 喂狗
86 }