外观
四、代码介绍
总体概述
项目使用STM32F103C8T6作为主控芯片,代码使用HAL库、STM32Cubemx进行图形化配置+Keil5进行编译,用户仅需关注逻辑代码实现即可。
该项目代码并不复杂,使用功能仅有IIC、定时器ETR、定时器PWM输出、串口通信、SPI、adc模块;处理的重点在频率的获取,以及频率的显示,由于外部输入频率较高,此时不能在读取频率时浪费太多时间,否则会导致计算不准。
SI5351A驱动代码
时钟发生器芯片支持IIC通信,且具有图形化配置工具,我们可以快速配置并生成对应的寄存器,详细的配置可以参考(https://blog.csdn.net/define_Motion/article/details/128301947)这篇博客,由于输出频率设置完后,一般不需要频繁去改动了,所以图形化配置就够用了,我们要做的就是将生成的寄存器数据发送给设备即可,唯一需要注意的是iic地址,图形化工具是7bit,实际代码应该是8bit。另外在GitHub上也有大佬写了任意输出频率的代码,这里大家可以参考(https://github.com/afiskon/stm32-si5351)
C
/*
函数内容:根据ClockBuilder软件生成的寄存器参数配置输出
函数参数:无
返回值:无
*/
void si5351a_default_configuration(void)
{
uint16_t i = 0;
uint8_t addr = 0,value = 0;
for(i=0;i<SI5351A_REVB_REG_CONFIG_NUM_REGS;i++)
{
addr = si5351a_revb_registers2[i].address;
value = si5351a_revb_registers2[i].value;
HAL_I2C_Mem_Write(&hi2c2,SI5351A_ADDR,addr,I2C_MEMADD_SIZE_8BIT,&value,1,100);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
峰值电压代码
由于输入频率不是固定的,读取峰值电压时,应该考虑最低频率也就是1hz时方波的电压,也就是0.5s低电平,0.5s高电平,那么我们至少需要500ms以上的读取,才能做到读取是正确的,这里我间隔10ms读取一次,取600ms内的最大值为峰值电压。
C
/*
函数内容:得到采集到的峰值adc值,最小是1Hz频率,也就是至少要采集500ms,否则会采集不到峰值
函数参数:无
返回值:无
*/
uint16_t get_peak_value(void)
{
uint16_t max_value = 0,temp_val = 0;
uint8_t i = 0;
for(i=0;i<60;i++)
{
temp_val = Get_ADC_Val();
if(max_value < temp_val){
max_value = temp_val;
}
HAL_Delay(10);
}
return max_value;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pwm输出电压代码
pwm输出电压主要是靠占空比,这里我们首先要确定pwm输出模式,判断是越靠近比较值占空比越高还是越小占空比越高即可。这里我选择的是pwm模式2,越靠近比较值占空比越高。然后通过将需要的电压转换为对应的计数值进行设置即可。
C
static uint8_t pwm_state = PWM_OFF;
static uint16_t pwm_period = 180; //默认周期为180
static uint16_t pwm_duty = 90; //默认占空比为50%
/*
* 函数内容:设置pwm输出电压
* 函数参数:无
* 返回值:uint16_t
*/
void set_output_vol(float vol)
{
uint16_t pwm_value = 0;
if(vol >= 3.3f){
vol = 3.3f;
}
else if(vol <= 0){
vol = 0;
}
pwm_value = (vol / 3.3f) * pwm_period;
set_pwm_duty(pwm_value);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
输入频率获取
外部的频率是通过定时器的ETR进行输入的,每产生一个上升沿/下降沿就触发一次计数,这里我们已经将外部输入信号转化为方波了,此时间隔1s去读取计数值,这样就知道对应的频率了,由于计数值很大,可能会溢出,这里增加一个溢出标志位,然后1s中去读取一次定时器的计数值+溢出值,需要注意读取完成后立刻清除计数,为下一次计数做准备,同时这个定时器的中断优先级要设置高一点,不能被其他事件打断,否则也容易造成干扰。
C
static __IO uint16_t key_timer_value; //按键定时器
static __IO uint16_t tft_timer_value; //屏幕定时器
static uint8_t key_timer_bit = RUN_MS_TIMER; //ms定时器标志位
static uint8_t tft_timer_bit = RUN_MS_TIMER; //ms定时器标志位
static uint16_t ms_cnt = 0; //ms计数器,每1s去进行处理,得到hz
static uint32_t irq_cnt = 0; //频率溢出计数器值
static uint32_t freq = 0; //频率值
static uint8_t freq_bit = 0; //频率计算完成标志位
/*
* 函数内容:定时器更新中断回调函数
* 函数参数:TIM_HandleTypeDef *htim -- 定时器句柄
* 返回值:无
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM4)
{
ms_cnt++;
if(ms_cnt >= 1000)
{
//1s处理一次计数值,得到hz
freq = __HAL_TIM_GetCounter(&htim1);
__HAL_TIM_SetCounter(&htim1,0); //清零上一次的计数值
freq = freq + irq_cnt * 65536;
ms_cnt = 0; //清除ms计数值
irq_cnt = 0; //清除溢出计数值
freq_bit = 1; //频率获取完成
}
if(key_timer_bit == RUN_MS_TIMER)
{
key_timer_value++;
if(key_timer_value >= 10000){
key_timer_value = 0; //防止数据越界
}
}
if(tft_timer_bit == RUN_MS_TIMER)
{
tft_timer_value++;
if(tft_timer_value >= 10000){
tft_timer_value = 0; //防止数据越界
}
}
}
if(htim->Instance == TIM1)
{
irq_cnt++; //溢出计数值自增
}
}
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
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
主函数实现
这里我仅实现了基础了频率测量功能,按键、指示灯都没有使用,并且,由于此时输入的频率是我测试下来已经很准了,所以低频输入通道也没有使用。在主函数中,对相关外设初始化即可,然后死循环间隔一段时间后刷新显示并发送数据到串口。
C
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
uint16_t vref_adc_value = 0;
uint16_t adc_value = 0;
uint32_t freq_value = 0;
float peak_vol = 0;
char show_Data[32] = {0};
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_I2C2_Init();
MX_TIM1_Init();
MX_TIM2_Init();
MX_TIM3_Init();
MX_USART1_UART_Init();
MX_TIM4_Init();
/* USER CODE BEGIN 2 */
si5351a_default_configuration(); //锁相环默认配置输出clk0-10khz,clk1-100khz,clk2-1Mhz
control_switch(HIGH_FREQ_IN); //默认控制为高频输入
Set_ADC_Channel(ADC_CHANNEL_VREFINT); //设置adc-vref通道输入
vref_adc_value = Get_ADC_Average(200); //得到200次平均值-vref通道
Set_ADC_Channel(ADC_CHANNEL_9); //设置adc通道9输入
TFT_Init(); //tft初始化
TFT_Fill(0,0,LCD_W,LCD_H,BLACK);
show_freq_value(5000000,10000000,20000000); //显示锁相环输出频率值,这个是默认参数,固定值用于校准与测试
HAL_TIM_Base_Start_IT(&htim4); //打开定时器4中断
HAL_TIM_Base_Start_IT(&htim1); //打开定时器1中断
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1); //打开pwm输出
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
adc_value = get_peak_value(); //得到输入信号adc值
peak_vol = (adc_value * 1.0f / vref_adc_value) * 1.2f; //得到输入信号峰值电压
set_output_vol(peak_vol/2); //将比较值设置为输入信号峰值一半
if(get_freq_bit_value() == 1)
{
freq_value = get_freq_value();
TFT_ShowString(0,40,(uint8_t *)"Freq:",GREEN,BLACK,24,0);
if(freq_value < 1000){
sprintf(show_Data,"%7d Hz",freq_value);
}
else if(freq_value < 1000000){
sprintf(show_Data,"%7.2fKHz",(float)(freq_value/1000.0f));
}
else
{
sprintf(show_Data,"%7.2fMHz",(float)(freq_value/1000000.0f));
}
TFT_ShowString(0,64,(uint8_t *)show_Data,WHITE,BLACK,24,0);
printf("vol:%.1f\r\n",peak_vol);
printf("freq:%dhz\r\n",freq_value);
set_freq_bit_value(0);
}
}
/* USER CODE END 3 */
}
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
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
代码下载
开源链接附件下载即可:https://oshwhub.com/course-examples/stm32-jian-yi-pin-lv-ji