外观
四、代码介绍
总体概述
项目使用STM32F070F6P6作为主控芯片,代码使用HAL库、STM32Cubemx进行图形化配置+Keil5进行编译,用户仅需关注逻辑代码实现即可。
该项目代码并不复杂,使用功能仅有IIC、ADC、GPIO输入输出三个模块;处理的重点在屏幕数据的显示、RDA5807调频数据处理、adc数据的读取与校准;
屏幕数据显示
屏幕驱动不做介绍,移植厂商给的驱动即可。重点关注屏幕数据显示,该项目需要显示的数据有电池电量、频率显示、信号强度、音量大小;除了电池电量和信号指令外,其余数据都是不需要实时显示,关于电池电量显示,它其实是提前取模好的图片,这里根据比例判断显示对应图片即可。
C
//oled显示电池电量值
void oled_show_electric(float value)
{
if(value >= 3.0f)
{
OLED_ShowPicture(100,0,24,16,(uint8_t *)bmp2,1);
}
else if(value >= 2.8f)
{
OLED_ShowPicture(100,0,24,16,(uint8_t *)bmp3,1);
}
else if(value >= 2.7f)
{
OLED_ShowPicture(100,0,24,16,(uint8_t *)bmp4,1);
}
else if(value >= 2.6f)
{
OLED_ShowPicture(100,0,24,16,(uint8_t *)bmp5,1);
}
else
{
OLED_ShowPicture(100,0,24,16,(uint8_t *)bmp1,1);
}
}
//显示当前频率值
memset(show_data,0,32);
sprintf(show_data,"%5.1fMhz",Get_Freq());
OLED_ShowString(20,0,(uint8_t *)"FM Radio",16,1);
OLED_ShowString(16,24,(uint8_t *)show_data,24,1);
//显示当前音量
memset(show_data,0,32);
sprintf(show_data,"Vol:%2d",volume);
OLED_ShowString(90,48,(uint8_t *)show_data,12,1);
//显示当前信号质量
memset(show_data,0,32);
sprintf(show_data,"Rssi:%3d",RSSI_Status());
OLED_ShowString(0,48,(uint8_t *)show_data,12,1);
//显示当前电池电量
oled_show_electric(electric_quantity);
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
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
按键处理
按键处理比较繁琐一点,此处是iic与按键的复用,在单片机模式下,按键引脚既是IIC功能又是输入功能;需要做一个分时复用,默认是按键输入功能,当检查到按键按下后,此时将引脚切换为iic功能,发送命令,然后重新切换为按键等待再次按下;
C
/*
函数内容:设置seek引脚模式
函数参数:mode:按键模式(KEY——MODE)、I2C模式(I2C_MODE)
返回值:无
*/
void set_seek_pin_mode(uint8_t mode)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(mode == KEY_MODE)
{
GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
else if(mode == I2C_MODE)
{
GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
C
if(key_handle[seek1].key_state != KEY_NoPress)
{
if(key_handle[seek1].key_state == KeyPress)
{
set_seek_pin_mode(I2C_MODE); //设置为I2C模式
Seek_Control(B_87_108M, ENABLE, ENABLE, 8, ENABLE);//向上自动搜台
memset(show_data,0,32);
sprintf(show_data,"%5.1fMhz",Get_Freq()); //显示当前信号频率
OLED_ShowString(20,0,(uint8_t *)"FM Radio",16,1);
OLED_ShowString(16,24,(uint8_t *)show_data,24,1);
memset(show_data,0,32);
sprintf(show_data,"Rssi:%3d",RSSI_Status()); //显示当前信号质量
OLED_ShowString(0,48,(uint8_t *)show_data,12,1);
OLED_Refresh();
set_seek_pin_mode(KEY_MODE); //设置为按键模式
}
key_handle[seek1].key_state = KEY_NoPress;
}
if(key_handle[seek2].key_state != KEY_NoPress)
{
if(key_handle[seek2].key_state == KeyPress)
{
set_seek_pin_mode(I2C_MODE);
Seek_Control(B_87_108M, DISABLE, ENABLE, 8, ENABLE);//向下自动搜台
memset(show_data,0,32);
sprintf(show_data,"%5.1fMhz",Get_Freq()); //显示当前信号频率
OLED_ShowString(20,0,(uint8_t *)"FM Radio",16,1);
OLED_ShowString(16,24,(uint8_t *)show_data,24,1);
memset(show_data,0,32);
sprintf(show_data,"Rssi:%3d",RSSI_Status()); //显示当前信号质量
OLED_ShowString(0,48,(uint8_t *)show_data,12,1);
OLED_Refresh();
set_seek_pin_mode(KEY_MODE);
}
key_handle[seek2].key_state = KEY_NoPress;
}
if(key_handle[vol1].key_state != KEY_NoPress)
{
volume++;
if(volume >= 15){
volume = 15;
}
set_seek_pin_mode(I2C_MODE);
Volume_Set(volume); //设置音量大小
memset(show_data,0,32);
sprintf(show_data,"Vol:%2d",volume);
OLED_ShowString(90,48,(uint8_t *)show_data,12,1);
OLED_Refresh();
set_seek_pin_mode(KEY_MODE);
key_handle[vol1].key_state = KEY_NoPress;
}
if(key_handle[vol2].key_state != KEY_NoPress)
{
volume--;
if(volume <= 1){
volume = 1;
}
set_seek_pin_mode(I2C_MODE);
Volume_Set(volume); //设置音量大小
memset(show_data,0,32);
sprintf(show_data,"Vol:%2d",volume);
OLED_ShowString(90,48,(uint8_t *)show_data,12,1);
OLED_Refresh();
set_seek_pin_mode(KEY_MODE);
key_handle[vol2].key_state = KEY_NoPress;
}
if(key_handle[pow].key_state != KEY_NoPress)
{
mode = 0;
OLED_DisPlay_Off();
HAL_GPIO_WritePin(GPIOA, SW_Pin, GPIO_PIN_SET);
key_handle[pow].key_state = KEY_NoPress;
}
else
{
if(key_timer_value >= 10)
{
//10ms扫描按键
set_key_timer_value(0);
key_scanf(&key_handle[pow]);
}
if(key_handle[pow].key_state != KEY_NoPress)
{
mode = 1;
OLED_DisPlay_On();
HAL_GPIO_WritePin(GPIOA, SW_Pin, GPIO_PIN_RESET);
key_handle[pow].key_state = KEY_NoPress;
}
}
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
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
RDA5807代码
该部分内容主要借鉴开源大佬写的库,没做什么修改,仅修改基础驱动函数; RDA5807驱动代码借鉴于:GitHub-HaochuanDeng;这里附上对应的开源链接:https://github.com/HaochuanDeng/RDA5807M?tab=readme-ov-file
电压采集
关于电池供电的项目,采集电压时,有一个误区是大家不能直接使用adc值*3.3然后/4095,因为随着电池电量的减少,供电电压也是在变化的,此时参考值是固定的话是采集不准的,我们需要借助内部参考电压或者外部基准来进行校准,这里使用内部基准通道。
然后是STM32F070采集多通道电压需要使能持续转换模式,然后按照顺序依次读取两次adc值,才能得到对应使能通道的值,在读取完成后,记得关闭adc,否则一直在转换,你下次读取时第一个adc值就不知道是哪个通道的值了。
C
HAL_ADC_Stop(&hadc);
HAL_ADC_Start(&hadc);
adc_value = Get_ADC_Val();
vref_value = Get_ADC_Val();
HAL_ADC_Stop(&hadc);
electric_quantity = ((adc_value*1.0f) / vref_value) * 1.21f;
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8