Framist's Little House

◇ 自顶而下 - 面向未来 ◇

0%

【电赛练习题 0x01】频率计

【电赛练习题 0x01】频率计

设计任务:

利用单片机小系统,设计并制作一台简易频率计,能够测量并显示输入信号的频率。

基本要求:

(1)频率测量范围:10Hz~1kHz

(2)分辨率:不低于 1Hz

(3)测量精度:不低于(1% 被测值±1Hz)

(4)被测信号幅度:100mVpp ~ 3Vpp(正弦、三角、方波),可能含有 -1V ~ +1V 任意 的直流偏移。

(5)输入阻抗:>100k

发挥部分:

(1)频率测量范围扩展至 1Hz ~ 100kHz

(2)分辨率自动切换,保证至少 4 位有效数字。 (例如,1Hz10Hz 显示:X.XXX Hz; 10Hz \ 100Hz 显示:XX.XX Hz,1kHz~10kHz 显示:X.XXX kHz,依次类推)

(2)精度提高至(±0.05% 被测值±2 字)

(3)能自动识别输入的波形(正弦、三角、方波),并显示在屏幕上

(4)其他,自由发挥(如显示输入信号幅度等)

文档及测试:

(1)说明对指标要求的分析,论证为达到这些要求你所采用的方案。

(2)完整的电路原理图(可手绘)

(3)软件设计方案说明(关键流程图、状态转移图等)

(4)测试报告

1)测试方法。(即:上述各项指标如何测试?设计一套测试方案,考虑如何用 最少最快的测试项目完成所有的指标测试?)

2)每项测试所用仪器、连接方法、测试步骤、数据处理方法等

3)测试结果

说 明:

1)信号源采用硬木课堂仪器

2)不允许使用频率测量专用芯片 允许放弃部分指标(相应扣分)

解决方案:

MSP430 配合外部电路实现

依靠看门狗处决 bug(可能由于中断嵌套引发)

依靠二次函数拟合解决精度问题

附 录:

主要实现代码

注:代码不具有参考价值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
/* 警告:必须把 P1.6 跳线拔除
* main.c
*/
#include"MSP430G2553.h"
#include"TCA6416A.h"
#include"HT1621.h"
#include"LCD_128.h"

#define TEST_TIME 0.05

void P1_IODect();
//void P13_Onclick();
void GPIO_init();
void LCD_Display_Frequence();
void Init_Timer();
#define myMCLK 16000000 //~1 秒钟

unsigned int TA_overflow; //TA 溢出次数
double F = 0;//频率
unsigned int port_i; //周期次数
int nT = 10;

//static double DTime = 0;

void main(void)
{

// WDTCTL = WDTPW + WDTHOLD; //关狗
WDTCTL = WDT_ARST_1000;

BCSCTL1 = CALBC1_16MHZ; /* Set DCO to16MHz */
DCOCTL = CALDCO_16MHZ;

// // 惯例检测
// if (CALBC1_16MHZ == 0xFF ||CALDCO_16MHZ == 0xFF)
// {
// while (1)
// ; // If calibration constants erased
// // do not load, trap CPU!!
// }

// 初始化 IO 扩展口
TCA6416A_Init();
int i = 0;
for(i=0;i<=7;i++){
PinOUT(i,1);
}
PinOUT(0,0);
__delay_cycles(TEST_TIME*myMCLK);

// 初始化 lcd_128
HT1621_init();
PinOUT(1,0);
LCD_DisplayNum(0);
HT1621_Reflash(LCD_Buffer);
__delay_cycles(TEST_TIME*myMCLK);

// 初始化 GPIO
GPIO_init();
PinOUT(2,0);
__delay_cycles(TEST_TIME*myMCLK);

// 初始化计时器
Init_Timer();
PinOUT(3,0);

unsigned int t = TACCR0;
LCD_DisplayNum(t);
__delay_cycles(TEST_TIME*myMCLK);

_enable_interrupts(); //开总中断
PinOUT(4,0);
// _bis_SR_register(LPM3_bits); //LPM3 休眠

__delay_cycles(0.05*myMCLK);
while (1)
{
LCD_Display_Frequence();
// t = TAR;
// LCD_DisplayNum(t);
if(nT < 10){
for(i = 0; i < 4; i++ ){
WDTCTL = WDT_ARST_1000;
__delay_cycles(0.5*myMCLK);

}

}else{
for(i = 0; i < 1; i++ ){
WDTCTL = WDT_ARST_1000;
__delay_cycles(0.5*myMCLK);

}
}
}

}



/******************************************************************************************************
* 名 称:GPIO_Init()
* 功 能:启用按键 IO 的上拉电阻
* 入口参数:无
* 出口参数:无
* 说 明:无
* 范 例:无
******************************************************************************************************/
void GPIO_init()
{

//-----配合机械按键,启用内部上拉电阻-----
P1REN |= BIT3; //启用 P1.3 内部上下拉电阻
P1OUT |= BIT3; //将电阻设置为上拉
//-----配置 P1.3 中断参数-----
P1DIR &= ~BIT3; // P1.3 设为输入 (可省略)
P1IES |= BIT3; // P1.3 设为下降沿中断
P1IE |= BIT3 ; // 允许 P1.3 中断
}




