项目概述 5 i% J/ Q! u4 D+ N" \; ?$ K' b3 K
8 d. M0 x- v6 W0 H# f9 o/ h% v
ST公司从2012年推出入门级低成本Cortex-M0内核的STM32F0到2018年推出新一代入门级低成本Cortex-M0+主流的STM32G0,具备了高性能、更低功耗、方便使用的特点,并大大提高了MCU的性价比。本次实验内容是基于NUCLEO-G071RB板的PWM调光实验。 c7 a7 g, `% F+ C
- x/ J, L! F3 K
( v$ f e( [- Y3 V; v* k5 ^
. h' C# i( k5 \3 }
硬件设计 3 L+ L% v4 s2 Z
NUCLEO-G071RB板照片如下。
2 U5 A* [- e9 j
M0 J& `" ^( u: K, k
0 L$ e. Y8 T6 |
; ?" R7 B- B, G4 ^% m5 m9 O$ P. }' L
NUCLEO-G071RB板具有如下特性 6 u* \$ T! e$ _
• 采用LQFP64封装的STM32微控制器 • 1个LED,并外接Arduino接口 • 1个复位按钮 • 32.768 kHz LSE晶体振荡器 • 扩展接口: – Arduino Uno V3 – ST morpho扩展引脚接口,用于访问所有STM32 I / O口 • 灵活的电源选项: – ST-LINK USB、 VBUS或外部电源(3.3 V,5 V,7 V至12 V) • 具有USB重新枚举功能的板载ST-LINK / V2-1调试器和编程器。 • USB支持三种不同的接口: – 大容量储存 – 虚拟COM端口 – 调试端口 • Flash memory 的大小为128 Kbytes • 支持多种集成开发环境(IDE),包括IAR™,Keil®,基于GCC的IDE
* w/ v8 ?: n! R5 s8 C4 i
上手操作: STM32 NUCLEO-G071RB板是一种低成本且易于使用的开发套件,可快速评估并开始采用QFP64封装的STM32微控制器进行开发。操作步骤如下: - 检查板上的跳线位置,连接CN4(STLK)的[1-2]、[3-4],JP2(PWR)的[1-2]、JP3(IDD)。
- 为了正确识别主机PC上的所有设备接口,请在连接电路板之前安装www.st.com/stm32nucleo网页上的Nucleo USB驱动程序。
- 通过连接器CN2用USB电缆“ A型转Micro B”连接到PC,为电路板供电。这样会看见绿色LED LD3(PWR)点亮,LD1(COM)闪烁。
- 按下用户按钮B1(蓝色)。
- 单击按钮B1,观察三个绿色LED LD4的闪烁频率是否改变。1 ]& [0 B/ Y- H Y7 g
* p4 J4 |+ h( H
系统要求: • Windows® OS (7, 8 and 10), Linux or macOS • USB Type-A to Micro-B 导线
- \# I- M* n' t, d
支持的开发工具: • Keil: MDK-ARM • IAR: EWARM • GCC-based IDE工具 7 C" @: S) x' Z3 H
下图说明了STM32及其外围设备(STLINK / V2-1,按钮,LED和morpho连接器)之间的连接。 [img][/img] 8 T; y) _6 V) a P+ k1 Q
默认跳线设置 - y/ `$ a+ z" ]7 d3 _. t
, v5 w2 G5 f* G0 e
ST-LINK跳线设置
- k5 j" l6 D5 }
5 W$ E2 w G- b4 P+ U" p
使用ST-LINK/V2-1对板载STM32编程和调试
/ s- D; C; Q/ @. K
要在板上编程STM32,只需插入CN4上的两个跳线,如下图所示的红色指示,但不要使用CN11连接器,因为这可能会干扰与NUCLEO的STM32微控制器的通信。 [img][/img] ; g) O) P q+ b
使用ST-LINK/V2-1对外部STM32编程和调试
% Y! T: P+ H/ N+ d6 M
使用ST-LINK / V2-1在外部应用上对STM32进行编程非常容易。只需从CN4卸下2个跳线,如下图所示,然后根据表6将您的应用程序连接到CN11调试连接器。 - ~) ~) c+ ?- G3 X8 Z
如果在外部应用中使用CN11引脚5,则SB19必须为OFF。 - \7 |: n0 j% L& R; H* o4 f
Debug 连接器 CN11 (SWD) [img][/img]
! i1 b6 _: @+ |9 l6 I
供电和供电选择 本实验采用USB STLK供电。 [img][/img] [img][/img] 7 y' q! m! N" O/ n
外接电源输出
) h8 [0 Y/ v9 g+ g8 j; \
• 5V:当NUCLEO-G071RB板由USB,VIN或E5V供电时,5V(CN6引脚5或CN7引脚18)可用作Arduino屏蔽板或扩展板的输出电源。在这种情况下,最大允许电流如表所示。
) y1 o4 j7 n- W: W3 c E
• 3.3V:在CN6引脚4或CN7引脚16上可用作电源输出。电流受调节器U6(意法半导体的LDL112PV33R)的最大电流能力限制。在这种情况下,STM32 NUCLEO-G071RB板和连接的屏蔽的最大功耗必须小于500 mA。 4 g G- ?' Z% t
NUCLEO-G071RB板载跳线默认设置 [img][/img]
: p+ ~' H0 G3 K
焊桥配置和设置 [img][/img] [img][/img]* C; J1 B1 l, u: n% v
: x2 V; s. n/ P
ST morpho连接器CN7和CN10 6 m9 f7 U+ y& v @1 U3 | |
ST morpho连接器CN7和CN10是可在STM32 NUCLEO-G071RB板使用两侧的公头插头(参见下图)。ST morpho连接器上提供了除STM32的VDD_CORE 1.2 V以外的所有信号和电源引脚。 [img][/img]
& f. q/ T4 }! H8 C
NUCLEO-G071RB ST morpho连接器引脚排列9 F6 I1 s- g1 f. x6 B6 ?
[img][/img]
) ] k, i* X+ {$ b5 p8 K
' A. _8 m8 e1 D
' y4 Y8 ]" K; m# k$ ~$ L7 ?
定时器TIM1的PWM实验 8 |0 ]: y; F0 Q
高级控制定时器(TIM1)包括一个16位自动重载计数器,该计数器由可编程预分频器驱动。它可用于多种用途,包括测量输入信号的脉冲长度(输入捕获)或生成输出波形(输出比较,PWM,具有死区插入功能的互补PWM)。使用定时器预分频器和RCC时钟控制器预分频器,可以将脉冲长度和波形周期从几微秒调节到几毫秒。高级控制(TIM1)和通用(TIMy)定时器是完全独立的,并且不共享任何资源。 8 F5 ]7 F {% t" u7 o6 ^
预分频器允许将计数器时钟频率除以1(在运行中)或在65536之间进行任意分频。 • 多达6个独立频道,用于: 输入捕获(但通道5和6) 输出比较 PWM生成(边沿和中心对齐模式) 单一脉冲模式输出 • 具有可编程死区时间的互补输出 • 同步电路,用于通过外部信号控制计时器,并将多个计时器互连在一起。 • 重复计数器仅在给定的循环次数后才更新定时器寄存器。 • 2个中断输入,将计时器的输出信号置于安全的用户可选配置中。 • 发生以下事件时产生中断/ DMA: 更新:计数器上溢/下溢,计数器初始化(通过软件或内部/外部触发) 触发事件(计数器开始,停止,初始化或通过内部/外部触发进行计数) 输入捕捉 输出比较 • 支持增量(正交)编码器和霍尔传感器电路,用于定位 • 触发输入,用于外部时钟或逐周期电流管理
! i3 q% d6 P' O2 g5 o+ z
将以下引脚连接到三色LED上,以监测不同的颜色变化,管脚定义如下:
6 e8 J! u; T7 W6 h6 E, s( [
[img][/img] / d$ i2 y! Y# R4 `5 D! U$ w
三色LED
: [3 @4 j" f# e5 I) M! q. M3 @ 5 U( x" G% c% \8 o) p0 _) r
共阳极4个引脚的三色发光二极管如下图所示。四个管教中最长的公共极接+3V3(CN7的16脚)。其余3个引脚分别与3个1k欧姆电阻串接后,再与PWM的三个输出端连接。三种颜色(蓝、绿、红)如下图。 [img][/img] [img][/img] , M4 ?9 W+ g0 j! ]% S- \( u/ w
6 t2 s, {0 F. T) I
. o/ C4 X" _' O; x B
9 Z0 x; Z& _4 V8 A e; ]
软件调试
, |; }& B; o/ [2 d8 N) l1 H
在本实验中,SystemCoreClock设置为56兆赫。TIM1输入时钟(TIM1CLK)被设置为APB1时钟(PCLK1),因为APB1预分频器等于1。
- [& A; q4 H; _' t. S( b* i [img][/img] * u8 l, ~$ i: ^; r6 |9 F2 T
PWM脉冲宽度调制模式允许生成具有由TIM1_ARR寄存器的值确定的频率和由TIM1_CCRx寄存器的值确定的占空比的信号。
% Q2 u. a" h7 g+ p) e' B
通过在TIM1_CCMRx寄存器的OCxM位中写入“ 0110”(PWM模式1)或“ 0111”(PWM模式2),可以在每个通道上独立选择PWM模式(每个OCx输出一个PWM)。相应的预加载寄存器必须通过设置。
9 O P+ g" g$ t! t
TIM1_CCMRx寄存器中的OCxPE位,以及通过将TIM1_CRx寄存器中的ARPE位置1,最终自动重载预加载寄存器(在递增计数或中心对齐模式下)。
d o/ D2 \9 p4 Q( T
由于仅在发生更新事件时才将预加载寄存器转移到影子寄存器,因此在启动计数器之前,必须通过将TIM1_EGR寄存器中的UG位置1来初始化所有寄存器。
( w: Y! ]( L, [
OCx极性可通过TIM1_CCER寄存器中的CCxP位进行软件编程。可以将其编程为高电平有效或低电平有效。OCx输出通过CCxE,CCxNE,MOE,OSSI和OSSR位(TIM1_CCER和TIM1_BDTR寄存器)的组合来使能。 + A* g5 _3 f& |- v1 w8 d$ T
有关更多详细信息,请参见TIM1_CCER寄存器说明。 & l& |: o7 F7 B' Z* A. [) j
在PWM模式(1或2)下,始终比较TIM1_CNT和TIM1_CCR1以确定TIM1_CCR1≤TIM1_CNT还是TIM1_CNT≤TIM1_CCR1(取决于计数器的方向)。 根据TIM1_CR1寄存器中的CMS位,定时器能够在边沿对齐模式或中心对齐模式下生成PWM。 4 [/ P6 x2 L3 b3 ^0 a* U6 F
TIM1捕获/比较模式寄存器(TIM1_CCMR1)和 (TIM1_CCMR2) 说明如下图 [img][/img] 用于输出比较模式时, 通道的方向通过配置相应的CCxS位来定义。该寄存器的所有其他位对于输入捕捉和输出比较模式具有不同的功能。
: P+ Z N( F. B( M( k% g
TIM1捕获/比较使能寄存器 (TIM1_CCER)
TIM1_CCER寄存器控制各个通道的输出。
& S6 c' {3 ~ ?$ P1 y4 [/ @
TIM1捕获/比较寄存器1 (TIM1_CCR1)、(TIM1_CCR2)、(TIM1_CCR3)
, u- m" k) i, I
TIM1 自动装载寄存器 (TIM1_ARR)
' x/ m- S( x2 q4 e/ Z
TIM1 控制寄存器 (TIM1_CR1)、(TIM1_CR2)
. u+ x1 m; g. K
TIM1事件生成寄存器(TIM1_EGR)
6 T4 n( v( m, e/ @
TIM1间隔和死区时间寄存器(TIM1_BDTR)
0 N% X, |# I' E; y" D0 G1 R
TIM1初始化配置 - . G# u( b3 y3 D
- TIM_MasterConfigTypeDef sMasterConfig = {0};8 \$ ?+ ?* C9 p l
- TIM_OC_InitTypeDef sConfigOC = {0};2 |2 M2 n R5 o5 D
- TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
. h: n- s2 L5 J) \* N' l/ F0 i - $ ~. Y' g, U; R2 E% ^
- /* USER CODE BEGIN TIM1_Init 1 */
( V: Z/ C Y3 L' L* u3 y3 p - 0 o. O" n$ l! j o$ X1 R
- /* USER CODE END TIM1_Init 1 */0 y% D: Y- a* a* v+ X
- htim1.Instance = TIM1;$ ~8 k& Z2 C- _
- htim1.Init.Prescaler = PRESCALER_VALUE;0 x+ K0 `$ \8 W
- htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
' q7 a* U. W) `! n4 N0 z - htim1.Init.Period = PERIOD_VALUE;! u$ a+ @3 z1 `; c) T3 ]% z' a
- htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
z* `! ^- k5 Q; G( F - htim1.Init.RepetitionCounter = 0;
0 b' v I# l) N* N - htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
/ u, n) k+ C* t+ q# d% b - if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)8 x n8 N1 M1 @
- {! r# P# R4 n" }, m. `1 W1 b# P
- Error_Handler();
: ^3 c4 S# Y" \, E% a - }5 J& }: C) N) ~" r/ A7 y4 E+ c
- sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
% F4 c- L/ e3 e' c, u6 T8 |, g - sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
( n) M3 F: {8 T! _3 O" X - sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
& F2 P. X; G, v5 p9 U7 N - if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
- h7 g9 f) K) K9 j. Z - {7 S" _6 O! A f$ L. K
- Error_Handler();2 o7 g* C$ D* x/ F, \5 a1 p
- }
3 D2 R7 Q4 \; ?( S1 ?: w' p1 I - sConfigOC.OCMode = TIM_OCMODE_PWM1;
, g, m: v4 ^* K* N: j( K - sConfigOC.Pulse = PULSE1_VALUE;2 ?- o; K/ W: ?; _ w- y. i
- sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
) j* H F2 W9 ]; r( c7 F6 | - sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;+ p; t7 F% h+ w, Z' p+ f
- sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
5 U6 l7 n1 k7 h% ^$ \ - sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
9 y2 c( w; X. L# [5 o% `4 W9 M( ^. p - sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;1 g4 J, E8 w L. I* r" |! Z
- if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)) {% S# x" t5 f" t
- {" W+ T! _3 @* O$ r/ K) S: q
- Error_Handler();
& m1 f) m2 @5 G& y2 L# T8 v% e - }
" o$ s/ O" n% i! [ - sConfigOC.Pulse = PULSE2_VALUE;/ n4 P0 r: e* A& ?
- if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
9 E) l" S6 R9 @) Z8 k, w$ o2 s - {; b/ @6 r% U/ {$ V
- Error_Handler();1 E6 h4 u) ]- Q1 k' F3 N2 O
- }' L7 u. n2 q$ d d3 V# @
- sConfigOC.Pulse = PULSE3_VALUE;( s' d8 Q9 V4 E7 e
- if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)6 J9 A9 X. W, M
- {3 \0 t9 d! v* m5 c" a: l9 {
- Error_Handler();
4 i, B6 e' B' j/ H8 [ - }" M) \+ \4 p( \. l
- sConfigOC.Pulse = PULSE4_VALUE;
1 }: u2 n8 Q7 P$ Y
( A1 |2 C- P7 @- /* if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)- o U- G) G& Q/ i4 {0 j! l0 {
- {
( ]# o7 P/ M! o- ^6 s' g$ t! j - Error_Handler();3 S4 X2 q! e! D4 n% z# J; d) @
- }
% m- d% n, ]! d- j6 }# d - */
) Q7 R$ a. V% A3 ?* l2 v - sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;6 h9 p E# H5 G! ~' e2 {7 E1 R" Q
- sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;0 N6 r9 {# Y* I
- sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;' _' p/ A" A8 G1 ?2 A
- sBreakDeadTimeConfig.DeadTime = 0;0 V1 q$ K! F) K; V$ Y
- sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
$ y/ Z T' m% F' ]" o( Q9 ~ - sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
0 w \8 H+ l* e: R1 {) b( [ - sBreakDeadTimeConfig.BreakFilter = 0;
8 a+ N. }5 L# o. a - sBreakDeadTimeConfig.BreakAFMode = TIM_BREAK_AFMODE_INPUT;2 h K K* n( F) N: W) G8 h3 x
- sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE;
0 Q& T8 ]5 p, {8 e+ ~4 d2 @ - sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH;
9 e. _# o" N; [ }8 C, C - sBreakDeadTimeConfig.Break2Filter = 0;. D8 [( r/ c( ~7 N6 L
- sBreakDeadTimeConfig.Break2AFMode = TIM_BREAK_AFMODE_INPUT;9 F& l, U4 v8 i3 {2 e! |
- sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;1 q! S! ~% O" ^
- if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)0 a4 n/ q* y, T% f1 w" h( f* g
- {( x9 s) Z4 [$ v
- Error_Handler();
$ R1 A* \5 |( D) L3 r - }
3 ^2 {: f' u5 s' d- W9 A6 U - /* USER CODE BEGIN TIM1_Init 2 */7 l5 N) r! i% \4 Q
- ! H$ m$ }5 P5 c; R
- /* USER CODE END TIM1_Init 2 */' s, }9 H% q+ s7 \
- HAL_TIM_MspPostInit(&htim1);
复制代码
# |8 M$ d: x6 |4 o+ L$ j( c1 o& p: a5 A
|
, O; x; K* {* g& i