流水灯、存储器、外部中断实验

计算机原理与应用实验-流水灯、存储器、外部中断实验

1 实验一 流水灯实验

1.1 实验目的

  1. 掌握ARM开发工具的使用。

  2. 掌握基本IO的使用。

1.2 实验原理及内容

  1. 电路结构图

实现流水灯的电路结构图如图1所示。以两条红色虚线为界,从左至右第一部分为ARM系统部分,第三部分为外围电路,第二部分是接口部分,需要自己将其连接。

图 1 流水灯的电路结构图

接线方式为:GPIOF_0~GPIOF_7(P12接口)接LED1~LED8(P2接口)。

  1. LED电路原理

LED灯的驱动原理如图2所示。当发光二极管正向导通时,LED灯点亮。

图 2 LED灯正向导通

如图3所示,LED灯与MCU引脚连接,MCU IO额定电流为25mA,例如0603封装红色LED灯额定电流为20mA,已经接近MCU IO的额定电流,可能会损毁器件。因此图1.3微控制器驱动LED灯方案不可取。

图 3 微控制器驱动 LED 灯

实际微控制驱动LED灯的电路模型包括:控制器、驱动器和执行器三部分,控制器提供控制信号,再由驱动器驱动执行器,如图4所示。

图 4 LED 灯去驱动电路模型

如图5所示LED灯通过N沟道MOS管驱动,MCU IO输出高电平时MOS管漏极和源极导通LED灯被点亮,反之MCU输出低电平时MOS管漏极和源极截止,LED灯不能被点亮。假设VCC电压3.3V,MOS管导通阻值为零,通过LED灯的电流将远超额定电流出现短路现象,故此方案不可取。

图 5 MOS 管驱动 LED 灯电路

如图6所示,LED驱动电路中增加电阻(R),以此保证通过LED灯的电流不超过额定电流,避免损坏器件,故将电阻R称为限流电阻。

图 6 限流电阻

如图7所示LED电路原理图,R1位置电阻作为限流电阻,R29位置电阻作为下拉电阻,避免MOS管栅极出现亚稳态。

图 7 LED 电路原理图
  1. 微控制器IO输出控制原理

如图8所示,基本结构针对STM32F407有7组IO。分别为GPIOA~GPIOG,每组IO有16个IO口,则有112个IO口。

图 8 I/O 端口位控制基本结构

STM32F4的GPIO工作方式有8种,其中4种输入,4种输出,分别为:输入浮空、输入上拉、输入下拉、模拟模式、开漏输出、开漏复用输出、推挽输出、推挽复用输出。

(i)输入方式

浮空即电路既不上拉也不下拉,直接通过施密特触发器送到输入数据寄存器再送入到CPU;上拉或下拉方式间接通过上拉或下拉电阻后再通过施密特触发器送到输入数据寄存器最后送入到CPU;模拟方式是关闭施密特触发器后信号直接通过模拟通道至片上外设。

(ii)输出方式

开漏输出指CPU直接或间接地把数据输出到数据寄存器,然后通过输出控制电路来决定最后的输出信号电平。当信号为1时,N—MOS管关闭,IO电平受上下拉电路的控制;当信号为0时,N—MOS管导通,输出下拉低电平。推挽输出方式时,当信号为1时,P-MOS管导通,N-MOS管截止,输出上拉高电平;当信号为0时,P-MOS管截止,N-MOS管导通,输出下拉低电平。开漏复用和推挽复用方式与开漏和推挽的区别是信号来源不同。开漏复用和推挽复用的信号来源是片上的外设模块。

(iii)IO的相关寄存器

每个通用的IO端口都包含4个32位的配置寄存器(GPIOx_MODER、GPIOx_OTYPER、PIOx_OSPEEDR和GPIOx_PUPDR)。2个32位的数据寄存器(GPIOx_IDR和GPIOx_ODR),1个32位置位/复位寄存器(GPIOx_BSRR),1个32位锁定寄存器(GPIOx_LCKR)和2个32位复用功能选择寄存器(GPIOx_AFRL)。

(iv)工作方式配置

端口方式配置寄存器(GPIOx_MODER)用来配置端口的输入、输出、复用和模拟方式;端口输出方式配置寄存器(GPIOx_OTYPER)用来配置端口输出为推挽还是开漏;端口速度配置寄存器(PIOx_OSPEEDR)用来配置端口的信息传输速率;端口上下拉配置寄存器(GPIOx_PUPDR)用来配置端口的无上下拉、上拉、下拉和保留方式。

(v)数据寄存器配置

端口输入数据寄存器(GPIOx_IDR)用到低16位,分别对应该组IO口的一个电平状态。端口输出数据寄存器(GPIOx_ODR)用到低16位,分别对应该组IO口的一个电平状态。置位和复位寄存器(GPIOx_BSRR)与前两个不同的,置位和复位寄存器用到了32位。低16为设置为1时,用于置1对应位。高16位设置为1时,用于置0对应位。而低16位和高16位设置为0时不影响原值。

1.3 完整的硬件接线图和软件程序流程图

  1. 硬件接线图

实现流水灯的电路结构图如图1所示。以两条红色虚线为界,从左至右第一部分为ARM系统部分,第三部分为外围电路,第二部分是接口部分,需要自己将其连接。

图 9 流水灯的硬件接线图

接线方式为:GPIOF_0~GPIOF_7(P12接口)接LED1~LED8(P2接口)。

  1. 软件程序流程图

实现流水灯的软件流程图如图10所示。

图 10 流水灯的软件流程图

1.4 流水灯源程序代码

  1. main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/************************************************************* 
实验名称:LED流水灯

硬件模块:计算机原理应用试验箱

硬件接线:P12~P2

实验现象:通过数据移位操作,LED1~LED8实现流水灯效果
**************************************************************/
#include "stm32f4xx.h"
#include "delay.h"

int main(void)
{
uint8_t cont = 128;
uint16_t led_data = 0;
GPIO_InitTypeDef GPIO_InitStructure;

// 开启GPIO时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;

// 输出模式
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
// 设置推挽输出
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
// 速率25MHZ
GPIO_InitStructure.GPIO_Speed = GPIO_Medium_Speed;
GPIO_Init(GPIOF, &GPIO_InitStructure);


GPIO_Write(GPIOF, 0x00FF);

// 初始化延时
Delay_Init();

while(1)
{
led_data = 128<<cont;
GPIO_Write(GPIOF, led_data);
Delay_Ms(100);
cont++;
// 大于7后清零
cont &= 0x07;
}
}

