GPIO模拟红外使用说明
由于 GX8002 没有红外模块,因此需要借助 GPIO 来模拟红外的功能。那么我们需要在用到 GPIO模拟红外 输出的时候将相应模块频率提上来,用完后再将相应模块频率恢复正常,这样做的好处是可以 省功耗。
1. GPIO模拟红外 相关模块的频率配置接口
有两个接口需要用到:
- 配置 CPU 频率: LvpDynamiciallyAdjustCpuFrequency
- 配置 GPIO 模块频率: LvpDynamiciallyAdjustGpioFrequency
lvp/common/lvp_system_init.c |
---|
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 | int LvpDynamiciallyAdjustGpioFrequency(GPIO_FREQUENCE_LEVEL gpio_frequence_level)
{
unsigned int state = gx_lock_irq_save();
if (gpio_frequence_level == GPIO_FREQUENCE_HIGH_SPEED) {
gx_clock_set_module_source(CLOCK_MODULE_PMU , MODULE_SOURCE_24M_PLL);
gx_clock_set_module_source(CLOCK_MODULE_GPIO , MODULE_SOURCE_24M_PLL);
gx_clock_set_div(CLOCK_MODULE_PMU , 0);
} else if (gpio_frequence_level == GPIO_FREQUENCE_STANDARD_SPEED) {
gx_clock_set_module_source(CLOCK_MODULE_PMU , MODULE_SOURCE_1M_12M);
gx_clock_set_module_source(CLOCK_MODULE_GPIO , MODULE_SOURCE_1M_12M);
gx_clock_set_div(CLOCK_MODULE_PMU , 3);
}
gx_unlock_irq_restore(state);
return 0;
}
int LvpDynamiciallyAdjustCpuFrequency(CPU_FREQUENCE_LEVEL cpu_frequence_level)
{
unsigned int state = gx_lock_irq_save();
#ifdef CONFIG_ENABLE_PLL_FREQUENCY_50M
int pll_multiple = 2;
#else
int pll_multiple = 1;
#endif
if (cpu_frequence_level == CPU_FREQUENCE_4M) {
gx_clock_set_div(CLOCK_MODULE_SCPU, 6 * pll_multiple);
} else if (cpu_frequence_level == CPU_FREQUENCE_6M) {
gx_clock_set_div(CLOCK_MODULE_SCPU, 4 * pll_multiple);
} else if (cpu_frequence_level == CPU_FREQUENCE_8M) {
gx_clock_set_div(CLOCK_MODULE_SCPU, 3 * pll_multiple);
} else if (cpu_frequence_level == CPU_FREQUENCE_12M) {
gx_clock_set_div(CLOCK_MODULE_SCPU, 2 * pll_multiple);
} else if (cpu_frequence_level == CPU_FREQUENCE_24M) {
gx_clock_set_div(CLOCK_MODULE_SCPU, 1 * pll_multiple);
} else if (cpu_frequence_level == CPU_FREQUENCE_50M) {
if (pll_multiple == 2) {
gx_clock_set_div(CLOCK_MODULE_SCPU, 0);
} else {
gx_unlock_irq_restore(state);
return -1;
}
} else if (cpu_frequence_level == CPU_FREQUENCE_DEFAULT) {
gx_clock_set_div(CLOCK_MODULE_SCPU, CUSTOMIZE_FREQUENCY_MCU_DIV_PARAM);
} else {
gx_unlock_irq_restore(state);
return -1;
}
# ifdef CONFIG_SYSTICK_COUNTER
gx_timer_init();
# endif
gx_unlock_irq_restore(state);
return 0;
}
|
2. GPIO模拟红外 使用步骤
- 第一步:需要在 模拟红外app 前按需调用
LvpDynamiciallyAdjustCpuFrequency
跟 LvpDynamiciallyAdjustGpioFrequency
LvpDynamiciallyAdjustCpuFrequency(CPU_FREQUENCE_24M); // 将cpu频率调高,如果默认的频率本来就是24M或者大于24M,则不需要调整cpu频率
LvpDynamiciallyAdjustGpioFrequency(GPIO_FREQUENCE_HIGH_SPEED); // 将gpio频率调高,这一步必须,否则电平翻的延迟会较大
相关参数请参考:
CPU频率 (LvpDynamiciallyAdjustCpuFrequency) |
GPIO模块频率 (LvpDynamiciallyAdjustGpioFrequency) |
设置GPIO电平所需要的时间(us)gx_gpio_set_level |
设置GPIO电平所需要的时间(us)gpio_level_flip_write |
CPU_FREQUENCE_50M |
GPIO_FREQUENCE_HIGH_SPEED |
1.3 |
0.6 |
CPU_FREQUENCE_24M |
GPIO_FREQUENCE_HIGH_SPEED |
2.3 |
0.9 |
CPU_FREQUENCE_16M |
GPIO_FREQUENCE_HIGH_SPEED |
3.2 |
1.1 |
CPU_FREQUENCE_12M |
GPIO_FREQUENCE_HIGH_SPEED |
4.3 |
1.3 |
gpio电平翻转推荐使用的是 gpio_level_flip_write,延时较短;
tip
- 延时的误差与CPU的频率有着相关性,CPU频率越高,延时误差越小,以及gpio电平翻转的时间也会越少;
注意
- cpu频率越高延迟和误差越小,但是对应的会增加功耗,cpu频率切换根据需求来设置。
- gpio的频率是必须要升高的,否则会带来较大的延时。
3. GPIO模拟红外 示例
- 完整示例请参考gitlab上的lvp_sdk,以下只展示部分代码;
- 控制电平翻转这里有两种操作方式:
// 延时较大,包含读写操作
gx_gpio_set_level(int port, int level); // 常用的接口,读写操作,延迟会较长
// 下面两个接口组合使用,延迟较小;初始化的时候进行读操作,后面翻转只需要写操作,延迟相对较短
gpio_level_flip_read(int port); // 读接口
gpio_level_flip_write(int level); // 写接口
app/gpio_pwm_demo/lvp_app_gpio_pwm_demo.c |
---|
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 | #define GPIO_DOUT (( GX_REG_BASE_GPIO0 ) + 0x04) // 输出的数据
float half_cycle_f;
unsigned int gpio_dout_l, gpio_dout_h;
unsigned int gpio_port = 1;
int gpio_level_flip_read(int port) // 读取gpio的寄存器值
{
gx_gpio_set_direction(port, GX_GPIO_DIRECTION_OUTPUT);
gpio_dout_l = readl(GPIO_DOUT) & (~ (1 << (port%32)));
gpio_dout_h = readl(GPIO_DOUT) | (1 << (port%32));
return 0;
}
int gpio_level_flip_write(int level) // 写寄存器翻转gpio电平
{
writel(level, GPIO_DOUT);
return 0;
}
int gpio_pwm_init(int opt) // pwm初始化
{
half_cycle_f = 1000000.f / CONFIG_LVP_APP_GPIO_PWM_FREQ / 2; // 计算pwm半个周期的时长us,CONFIG_LVP_APP_GPIO_PWM_FREQ是在编译配置里设置的需要的pwm频率
int cpu_fre = gx_clock_get_module_frequence(CLOCK_MODULE_SCPU); // 获取cpu的频率
// 减掉翻转电平所需要的固定延迟
#if opt // 如果是使用gx_gpio_set_level,就是设为1,使用以下参数
if (cpu_fre >= 12287990 && cpu_fre <= 12288010)
half_cycle_f -= 4.2;
else if (cpu_fre >= 16383990 && cpu_fre <= 16384010)
half_cycle_f -= 3.2;
else if (cpu_fre >= 24575990 && cpu_fre <= 24576010)
half_cycle_f -= 2.3;
else if (cpu_fre >= 49151990 && cpu_fre <= 49152010)
half_cycle_f -= 1.3;
#else // 如果是使用的是gpio_level_flip_write接口,设为0,使用以下参数
if (cpu_fre >= 12287990 && cpu_fre <= 12288010)
half_cycle_f -= 1.3;
else if (cpu_fre >= 16383990 && cpu_fre <= 16384010)
half_cycle_f -= 1.1;
else if (cpu_fre >= 24575990 && cpu_fre <= 24576010)
half_cycle_f -= 0.9;
else if (cpu_fre >= 49151990 && cpu_fre <= 49152010)
half_cycle_f -= 0.6;
#endif
return 0;
}
// 以上接口不需要修改
// =========================================================================================================================
// 以下代码按需求修改
static int GpioPwmAppInit(void)
{
printf(LOG_TAG" ---- %s ----\n", __func__);
LvpDynamiciallyAdjustCpuFrequency(CPU_FREQUENCE_24M); // 将cpu频率调高,如果默认的频率本来就是24M或者大于24M,则不需要调整cpu频率
LvpDynamiciallyAdjustGpioFrequency(GPIO_FREQUENCE_HIGH_SPEED); // 将gpio频率调高,这一步必须,否则电平翻的延迟会较大
int coeff = gx_udelay_fine_config(gx_clock_get_module_frequence(CLOCK_MODULE_SCPU)); // 通过cpu频率获取延时系数
if (coeff == -1)
{
return -1;
}
printf("coeff = %d mcu:%d\n", coeff, gx_clock_get_module_frequence(CLOCK_MODULE_SCPU));
// gx_gpio_set_level 有固定延迟: https://nationalchip.gitlab.io/ai_audio_docs/software/lvp/SDK%E5%BC%80%E5%8F%91%E6%8C%87%E5%8D%97/SDK%E5%BC%80%E5%8F%91_FAQ/GPIO%E6%A8%A1%E6%8B%9F%E7%BA%A2%E5%A4%96%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9/#2
gpio_pwm_init(0); // pwm初始化,如果是使用gx_gpio_set_level翻转电平则传非0参数,如果使用的是gpio_level_flip_write接口,传参数0
int half_cycle = (int)half_cycle_f;
printf("half_cycle %d\n", half_cycle);
gpio_level_flip_read(gpio_port); // 读gpio寄存器,传需要操作的gpio端口
unsigned int irq = gx_lock_irq_save(); // 保存中断并关闭中断,中断会影响延时
while(1) {
gpio_level_flip_write(gpio_dout_l); // 电平设为低电平
gx_udelay_fine(half_cycle, coeff); // 延时,传入延时时间和延时系数
gpio_level_flip_write(gpio_dout_h); // 电平设为高电平
gx_udelay_fine(half_cycle, coeff); // 延时,传入延时时间和延时系数
}
gx_unlock_irq_restore(irq); // 操作完成后打开中断
LvpDynamiciallyAdjustGpioFrequency(GPIO_FREQUENCE_STANDARD_SPEED); // 操作完成之后需要恢复默认频率
LvpDynamiciallyAdjustCpuFrequency(CPU_FREQUENCE_DEFAULT); // 恢复默认cpu频率,和之前的调整cpu频率组合使用
return 0;
}
|