在linux上编写求解ADAU1772的PLL系数

通过在Linux上编写代码,计算求解ADAU1772在 输入时钟(MCLK Input)为16.6MHz时,如何设置内部的4个参数,以达到符合输出时钟要求的目的。

据《ADAU1772》和SigmaStudio,1772 Codec 内部可修改的参数有 输入时钟分频(Input Clock Divider)、整数设置(Integer Setting)、分子(Numerator) 和 分母(Denominator)四个。

目标输出时钟(VCO Output) 是 24.576MHz。

原公式

以下为《ADAU1772》第30页中,关于PLL计算的公式描述:

image-20210706002255187

以下为SigmaStudio中的PLL设定界面参数设定:

image-20210706002412136

代码编写

注意:本代码在Linux上,以C++17的版本进行编译运行。

编译代码如下:

1
g++ main.cpp -std=c++17 -lpthread

在编写代码时,考虑到SigmaStudio和数据手册之间可能存在表述差异,因此计算PLL的源代码中,也包含了开启 1/2 系数的宏定义 #define HALFCOFF 1 ,具体代码如下:

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
/*---------------------------------------------------------
* HEADER FILES
*-------------------------------------------------------*/
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <thread>

using namespace std;
/*---------------------------------------------------------
* MACRO DEFINITION
*-------------------------------------------------------*/
#define ACCURACY 1000000000 //输出时钟精度,小数点后9个0
// #define HALFCOFF 1 //打开1/2系数进行计算,SigmaStudio图示中有该系数,但《ADAU1772》数据手册中没有该系数

/*---------------------------------------------------------
* MACRO DEFINITION FOR DEBUG
*-------------------------------------------------------*/
// #define DEBUG_MODE //Debug模式


/*---------------------------------------------------------
* DATA TYPES
*-------------------------------------------------------*/
typedef double CLK; //数据类型_时钟
typedef unsigned short COFF; //数据类型_系数


/*---------------------------------------------------------
* FILE DOMAIN DATA DECLARATION
*-------------------------------------------------------*/
CLK CLK_IN = 16.6; //输入时钟
CLK CLK_OUT = 24.576; //输出时钟

struct TargetCofficient {
COFF Numerator;
COFF Denominator;
};

COFF Maxiator = 65535;
COFF InputClockDivider = 4;
COFF IntegerSetting = 8;

/*---------------------------------------------------------
* FUNCTIONS DECLARATION
*-------------------------------------------------------*/
int isSafe(COFF, COFF); //判断系数的分子和分母相除是否满足要求
int isOK(CLK); //判断输出结果是否符合输出时钟的精度要求
int CofficientCalculate(CLK, COFF, COFF); //计算

void ICD1(void); //子线程
void ICD2(void); //子线程
void ICD3(void); //子线程
void ICD4(void); //子线程
void SysPrintf(void);

/*---------------------------------------------------------
* MAIN FUNCTIONS
*-------------------------------------------------------*/
int main(){

cout << "SYSTEM: Procedure starts." << endl;
thread thread1(ICD1);
thread thread2(ICD2);
thread thread3(ICD3);
thread thread4(ICD4);

thread1.join();
thread2.join();
thread3.join();
thread4.join();

cout << "SYSTEM: Procedure ends up." << endl;
return 0;
}


/*---------------------------------------------------------
* FUNCTIONS DEFINITION
*-------------------------------------------------------*/
int isSafe(COFF N, COFF D){
double result = (double)N / (double)D;
if( (result >= 0.1) && (result <= 0.9) ) return 1; //判断系数是否满足要求,满足则返回1
else return 0;
}

int isOK(CLK clock){
CLK integerClock , fractClock;
fractClock = modf(clock , &integerClock); //分别取出整数和小数部分

if( ((int)(fractClock * ACCURACY) == (int)(576/(double)1000 * ACCURACY)) && ((int)integerClock == (int)CLK_OUT) ) { //如果小数点精度满足要求,且整数部分相同
return 1; //则返回1
}
else return 0;
}

int CofficientCalculate(CLK ClockInput, COFF ICD, COFF IS){
CLK tempCLK;

for(COFF i = 0; i < Maxiator; i++){ //分母
for(COFF j = 0; j < Maxiator; j++){ //分子
if(isSafe(j, i)){
#ifdef HALFCOFF
tempCLK = ClockInput /(double)ICD * ((double)IS + ((double)j/(double)i)) *1/2; //有系数时
#else
tempCLK = ClockInput /(double)ICD * ((double)IS + ((double)j/(double)i)) ; //无系数时
#endif

#ifdef DEBUG_MODE
cout << "SYSTEM:IDC = " << ICD << ", IS = " << IS << hex <<", Numerator = 0x"<< j << ", Denominator = 0x" << i << ", OutputClock = " << tempCLK << endl;
#endif
}
else continue;

if( isOK(tempCLK)) {
cout << "SYSTEM:IDC = " << ICD << ", IS = " << IS << hex <<", Numerator = 0x"<< j << ", Denominator = 0x" << i << ", OutputClock = " << tempCLK << endl;
}
}
}
return 0;
}


/*---------------------------------------------------------
* THREAD _ CALCULATOR
*-------------------------------------------------------*/
void ICD1(void){
for(COFF enumIS = 2; enumIS <= IntegerSetting; enumIS++) CofficientCalculate(CLK_IN , 1, enumIS);
}

void ICD2(void){
for(COFF enumIS = 2; enumIS <= IntegerSetting; enumIS++) CofficientCalculate(CLK_IN , 2, enumIS);
}

void ICD3(void){
for(COFF enumIS = 2; enumIS <= IntegerSetting; enumIS++) CofficientCalculate(CLK_IN , 3, enumIS);
}

void ICD4(void){
for(COFF enumIS = 2; enumIS <= IntegerSetting; enumIS++) CofficientCalculate(CLK_IN , 4, enumIS);
}

void SysPrintf(void){
// while(1) cout << "Calculating...";
while(1){
printf("Calculating...");
printf("\r\003");
}
}


运行结果

通过自行编写的代码,遍历所有可更改的系数进行计算求解,取得小数点后9位精度,并且符合要求的系数如下(含1/2系数):

image-20210706001243698

以下为不含1/2系数的结果:

image-20210706001350802

经过手工计算,结果(小数点后9位)确实满足要求。