1.5 流水灯实验结果

流水灯实验结果如图11所示,实验现象表示为LED1~LED8依次被点亮。

图 11 流水灯实验结果

2 实验二 双端口存储器实验

2.1 实验目的

(1)熟练掌握GPIO输入输出操作。

(2)掌握存储器原理及应用。

(3)学会根据时序图编写程序。

2.2 实验原理及内容

(1)双端口存储器简介

双端口存储器是指同一个存储器具有两组相互独立的读写控制线路,进行并行的独立操作,是一种高速工作的存储器。

  1. 双端口存储器

双端口存储器的结构图如图12所示。其中:

clock:系统时钟50Mhz,用于提供RAM读写数据;

wraddress:写数据地址,将数据写入该地址的RAM存储区内;

data:写入的数据0~7;

wren:写使能高电平有效;

rdadress:读数据地址,将内存中的数据从RAM存储区读出;

图 12 双端口存储器结构图

此外,数据写入写出时的波形分别如图13和图14所示。

图 13 数据写入时波形

图 14 数据读取时波形

2.3 完整的硬件接线图和程序流程图

(1)硬件接线图

完整的硬件接线图如图15所示。其中P2接口与P10接口相连,P1接口与P11接口相连,P12接口与P13接口相连。

图 15 存储器硬件接线图

(2)软件程序流程图

Quartus中的程序流程图如图16所示,Keil中的软件流程图如图17所示。

图 16 Quartus中的程序流程图

图 17 Keil中的软件流程图

2.4 存储器源代码程序

