一、应用场景
有时候会遇到两个设备进行异步通信的情况,比如串口,没有同步时钟,靠波特率来约定采样时间,假设波特率为921600,接收端时钟频率为50MHz,那么一个bit需要50000000/921600≈54.25个时钟,但是实际数时钟个数不可能有小数,因此实际接收端波特率和发送端波特率会有一个误差,如果接收端始终按照固定的时钟间隔采样的话,并且在连续采集模式下,随着时间的推移误差会越来越大,最终带来的结果是采样出错。有一个解决办法是调整输入时钟,FPGA的PLL是乘以一个1~256之间的整数,然后除以一个1~256之间的整数得到一个新的频率,但是得到的频率有上限。
其实举这个例子不合适,因为串口会在每次接收到起始位的时候校准,不存在误差累计的问题。
二、程序代码
1 #include
2 #include
3 #include
4
5 void main(void)
6 {
7 int m, d, k, min_m, min_d;
8 const double in_freq = 50000000.0; /* 输入频率 */
9 const double boad_rate = 921600.0; /* 期望波特率 */
10 const double bit_cnt = 210; /* 连续发送的位数 */
11 const double max_out_freq = 315000000.0; /* 最大输出频率 */
12
13 double out_freq = 0; /* 输出频率 */
14 double min_err = 100;
15 double err = 0;
16 double clk_cnt = 0;
17 double frame_time_us_send = 1000000 * bit_cnt / boad_rate; /* 发送端发送一帧需要的时间 */
18 double frame_time_us_recv; /* 接收端接收一帧需要的时间 */
19
20 for (k = 0; k < 2; k++) {
21 for (m = 1; m <= 256; m++) {
22 for (d = 1; d <= 256; d++) {
23 out_freq = in_freq * (double)m / (double)d;
24 if (out_freq > max_out_freq) { /* 不能超过最高频率 */
25 continue;
26 }
27
28 clk_cnt = floor(out_freq / boad_rate);
29 if (clk_cnt < 5) { /* 小于5个时钟的处理不了 */
30 continue;
31 }
32
33 frame_time_us_recv = clk_cnt * bit_cnt * 1000000 / out_freq;
34
35 err = fabs(frame_time_us_recv - frame_time_us_send);
36
37 if (k == 0) {
38 if (err < min_err) {
39 min_err = err;
40 min_m = m;
41 min_d = d;
42 }
43
44 /* 看一眼不用PLL误差有多大 */
45 #if 0
46 if ((m == 1 && d == 1) || (err < 0.01)) { /* 打印出所有误差小于0.1的结果 */
47 printf("M = %d\n", m);
48 printf("D = %d\n", d);
49 printf("frame_time_us_send = %f\n", frame_time_us_send);
50 printf("frame_time_us_recv = %f\n", frame_time_us_recv);
51 printf("clk_cnt = %f\n", clk_cnt);
52 printf("err = %fus\n\n", err);
53 }
54 #endif
55 } else {
56 if (err == min_err) { /* 输出所有最优解 */
57 printf("M = %d\n", m);
58 printf("D = %d\n", d);
59 printf("输出频率 = %fMHz\n", out_freq / 1000000);
60 printf("最小误差 = %fus\n\n", err);
61 }
62 }
63 }
64 }
65 }
66
67
68
69
70 }