/******************************************************************************************************
* 名 称:
* 功 能:
* 入口参数:无
* 出口参数:无
* 说 明:
* 范 例:无
******************************************************************************************************/
void Init_Timer()
{
TACCTL0 = CCIE; // CCR0 interrupt enabled
TACCR0 = 65534;
// TACTL = TASSEL_1 + MC_1 + TAIE + TACLR; //up mode
TACTL = TASSEL_1 + MC_1 + TAIE + TACLR; //up mode
return;
}


/******************************************************************************************************
* 名 称:PORT1_ISR()
* 功 能:响应 P1 口的外部中断服务
* 入口参数:无
* 出口参数:无
* 说 明:P1.0~P1.8 共用了 PORT1 中断,所以在 PORT1_ISR() 中必须查询标志位 P1IFG 才能知道
* 具体是哪个 IO 引发了外部中断。P1IFG 必须手动清除,否则将持续引发 PORT1 中断。
* 范 例:无
******************************************************************************************************/
#pragma vector = PORT1_VECTOR
__interrupt void PORT1_ISR(void)
{
//-----启用 Port1 事件检测函数-----
P1_IODect(); //检测通过,则会调用事件处理函数
P1IFG=0; //退出中断前必须手动清除 IO 口中断标志
}
/******************************************************************************************************
* 名 称:P1_IODect()
* 功 能:判断具体引发中断的 IO,并调用相应 IO 的中断事件处理函数
* 入口参数:无
* 出口参数:无
* 说 明:该函数兼容所有 8 个 IO 的检测,请根据实际输入 IO 激活“检测代码”。
* 本例中,仅有 P1.3 被用作输入 IO,所以其他 7 个 IO 的“检测代码”没有被“激活”。
* 范 例:无
******************************************************************************************************/
void P1_IODect()
{

unsigned int tar = TAR;
if (P1IFG & BIT3)
{
// P1OUT ^= BIT6;
port_i++;
if (port_i >= nT)
{

if ( (TA_overflow * 65535) + tar <= 0){
F = -1;
}else{
F = (double)((16000000.0 * nT) / ((TA_overflow * 65535) + tar)); //16mHZ
}

port_i = 0;
TA_overflow = 0;
TACTL |= TACLR; //计数清零
}
}

}
/******************************************************************************************************
* 名 称:void Timer_A1(void)
* 功 能:Timer A1 interrupt service routine
* 入口参数:无
* 出口参数:无
* 说 明:Timer A1 interrupt service routine
* 范 例:无
******************************************************************************************************/

#pragma vector = TIMER0_A1_VECTOR
__interrupt void Timer_A1()
{
//PinOUT(7,0);
switch (TA0IV)
{
case 2:
break;
case 4:
break;
case 10:
if (TA_overflow >= 65535){
TA_overflow = 0;
}

TA_overflow++;

break;
default:
break;

}
}

/******************************************************************************************************
* 名 称:LCD_Display_Frequence()
* 功 能:显示频率
* 入口参数:无
* 出口参数:无
* 说 明:
* 范 例:无
******************************************************************************************************/
void LCD_Display_Frequence()
{
_disable_interrupt();
LCD_Clear();
LCD_DisplaySeg(_LCD_Hz);
double F_true;
// F_true = (0.0020355368*F+0.457199);
if(F < 4884) {
F_true = (3.1344*0.000000001*F*F+0.0020282*F+0.012699);
}else if(F < 49139) {
F_true = (-2.747*0.0000000001*F*F+0.0020483*F+0.00032383);
}else if(F < 491380) {
F_true = (-3.4733*0.00000000001*F*F+0.0020529*F-0.63919);
}else{
F_true = (-1.6679*0.000000000001*F*F+0.0020567*F-5.9524);
}


//:
if (F_true < 1) {
LCD_DisplayNum(-1); //超量程
nT = 1;

}else if(1 <= F_true && F_true < 10){
LCD_DisplayNum((long int)(F_true*1000)); //整数显示
LCD_DisplaySeg(_LCD_DOT2);

nT = 1;
}else if(10 <= F_true && F_true < 100){
LCD_DisplayNum((long int)(F_true*100)); //整数显示
LCD_DisplaySeg(_LCD_DOT3);

nT = 10;
}else if(100 <= F_true && F_true < 1000){
LCD_DisplayNum((long int)(F_true*10)); //整数显示
LCD_DisplaySeg(_LCD_DOT4);

nT = 100;
}else if(1000 <= F_true){
LCD_DisplayNum((long int)(F_true*1)); //整数显示
LCD_DisplaySeg(_LCD_DOT2);
LCD_DisplaySeg(_LCD_k_Hz);
nT = 1000;
}

//测试用:
//LCD_DisplayNum((long int)(F/10)); //整数显示



HT1621_Reflash(LCD_Buffer);

_enable_interrupt();

return;
}