(1)double_ram.v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1.	module double_ram(  
2. clock,
3. data,
4. rdaddress,
5. wraddress,
6. wren,
7. q
8. ); // 定义参数
9. input clock;
10. input [3:0]data;
11. input [2:0]rdaddress;
12. input [2:0]wraddress;
13. input wren;
14. output [3:0]q;
15.
16. wire [3:0]n_rdaddress = {1'b0,rdaddress};
17. wire [3:0]n_wraddress = {1'b0,wraddress};
18.
19. // 调用模块
20. ram2 ram2
21. (
22. .clock(clock),
23. .data(data),
24. .rdaddress(n_rdaddress),
25. .wraddress(n_wraddress),
26. .wren(wren),
27. .q(q)
28. );
29. endmodule

(2)ram.2v

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
1.	// megafunction wizard: %RAM: 2-PORT%  
2. // GENERATION: STANDARD
3. // VERSION: WM1.0
4. // MODULE: altsyncram
5.
6. // ============================================================
7. // File Name: ram2.v
8. // Megafunction Name(s):
9. // altsyncram
10. //
11. // Simulation Library Files(s):
12. // altera_mf
13. // ============================================================
14. // ************************************************************
15. // THIS IS A WIZARD-GENERATED FILE. DO NOT EDIT THIS FILE!
16. //
17. // 13.1.0 Build 162 10/23/2013 SJ Full Version
18. // ************************************************************
19.
20.
21. //Copyright (C) 1991-2013 Altera Corporation
22. //Your use of Altera Corporation's design tools, logic functions
23. //and other software and tools, and its AMPP partner logic
24. //functions, and any output files from any of the foregoing
25. //(including device programming or simulation files), and any
26. //associated documentation or information are expressly subject
27. //to the terms and conditions of the Altera Program License
28. //Subscription Agreement, Altera MegaCore Function License
29. //Agreement, or other applicable license agreement, including,
30. //without limitation, that your use is for the sole purpose of
31. //programming logic devices manufactured by Altera and sold by
32. //Altera or its authorized distributors. Please refer to the
33. //applicable agreement for further details.
34.
35.
36. // synopsys translate_off
37. `timescale 1 ps / 1 ps
38. // synopsys translate_on
39. module ram2 (
40. clock,
41. data,
42. rdaddress,
43. wraddress,
44. wren,
45. q);
46.
47. input clock;
48. input [3:0] data;
49. input [3:0] rdaddress;
50. input [3:0] wraddress;
51. input wren;
52. output [3:0] q;
53. `ifndef ALTERA_RESERVED_QIS
54. // synopsys translate_off
55. `endif
56. tri1 clock;
57. tri0 wren;
58. `ifndef ALTERA_RESERVED_QIS
59. // synopsys translate_on
60. `endif
61.
62. wire [3:0] sub_wire0;
63. wire [3:0] q = sub_wire0[3:0];
64.
65. altsyncram altsyncram_component (
66. .address_a (wraddress),
67. .clock0 (clock),
68. .data_a (data),
69. .wren_a (wren),
70. .address_b (rdaddress),
71. .q_b (sub_wire0),
72. .aclr0 (1'b0),
73. .aclr1 (1'b0),
74. .addressstall_a (1'b0),
75. .addressstall_b (1'b0),
76. .byteena_a (1'b1),
77. .byteena_b (1'b1),
78. .clock1 (1'b1),
79. .clocken0 (1'b1),
80. .clocken1 (1'b1),
81. .clocken2 (1'b1),
82. .clocken3 (1'b1),
83. .data_b ({4{1'b1}}),
84. .eccstatus (),
85. .q_a (),
86. .rden_a (1'b1),
87. .rden_b (1'b1),
88. .wren_b (1'b0));
89. defparam
90. altsyncram_component.address_aclr_b = "NONE",
91. altsyncram_component.address_reg_b = "CLOCK0",
92. altsyncram_component.clock_enable_input_a = "BYPASS",
93. altsyncram_component.clock_enable_input_b = "BYPASS",
94. altsyncram_component.clock_enable_output_b = "BYPASS",
95. altsyncram_component.intended_device_family = "Cyclone IV E",
96. altsyncram_component.lpm_type = "altsyncram",
97. altsyncram_component.numwords_a = 16,
98. altsyncram_component.numwords_b = 16,
99. altsyncram_component.operation_mode = "DUAL_PORT",
100. altsyncram_component.outdata_aclr_b = "NONE",
101. altsyncram_component.outdata_reg_b = "CLOCK0",
102. altsyncram_component.power_up_uninitialized = "FALSE",
103. altsyncram_component.read_during_write_mode_mixed_ports = "DONT_CARE",
104. altsyncram_component.widthad_a = 4,
105. altsyncram_component.widthad_b = 4,
106. altsyncram_component.width_a = 4,
107. altsyncram_component.width_b = 4,
108. altsyncram_component.width_byteena_a = 1;
109.
110.
111. endmodule
112.
113. // ============================================================
114. // CNX file retrieval info
115. // ============================================================
116. // Retrieval info: PRIVATE: ADDRESSSTALL_A NUMERIC "0"
117. // Retrieval info: PRIVATE: ADDRESSSTALL_B NUMERIC "0"
118. // Retrieval info: PRIVATE: BYTEENA_ACLR_A NUMERIC "0"
119. // Retrieval info: PRIVATE: BYTEENA_ACLR_B NUMERIC "0"
120. // Retrieval info: PRIVATE: BYTE_ENABLE_A NUMERIC "0"
121. // Retrieval info: PRIVATE: BYTE_ENABLE_B NUMERIC "0"
122. // Retrieval info: PRIVATE: BYTE_SIZE NUMERIC "8"
123. // Retrieval info: PRIVATE: BlankMemory NUMERIC "1"
124. // Retrieval info: PRIVATE: CLOCK_ENABLE_INPUT_A NUMERIC "0"
125. // Retrieval info: PRIVATE: CLOCK_ENABLE_INPUT_B NUMERIC "0"
126. // Retrieval info: PRIVATE: CLOCK_ENABLE_OUTPUT_A NUMERIC "0"
127. // Retrieval info: PRIVATE: CLOCK_ENABLE_OUTPUT_B NUMERIC "0"
128. // Retrieval info: PRIVATE: CLRdata NUMERIC "0"
129. // Retrieval info: PRIVATE: CLRq NUMERIC "0"
130. // Retrieval info: PRIVATE: CLRrdaddress NUMERIC "0"
131. // Retrieval info: PRIVATE: CLRrren NUMERIC "0"
132. // Retrieval info: PRIVATE: CLRwraddress NUMERIC "0"
133. // Retrieval info: PRIVATE: CLRwren NUMERIC "0"
134. // Retrieval info: PRIVATE: Clock NUMERIC "0"
135. // Retrieval info: PRIVATE: Clock_A NUMERIC "0"
136. // Retrieval info: PRIVATE: Clock_B NUMERIC "0"
137. // Retrieval info: PRIVATE: IMPLEMENT_IN_LES NUMERIC "0"
138. // Retrieval info: PRIVATE: INDATA_ACLR_B NUMERIC "0"
139. // Retrieval info: PRIVATE: INDATA_REG_B NUMERIC "0"
140. // Retrieval info: PRIVATE: INIT_FILE_LAYOUT STRING "PORT_B"
141. // Retrieval info: PRIVATE: INIT_TO_SIM_X NUMERIC "0"
142. // Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone IV E"
143. // Retrieval info: PRIVATE: JTAG_ENABLED NUMERIC "0"
144. // Retrieval info: PRIVATE: JTAG_ID STRING "NONE"
145. // Retrieval info: PRIVATE: MAXIMUM_DEPTH NUMERIC "0"
146. // Retrieval info: PRIVATE: MEMSIZE NUMERIC "64"
147. // Retrieval info: PRIVATE: MEM_IN_BITS NUMERIC "0"
148. // Retrieval info: PRIVATE: MIFfilename STRING ""
149. // Retrieval info: PRIVATE: OPERATION_MODE NUMERIC "2"
150. // Retrieval info: PRIVATE: OUTDATA_ACLR_B NUMERIC "0"
151. // Retrieval info: PRIVATE: OUTDATA_REG_B NUMERIC "1"
152. // Retrieval info: PRIVATE: RAM_BLOCK_TYPE NUMERIC "0"
153. // Retrieval info: PRIVATE: READ_DURING_WRITE_MODE_MIXED_PORTS NUMERIC "2"
154. // Retrieval info: PRIVATE: READ_DURING_WRITE_MODE_PORT_A NUMERIC "3"
155. // Retrieval info: PRIVATE: READ_DURING_WRITE_MODE_PORT_B NUMERIC "3"
156. // Retrieval info: PRIVATE: REGdata NUMERIC "1"
157. // Retrieval info: PRIVATE: REGq NUMERIC "1"
158. // Retrieval info: PRIVATE: REGrdaddress NUMERIC "1"
159. // Retrieval info: PRIVATE: REGrren NUMERIC "1"
160. // Retrieval info: PRIVATE: REGwraddress NUMERIC "1"
161. // Retrieval info: PRIVATE: REGwren NUMERIC "1"
162. // Retrieval info: PRIVATE: SYNTH_WRAPPER_GEN_POSTFIX STRING "0"
163. // Retrieval info: PRIVATE: USE_DIFF_CLKEN NUMERIC "0"
164. // Retrieval info: PRIVATE: UseDPRAM NUMERIC "1"
165. // Retrieval info: PRIVATE: VarWidth NUMERIC "0"
166. // Retrieval info: PRIVATE: WIDTH_READ_A NUMERIC "4"
167. // Retrieval info: PRIVATE: WIDTH_READ_B NUMERIC "4"
168. // Retrieval info: PRIVATE: WIDTH_WRITE_A NUMERIC "4"
169. // Retrieval info: PRIVATE: WIDTH_WRITE_B NUMERIC "4"
170. // Retrieval info: PRIVATE: WRADDR_ACLR_B NUMERIC "0"
171. // Retrieval info: PRIVATE: WRADDR_REG_B NUMERIC "0"
172. // Retrieval info: PRIVATE: WRCTRL_ACLR_B NUMERIC "0"
173. // Retrieval info: PRIVATE: enable NUMERIC "0"
174. // Retrieval info: PRIVATE: rden NUMERIC "0"
175. // Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all
176. // Retrieval info: CONSTANT: ADDRESS_ACLR_B STRING "NONE"
177. // Retrieval info: CONSTANT: ADDRESS_REG_B STRING "CLOCK0"
178. // Retrieval info: CONSTANT: CLOCK_ENABLE_INPUT_A STRING "BYPASS"
179. // Retrieval info: CONSTANT: CLOCK_ENABLE_INPUT_B STRING "BYPASS"
180. // Retrieval info: CONSTANT: CLOCK_ENABLE_OUTPUT_B STRING "BYPASS"
181. // Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone IV E"
182. // Retrieval info: CONSTANT: LPM_TYPE STRING "altsyncram"
183. // Retrieval info: CONSTANT: NUMWORDS_A NUMERIC "16"
184. // Retrieval info: CONSTANT: NUMWORDS_B NUMERIC "16"
185. // Retrieval info: CONSTANT: OPERATION_MODE STRING "DUAL_PORT"
186. // Retrieval info: CONSTANT: OUTDATA_ACLR_B STRING "NONE"
187. // Retrieval info: CONSTANT: OUTDATA_REG_B STRING "CLOCK0"
188. // Retrieval info: CONSTANT: POWER_UP_UNINITIALIZED STRING "FALSE"
189. // Retrieval info: CONSTANT: READ_DURING_WRITE_MODE_MIXED_PORTS STRING "DONT_CARE"
190. // Retrieval info: CONSTANT: WIDTHAD_A NUMERIC "4"
191. // Retrieval info: CONSTANT: WIDTHAD_B NUMERIC "4"
192. // Retrieval info: CONSTANT: WIDTH_A NUMERIC "4"
193. // Retrieval info: CONSTANT: WIDTH_B NUMERIC "4"
194. // Retrieval info: CONSTANT: WIDTH_BYTEENA_A NUMERIC "1"
195. // Retrieval info: USED_PORT: clock 0 0 0 0 INPUT VCC "clock"
196. // Retrieval info: USED_PORT: data 0 0 4 0 INPUT NODEFVAL "data[3..0]"
197. // Retrieval info: USED_PORT: q 0 0 4 0 OUTPUT NODEFVAL "q[3..0]"
198. // Retrieval info: USED_PORT: rdaddress 0 0 4 0 INPUT NODEFVAL "rdaddress[3..0]"
199. // Retrieval info: USED_PORT: wraddress 0 0 4 0 INPUT NODEFVAL "wraddress[3..0]"
200. // Retrieval info: USED_PORT: wren 0 0 0 0 INPUT GND "wren"
201. // Retrieval info: CONNECT: @address_a 0 0 4 0 wraddress 0 0 4 0
202. // Retrieval info: CONNECT: @address_b 0 0 4 0 rdaddress 0 0 4 0
203. // Retrieval info: CONNECT: @clock0 0 0 0 0 clock 0 0 0 0
204. // Retrieval info: CONNECT: @data_a 0 0 4 0 data 0 0 4 0
205. // Retrieval info: CONNECT: @wren_a 0 0 0 0 wren 0 0 0 0
206. // Retrieval info: CONNECT: q 0 0 4 0 @q_b 0 0 4 0
207. // Retrieval info: GEN_FILE: TYPE_NORMAL ram2.v TRUE
208. // Retrieval info: GEN_FILE: TYPE_NORMAL ram2.inc FALSE
209. // Retrieval info: GEN_FILE: TYPE_NORMAL ram2.cmp FALSE
210. // Retrieval info: GEN_FILE: TYPE_NORMAL ram2.bsf FALSE
211. // Retrieval info: GEN_FILE: TYPE_NORMAL ram2_inst.v FALSE
212. // Retrieval info: GEN_FILE: TYPE_NORMAL ram2_bb.v TRUE
213. // Retrieval info: LIB_FILE: altera_mf

(3)main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
1.	/******************************************************************* 
2. 实验名称:第2次试验.拨档开关输入实验
3.
4. 硬件模块:计算机原理应用实验箱
5.
6. 硬件接线:P12-P13,P11-P1,P10-P2
7. 实验现象:拨码开关状态映射到LED灯上。
8.
9. key 1 读 Key 2 写
10. ********************************************************************/
11. #include "stm32f4xx.h"
12. #include "delay.h"
13.
14. /*******************************
15. 功 能:LED端口初始化
16. 参 数:无
17. 返回值:无
18. *******************************/
19.
20.
21. #define KEY_READ() GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_8) // 上按键检测
22. #define KEY_WRITE() GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_11) // 下按键检测
23.
24.
25. #define CLK(val) GPIO_WriteBit(GPIOF, GPIO_Pin_6, (BitAction)val) // FPGA 时钟信号
26. #define WREN(val) GPIO_WriteBit(GPIOF, GPIO_Pin_7, (BitAction)val) // FPGA 读写信号
27.
28.
29. /*******************************
30. 功 能:LED灯读写数据显示
31. 参 数:无
32. 返回值:无
33. *******************************/
34.
35. void Led_Init(void)
36. {
37. GPIO_InitTypeDef GPIO_InitStructure;
38.
39. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); //开启GPIOB的时钟
40.
41. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 \
42. | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
43.
44. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //输出模式
45. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出
46. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
47. GPIO_Init(GPIOB, &GPIO_InitStructure);
48. GPIO_Write(GPIOB, 0x0000);
49. }
50.
51. /*******************************
52. 功 能:拨码开关端口初始化
53. 参 数:无
54. 返回值:无
55. *******************************/
56. void Sw_Init(void)
57. {
58. GPIO_InitTypeDef GPIO_InitStructure;
59.
60. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); //开启GPIOC的时钟
61.
62. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3
63. | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_11;
64.
65. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; //输入模式
66. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
67. GPIO_Init(GPIOC, &GPIO_InitStructure);
68. }
69.
70. /*******************************
71. 功 能:GPIOF与FPGA连接初始化
72. 参 数:无
73. 返回值:无
74. *******************************/
75. void GPIO_FPGA_Init()
76. {
77. GPIO_InitTypeDef GPIO_InitStructure;
78.
79. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); //开启GPIOF的时钟
80.
81. GPIO_InitStructure.GPIO_Pin = 0x0fff;
82.
83. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //输出模式
84. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出
85. GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed; //输出速率25MHz
86. GPIO_Init(GPIOF, &GPIO_InitStructure);
87.
88.
89. GPIO_InitStructure.GPIO_Pin = 0xf000;
90. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; //输出模式
91. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
92. GPIO_Init(GPIOF, &GPIO_InitStructure);
93. }
94.
95.
96.
97.
98. /************读取数据***************/
99. void Read_Data(uint8_t read_addr, uint8_t *read_date)
100. {
101. uint16_t buf = 0; // GPIOF 数据变量
102. WREN(0); // 读数据
103. CLK(0); // 时钟信号
104. buf = GPIO_ReadOutputData(GPIOF) & 0x00C0; // 保留第6、7位,即时钟信号和读写信号不变
105. buf |= read_addr; // 将读取地址放入GPIOF, 读地址 0-2位
106. GPIO_Write(GPIOF, buf); // GPIOF输出数据
107. CLK(1); // 时钟周期发送读数据地址
108. CLK(0);
109. CLK(1); // 时钟信号获取内存数据
110. *read_date = ((GPIO_ReadInputData(GPIOF) & 0xf000) >> 12 );// 获得FPGA返回数据q, 数据pin在12-15位
111. WREN(0);
112. }
113.
114. /***********写入数据***************/
115. void Write_Data(uint8_t write_addr, uint8_t write_date)
116. {
117. uint16_t buf = 0; // GPIOF 数据变量
118. WREN(1); // 写数据
119. CLK(0); // 时钟信号
120. buf = GPIO_ReadOutputData(GPIOF) & 0x00C0; // 保留第6、7位,即时钟信号和读写信号不变
121. buf |= ((write_addr & 0x7) << 3) | (write_date << 8) ; // 将读取地址放入GPIOF, 写地址在3-5位,写数据在8-11位
122. GPIO_Write(GPIOF, buf); // GPIOF输出数据
123. CLK(1);
124. WREN(0);
125. }
126.
127. void Key_Scan(void)
128. {
129. uint8_t gpioc_data; // GPIOC数据读取变量
130. uint8_t read_addr; // 拨码开关 0-2
131. uint8_t write_addr; // 写入地址
132. uint8_t read_date; // 拨码开关 4-7
133. uint8_t write_date; // 写入数据
134. if(KEY_READ() == 0) // 读按键检测
135. {
136. Delay_Ms(5); // 防抖
137. if(KEY_READ() == 0)
138. {
139. while(KEY_READ() == 0){};
140. gpioc_data = ~(GPIO_ReadInputData(GPIOC) & 0x00FF); // 获取拨码开关数据,上0,下1
141. read_addr = gpioc_data & 0x07; // 获得读地址
142. read_date = (gpioc_data & 0xF0) >> 4; // 读数据,这个没用,调试用
143. Read_Data(read_addr, &read_date); // 传入指针读取FPGA内存数据
144. GPIO_Write(GPIOB, read_date); // 获得的数据从GPIOB发送到LED
145. //LED1(!LED1_DATA());
146. }
147. }
148. if(KEY_WRITE() == 0) // 写按键检测
149. {
150. Delay_Ms(5); // 防抖
151. if(KEY_WRITE() == 0)
152. {
153.
154. while(KEY_WRITE() == 0){};
155. gpioc_data = ~(GPIO_ReadInputData(GPIOC) & 0x00FF); // 获取拨码开关数据,上0,下1
156. write_addr = gpioc_data & 0x07; // 获得写地址
157. write_date = (gpioc_data & 0xF0) >> 4; // 写数据保存
158. Write_Data(write_addr, write_date); // 向FPGA内存写入数据
159. GPIO_Write(GPIOB, write_date); // 把向FPGA写入的数据显示出来
160. //LED2(!LED2_DATA());
161. }
162. }
163. }
164.
165.
166. int main(void)
167. {
168. Delay_Init();
169. Led_Init(); //LED灯初始化
170. Sw_Init(); //拨码开关初始化 按键初始化
171. GPIO_FPGA_Init(); // GPIOF初始化
172. while(1)
173. {
174. Key_Scan();
175. }
176. }

2.5 存储器实验结果

存储器实验的实验步骤如下所示:

(1) 在地址101写入数据1001,如图18所示;

图 18 在地址101写入数据1001

(2)在地址111写入数据0111,如图19所示;

图 19 在地址111写入数据0111

(3)在地址101读出数据1001,如图20所示;

图 20 在地址101读出数据1001

(4) 在地址111读出数据0111,如图21所示;

图 21 在地址111读出数据0111

3 实验三 外部中断实验

3.1 实验目的

(1)掌握 NVIC 中断优先级配置。

(2)学会外部中断配置

3.2 实验原理及内容

(1)NVIC中断优先级

NVIC 是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对 Cortex-M4 内核里面的 NVIC 进行裁剪,把不需要的部分去掉,所以说进行裁剪,把不需要的部分去掉,所以说 STM32 的 NVIC 是 Cortex-M4 的 NVIC 的一个子集。

CM4 内核可以支持 256 个中断,包括 16 个内核中断和 240 个外部中断,256 级的可编程中断设置。对于 STM32F4 没有用到 CM4 内核的所有东西,只是用到了一部分,对于 STM32F40 和 41 系列共有 92 个中断,其中有 10 个内核中断和 82 个可屏蔽中断,常用的为 82 个可屏蔽中断。

ISER[8] 中断使能寄存器组,用来使能中断,每一位控制一个中断,由于上面已经说明了控制 82 个可屏蔽的中断,因此利用 ISER[0~2] 这三个 32 位寄存器就够了。以下的几个寄存器同理。

ICER[8]——中断除能寄存器组,用来消除中断。

ISPR[8]——中断挂起控制寄存器组,用来挂起中断。

ICPR[8]——中断解挂控制寄存器组,用来解除挂起。

IABR[8]——中断激活标志寄存器组,对应位如果为 1 则表示中断正在被执行。

IP[240]——中断优先级控制寄存器组,它是用来设置中断优先级的。我们只用到了 IP[0]~IP[81],每个寄存器只用到了高 4 位,这 4 位又用来设置抢占优先级和响应优先级(有关抢占优先级和响应优先级后面会介绍到),而对于抢占优先级和响应优先级各占多少位则由 AIRCR 寄存器控制,相关设置如表1所示。

表 1 中断分组
AIRCR[10:8] bit[7:4]分配情况 分配结果
0 111 0:4 0 位抢占优先级,4 位响应优先级
1 110 1:3 1 位抢占优先级,3 位响应优先级
2 101 2:2 2 位抢占优先级,2 位响应优先级
3 100 3:1 3 位抢占优先级,1 位响应优先级
4 011 4:0 4 位抢占优先级,0 位响应优先级

关于抢占优先级和响应优先级的理解,可以将它们简单地理解为两个级别,抢占优先级的级别要比响应优先级的级别高,简单地理解为一个为长辈的一个为晚辈的,晚辈要让着长辈,因此抢占优先级的中断可以打断响应优先级的中断,而同级别的中断就得有个先来后到的了,先来的先执行。抢占优先级与相应优先级示例,如表2所示。

表 2 抢占优先级与响应优先级示例
中断向量 抢占优先级 响应优先级
A 0 1 抢占优先级相同,响应优先级数值小的优先级高
B 0 2
A 1 2 响应优先级相同,抢占优先级数值小的优先级高
B 0 2
A 1 0 抢占优先级比响应优先级高
B 0 2
A 1 1 抢占优先级和响应优先级均相同,则中断向量编号小的先执行
B 1 1

(2)外部中断

外部中断事件控制器(EXTI)管理了控制器的 23 个中断事件线。每个中断事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI可以实现对每个中断事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。

EXTI功能框图如图22所示。

图 22 EXTI 功能框图

图 22 中,在信号线上打一个斜杠并标注 23 字样表示在控制器内部类似的信号线路有 23 个,这与 EXTI 总共有 23 个中断事件线是吻合的。

EXTI 可分为两大部分功能,一个是产生中断,另一个是产生事件。图 22 中,线路 ①②③④⑤ 指示的电路流程是一个产生中断的线路,最终信号流入到 NVIC 控制器内。

编号 1 是输入线,EXTI 控制器有 23 个中断事件输入线,这些输入线可以通过寄存器设置为任意一个 GPIO,也可以是一些外设的事件,输入线一般是存在电平变化的信号。

编号2 是一个边沿检测电路,它会根据上升沿触发选择寄存器(EXTI_RTSR)和下降沿触发选择寄存器 (EXTI_FTSR)对应位的设置来控制信号触发。边沿检测电路以输入线作为信号输入端,如果检测到有边沿跳变就输出有效信号 1 给编号 3 电路,否则输出无效信号 0 。而 EXTI_RTSR 和 EXTI_FTSR
两个寄存器可以控制器需要检测哪些类型的电平跳变过程,可以是只有上升沿触发、只有下降沿触发或者上升沿和下降沿都触发。

编号3 电路实际就是一个或门电路,它一个输入来自编号 2 电路,另外一输入来自软件中断事件寄存器 (EXIT_SWIER) 。 EXTI_SWIER 允许我们通过程序控制就可以启动中断 事件线,这在某些地方非常有用。

编号 4 电路是一个与门电路,它一个输入编号 3 电路,另外一个输入来自中断屏蔽寄存器(EXTI_IMR)。与门电路要求输入都为 1 才输出 1,导致的结果如果EXTI_IMR 设置为 0 时,那不管编号 3 电路的输出信号是 1 还是 0,最终编号 4 电路输出的信号都为 0;如果 EXTI_IMR 设置为 1 时,最终编号 4 电路输出的信号才由编号 3 电路的输出信号决定,这样我们可以简单地控制 EXTI_IMR 来实现是否产生中断的目的。编号 4 电路输出的信号会被保存到挂起寄存器(EXTI_PR)内,如果确定编号 4 电路输出为 1 就会把 EXTI_PR 对应位置 1。

编号 5 是将 EXTI_PR 寄存器内容输出到 NVIC 内,从而实现系统中断事件控制。它是一个产生事件的线路,最终输出一个脉冲信号。

编号 6 电路是一个与门,它一个输入编号 3 电路,另外一个输入来自事件屏蔽寄存器(EXTI_EMR)。如果 EXTI_EMR 设置为 0 时,那不管编号 3 电路的输出信号是 1 还是 0,最终编号 6 电路输出的信号都为 0;如果 EXTI_EMR 设置为 1时,最终编号 6 电路输出的信号才由编号 3 电路的输出信号决定,这样我们可以简单的控制、EXTI_EMR 来实现是否产生事件的目的。

编号 7 是一个脉冲发生器电路,当它的输入端,即编号 6 电路的输出端,是一个有效信号 1 时就会产生一个脉冲;如果输入端是无效信号就不会输出脉冲。

编号 8 是一个脉冲信号,就是产生事件的线路最终的产物,这个脉冲信号可以给其他外设电路使用,比如定时器 TIM、模拟数字转换器 ADC 等等。产生中断线路目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输,属于硬件级的。

EXTI 有 23 个中断/事件线,每个 GPIO 都可以被设置为外部中断的中断输入口,这点也是 STM32F4 的强大之处。STM32F407 的中断控制器支持 23 个外部中断/事件请求。每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置。

STM32F407 的 23 个外部中断为:

  • EXTI 线 0~15:对应外部 IO 口的输入中断。

  • EXTI 线 16:连接到 PVD 输出。

  • EXTI 线 17:连接到 RTC 闹钟事件。

  • EXTI 线 18:连接到 USB OTG FS 唤醒事件。

  • EXTI 线 19:连接到以太网唤醒事件。

  • EXTI 线 20:连接到 USB OTG HS(在 FS 中配置)唤醒事件。

  • EXTI 线 21:连接到 RTC 入侵和时间戳事件。

  • EXTI 线 22:连接到 RTC 唤醒事件。

STM32F4 供 IO 口使用的中断线只有 16 个,但是 STM32F4 的 IO 口却远远不止 16 个,那么 STM32F4 是怎么把 16 个中断线和 IO 口一一对应起来的呢?于是STM32 就这样设计,GPIO 的管脚 GPIOx.0~GPIOx.15(x=A,B,C,D,E,F,G,H,I)分别对应中断线 0~15。这样每个中断线对应了最多 9 个 IO 口,以线 0 为例:它对应了 GPIOA.0、GPIOB.0、GPIOC.0、GPIOD.0、GPIOE.0、GPIOF.0、GPIOG.0、GPIOH.0、GPIOI.0。而中断线每次只能连接到 1 个 IO 口上,这样就需要通过配置来决定对应的中断线配置到哪个 GPIO 上了。GPIO 跟中断线的映射关系如图23所示。

图 23 GPIO 中断线映射

3.3 完整的硬件接线图和程序流程图

(1)硬件接线图

完整的硬件接线图如图24所示。

图 24 外部中断实验硬件接线图

(2)程序流程图

程序流程图如图25所示。

图 25 外部中断实验程序流程图

3.4 外部中断实验源代码程序

(1)main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
1.	/******************************************************************** 
2. 实验名称:数码管动态扫描实验
3.
4. 硬件模块:计算机原理应用实验箱
5.
6. 硬件接线:ARM P11接口---------数码管 P4接口
7. PC0--------SI
8. PC1--------RCK
9. PC2--------SCK
10. PC3--------A
11. PC4--------B
12. PC5--------C
13. ARM P10接口----------按键 P1接口
14. PB8--------KEY1_N
15. PB11-------KEY2_N
16. ARM P12接口-----------LED P2接口
17. PF0~PF7------LED1~LED8
18. 注:可用20P排线直连P11、P4接口,直连P10、P1接口,直连P12、P2接口。
19.
20. 实验现象:数码管上显示数字0~7,可通过按键检测或者外部中断触发流水灯。
21. ************************************************************************/
22. #include "stm32f4xx.h"
23. #include "delay.h"
24. #include "smg.h"
25. #include "led.h"
26. #include "exti.h"
27. #include "my_timer.h"
28.
29. #define KEY GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11)
30.
31. void Key_Hardware_Init()
32. {
33. GPIO_InitTypeDef GPIO_TypeDefStructure;
34.
35. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); //开启中断输入端口时钟
36.
37. GPIO_TypeDefStructure.GPIO_Pin = GPIO_Pin_11;
38. GPIO_TypeDefStructure.GPIO_Mode = GPIO_Mode_IN; //通用输入模式
39. GPIO_TypeDefStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
40. GPIO_Init(GPIOB, &GPIO_TypeDefStructure);
41. }
42.
43.
44. int main(void)
45. {
46. uint8_t i;
47. uint16_t cont = 0;
48. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
49. Delay_Init(); //延时初始化
50. EXTI_Configure(); //外部中断初始化
51. TIM1_Configure(999, 167); //定时器初始化 1ms
52. SMG_Init(); //数码管初始化
53. LED_Hardware_Init(); //LED灯初始化
54. Key_Hardware_Init(); //按键检测初始化
55.
56. while(1)
57. {
58. cont++;
59. if(cont >= 500)
60. {
61. cont = 0;
62. SMG_Sele(i); //数码管显示数据
63. if(i == 5) //数码管数据显示到第五位时按键检测
64. {
65. if(!KEY)
66. {
67. Delay_Ms(10);
68. if(!KEY)
69. {
70. while(!KEY){};
71. led_flow_flag = 1; //开启流水灯
72. }
73. }
74. }
75. i++;
76. i &= 0x07;
77.
78. }
79. Delay_Ms(1); //需要明显效果时打开延时
80. LED_Flow();
81. }
82. }
83.
84. //end file

(2)led.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
1.	#include "led.h"  
2. #include "delay.h"
3.
4. uint8_t led_flow_flag = 0; //流水灯开启标志
5.
6. /**********************************************************
7. *功 能:LED初始化
8. *参 数:无
9. *返回值:无
10. **********************************************************/
11. void LED_Hardware_Init(void)
12. {
13. GPIO_InitTypeDef GPIO_InitStructure;
14.
15. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); //开启GPIOF的时钟
16.
17. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
18.
19. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //输出模式
20. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出
21. GPIO_InitStructure.GPIO_Speed = GPIO_Medium_Speed; //速率25MHz
22. GPIO_Init(GPIOF, &GPIO_InitStructure);
23.
24. GPIO_Write(GPIOF, 0x0000);
25. }
26.
27.
28. void LED_Flow(void)
29. {
30. static uint8_t cont;
31. static uint16_t led_data;
32. if(led_flow_flag == 1)
33. {
34. led_data = 1<<cont;
35. LED_SET(led_data);
36. Delay_Ms(100);
37. cont++;
38. cont &= 0x07; //大于7后清零
39. }else if(led_flow_flag == 2)
40. {
41. LED_SET(0xff00);
42. Delay_Ms(300);
43. LED_SET(0x0000);
44. Delay_Ms(300);
45. }
46. }

(3)exti.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
1.	#include "exti.h"  
2. #include "led.h"
3.
4. /*******************************
5. 功 能:外部中断配置
6. 参 数:无
7. 返回值:无
8. *******************************/
9. void EXTI_Configure(void)
10. {
11. GPIO_InitTypeDef GPIO_TypeDefStructure;
12. EXTI_InitTypeDef EXTI_TypeDefStructure;
13. NVIC_InitTypeDef NVIC_TypeDefStructure;
14.
15. //开启中断输入端口时钟
16. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
17.
18. //开启外部中断时钟
19. RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
20.
21. GPIO_TypeDefStructure.GPIO_Pin = GPIO_Pin_8;
22. GPIO_TypeDefStructure.GPIO_Mode = GPIO_Mode_IN; //通用输入模式
23. GPIO_TypeDefStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
24. GPIO_Init(GPIOB, &GPIO_TypeDefStructure);
25.
26. //中断线关联
27. SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource8);
28.
29. EXTI_TypeDefStructure.EXTI_Line = EXTI_Line8;
30. EXTI_TypeDefStructure.EXTI_Mode = EXTI_Mode_Interrupt;
31. EXTI_TypeDefStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
32. EXTI_TypeDefStructure.EXTI_LineCmd = ENABLE;
33. EXTI_Init(&EXTI_TypeDefStructure);
34.
35. //EXT9_5_IRQn中断向量优先级设置
36. NVIC_TypeDefStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
37. NVIC_TypeDefStructure.NVIC_IRQChannelPreemptionPriority = 0;
38. NVIC_TypeDefStructure.NVIC_IRQChannelSubPriority = 7;
39. NVIC_TypeDefStructure.NVIC_IRQChannelCmd = ENABLE;
40. NVIC_Init(&NVIC_TypeDefStructure);
41. }
42.
43.
44.
45. /*******************************
46. 功 能:外部中断服务函数
47. 参 数:无
48. 返回值:无
49. *******************************/
50. void EXTI9_5_IRQHandler(void)
51. {
52. if(EXTI_GetITStatus(EXTI_Line8))
53. {
54. if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_8) == 0)
55. {
56. led_flow_flag = 2;
57. EXTI_ClearITPendingBit(EXTI_Line8);
58. }
59. }
60. }

(4)smg.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
1.	#include "smg.h"  
2. #include "delay.h"
3.
4. /*******************************
5. 功 能:数码管端口初始化
6. 参 数:无
7. 返回值:无
8. *******************************/
9. void SMG_Init(void)
10. {
11. GPIO_InitTypeDef GPIO_InitStructure;
12.
13. RCC_AHB1PeriphClockCmd(SMG_RCC_GPIO, ENABLE); //开启GPIOC的时钟
14.
15. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2
16. | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;
17. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //输出模式
18. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出
19. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
20. GPIO_Init(SMG_GPIO, &GPIO_InitStructure);
21. }
22.
23. /*******************************
24. 功 能:HC595发送数据
25. 参 数:dat 数据
26. 返回值:无
27. *******************************/
28. void HC595_Send(uint8_t dat)
29. {
30. uint8_t dat_buf = 0, i;
31. for(i=0; i<8; i++)
32. {
33. dat_buf = dat & 0x80;
34. if (dat_buf) //输出1bit数据
35. {
36. HC595_SI(1); //将74HC595串行数据输入引脚设置为高电平
37. }
38. else
39. {
40. HC595_SI(0); //将74HC595串行数据输入引脚设置为低电平
41. }
42. HC595_SCK(0);
43. Delay_Us(1);
44. HC595_SCK(1);
45. Delay_Us(1);
46. dat <<= 1;
47. }
48. HC595_RCK(0);
49. Delay_Us(3);
50. HC595_RCK(1);
51. }
52.
53.
54.
55. //显示的数字数组,依次为0,1,..,7
56. uint8_t digivalue[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07};
57.
58. /*******************************
59. 功 能:数码管位段控制
60. 参 数:index 对应的数码管
61. 返回值:无
62. *******************************/
63. void SMG_Sele(uint8_t index)
64. {
65. HC595_Send(digivalue[index]);
66. }

(5)my_timer.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
1.	#include "my_timer.h"  
2. #include "led.h"
3.
4. /******************************************
5. 功 能:定时器1初始化
6. 参 数:arr 定时器初值 psc 预分频系数
7. 返回值:无
8. ******************************************/
9. void TIM1_Configure(uint32_t arr,uint16_t psc)
10. {
11. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
12. NVIC_InitTypeDef NVIC_InitStructure;
13.
14. RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
15.
16. //定时器初值
17. TIM_TimeBaseStructure.TIM_Period = arr;
18. //预分配系数
19. TIM_TimeBaseStructure.TIM_Prescaler = psc;
20. //向上计数
21. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
22. //时钟分频因子
23. TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
24. //重复计数次数
25. TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
26.
27. TIM_TimeBaseInit(TIM1,&TIM_TimeBaseStructure);
28.
29. //选择中断向量
30. NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_TIM10_IRQn;
31. //抢占优先级
32. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
33. //响应优先级
34. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 6;
35. //使能中断向量
36. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
37. NVIC_Init(&NVIC_InitStructure);
38.
39. //TIM1中断使能
40. TIM_ITConfig(TIM1,TIM_IT_Update,ENABLE);
41. //TIM1使能
42. TIM_Cmd(TIM1,ENABLE);
43. }
44.
45. /******************************************
46. 功 能:定时器1中断服务函数
47. 参 数:无
48. 返回值:无
49. ******************************************/
50. void TIM1_UP_TIM10_IRQHandler(void)
51. {
52. static uint16_t cont_time = 0;
53. if(TIM_GetITStatus(TIM1, TIM_IT_Update) == SET)
54. {
55. if(led_flow_flag)
56. {
57. cont_time++;
58. if(cont_time >= 5000) // 闪烁时间
59. {
60. cont_time = 0;
61. led_flow_flag = 0;
62. }
63. }
64. TIM_ClearITPendingBit(TIM1,TIM_IT_Update);
65. }
66. }

3.5 外部中断实验结果

外部中断实验的实验步骤如下所示:

(1) 正常状态下,数码管由0到7不断循环显示数字,如图26所示。

图 26 数码管由0到7不断循环显示数字

(2) 按下按键1后,数码管保持当前显示不动,交通灯闪烁,如图27所示。

图 27 数码管保持当前显示不动,交通灯闪烁

(3) 当数码管显示5时,按下按键2后,数码管保持当前显示不动,流水灯启动,如图28所示。

图 28 数码管保持当前显示不动,流水灯启动

4 实验总结

4.1 ARM相关驱动程序的安装

由于授课教师提供的FTP服务器中只含有ST-LINK的驱动安装包,而本次实验需要使用的是J-LINK驱动安装,因此需要自行在网络上下载J-LINK的驱动安装包,Keil才可以读取到硬件。

4.2 Keil注册机的下载

由于FTP服务器中的Keil注册机已经在2021年的今天不再适用,故需要通过网络自行搜索下载最新版破解到2032年的Keil注册机。此外,Keil注册机可能会被杀毒软件误杀,且无法在QQ群中进行共享,因此最好的方式还是从网络上下载。

4.3 USB-Blaster驱动的安装

由于版本的变化,在先前数电实验中使用的Quartus 20.1版本所对应的USB Blaster驱动无法与课程要求的Quartus 13.1版本相适应,且电脑无法自动安装驱动。故需要打开设备管理器手动在Quartus 13.1安装文件中的drivers文件夹添加USB Blaster驱动。

4.4 Keil软件的烧录问题

在编程过程中遇到了点击烧录但开发板的效果没有变化的情况。这是因为在烧录之前没有进行ranslate再进行build操作,然后再进行烧录。

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022-2024 lgc0208@foxmail.com
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信