跳转至

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;
}

提醒

接口 gx_clock_set_div 功能使用请阅读动态调整频率之gx_clock_set_div接口说明

2. GPIO模拟红外 使用步骤*

注意

请严格按如下步骤调用响应的 API 接口。

  • 第一步:需要在 模拟红外app 前按需调用 LvpDynamiciallyAdjustCpuFrequencyLvpDynamiciallyAdjustGpioFrequency
    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

  1. 延时的误差与CPU的频率有着相关性,CPU频率越高,延时误差越小,以及gpio电平翻转的时间也会越少;

注意

  1. cpu频率越高延迟和误差越小,但是对应的会增加功耗,cpu频率切换根据需求来设置。
  2. gpio的频率是必须要升高的,否则会带来较大的延时。
  • 第二步:获取延时系数

    int coeff = gx_udelay_fine_config(gx_clock_get_module_frequence(CLOCK_MODULE_SCPU));    // 通过cpu频率获取延时系数
    

  • 第三步:读gpio寄存器

    gpio_level_flip_read(gpio_port);                // 读gpio寄存器,传需要操作的gpio端口
    

  • 第四步:关闭中断

  • 使用该接口需要关闭中断,否则受中断影响可能延时不准
    使用完请恢复中断

    unsigned int irq = gx_lock_irq_save();          // 保存中断并关闭中断,中断会影响延时
    

  • 第五步:延时和翻转电平

    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);                     // 操作完成后打开中断
    

  • 第七步:需要在 模拟红外app 后恢复默认频率:

    1
    2
    LvpDynamiciallyAdjustCpuFrequency(CPU_FREQUENCE_DEFAULT); // 按需填写
    LvpDynamiciallyAdjustGpioFrequency(GPIO_FREQUENCE_STANDARD_SPEED); // 必须传入 GPIO_FREQUENCE_STANDARD_SPEED
    

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;
}