硬汉嵌入式论坛

 找回密码
 立即注册
查看: 913|回复: 2
收起左侧

开启pio 50M 后台DMA采集程序后,触发GPIO中断时引起异常

[复制链接]

747

主题

1049

回帖

3295

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
3295
发表于 2022-3-25 00:59:05 | 显示全部楼层 |阅读模式
发现新问题, 一晚上没解决。
开启pio 50M 后台DMA采集程序后,触发中断后引起异常。中断程序也没执行。
不写 bsp_StartDso(DSO_MODE_CH1CH2, DSO_FREQ_50M, 8 * 1024); 这句话,中断正常响应(仅仅熄灭LED), 写了就触发异常。

2i.png

pio程序启用不开dma是正常可执行GPIO中断。
只要开启DMA,当GPIO触发中断时,CPU异常停机。(已排除超频问题)

开启DMA的代码如下:双DMA循环触发

[C] 纯文本查看 复制代码
/*
*********************************************************************************************************
*    函 数 名: bsp_StartDso
*    功能说明: 启动示波器程序
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
void bsp_StartDso(DSO_MODE_E _mode, DSO_FREQ_E _FreqId, uint32_t _SampleSize) 
{
    dma_channel_config c;
    uint offset;
    int ctrl_chan;
    int data_chan;      
   
    static uint32_t s_transfer_count = 0;   
    PIO pio = pio0;
    uint8_t sm = 3;    
     
    /* 设置DMA优先级为高 */
    bus_ctrl_hw->priority = BUSCTRL_BUS_PRIORITY_DMA_W_BITS | BUSCTRL_BUS_PRIORITY_DMA_R_BITS;

    if (_mode == DSO_MODE_CH1CH2)       /* 双通道 */
    {
        offset = pio_add_program(pio0, &dso_ch1ch2_program);
        pio_dso_ch1ch2_init(pio, sm, offset, g_PioClkDiv[_FreqId]);
    }
    else if (_mode == DSO_MODE_CH1)     /* 单通道交错 */
    {
        offset = pio_add_program(pio0, &dso_ch1_program);
        pio_dso_ch1_init(pio, sm, offset, g_PioClkDiv[_FreqId]);           
    }
    else
    {
        return;
    }
    
    if (_SampleSize > DSO_SAMPLE_SIZE_MAX)
    {
        return;
    }
    
    s_transfer_count = _SampleSize;
    
    /* 启用2个DMA通道,实现不间断DMA传输 */
    ctrl_chan = dma_claim_unused_channel(true);     
    data_chan = dma_claim_unused_channel(true);
    g_tDSO.ctrl_chan = ctrl_chan;
    g_tDSO.data_chan = data_chan;
    
    /* 控制通道 */
    c = dma_channel_get_default_config(ctrl_chan);
    channel_config_set_transfer_data_size(&c, DMA_SIZE_32);    /* DMA控制寄存器bit */
    channel_config_set_read_increment(&c, false);
    channel_config_set_write_increment(&c, false);

    dma_channel_configure(
        ctrl_chan,
        &c,
        &dma_hw->ch[data_chan].al1_transfer_count_trig,     /* 目标地址: 数据通道的传输个数 */
        &s_transfer_count,                                  /* 源地址: 内存变量 */
        1,                                                  /* 传送1次 */
        false                                               /* 暂时不启动DMA传输 */
    );

    /* 数据通道 */
    c = dma_channel_get_default_config(data_chan);
    channel_config_set_transfer_data_size(&c, DMA_SIZE_16);         /* 双通道8bit,合计16bit */
    channel_config_set_dreq(&c, pio_get_dreq(pio, sm, false));      /* pio状态机的fifo数据请求 */
    channel_config_set_read_increment(&c, false);                   /* 源地址不地址 FIFO */
    channel_config_set_write_increment(&c, true);                   /* 目标地址递增,内存 */
    channel_config_set_chain_to(&c, ctrl_chan);                     /* 数据通道传输完毕后,自动启动控制通道 */
    channel_config_set_ring(&c, true, 15);                          /* 1 << 15 (32K) */
    
    dma_channel_configure(
        data_chan,
        &c,
        g_dso_rxbuf,        /* 目标地址: 内存 */
        &pio->rxf[sm],      /* 源地址: pio fifo 读 */
        0,                  /* DMA控制通道将填充这个域 */
        false               /* 暂时不启动DMA传输 */
    );
    
    dma_channel_start(ctrl_chan);  /* 启动DMA传输 */
}



回复

使用道具 举报

747

主题

1049

回帖

3295

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
3295
 楼主| 发表于 2022-3-26 00:42:44 | 显示全部楼层

因为GPIO中断会引起异常,暂时用软件方式判断先跳过去。结果定时器中断也异常,一步一步地填坑。
我的代码:
[C] 纯文本查看 复制代码
/*
*********************************************************************************************************
*    函 数 名: TrigCallback
*    功能说明: 启动硬件触发,并设置触发电平
*    形    参: _TrigVolt 触发电压,单位V
*    返 回 值: 无
*********************************************************************************************************
*/
void TrigCallback(uint gpio, uint32_t events)
{
    if (gpio_get(25) == 0)
    {
        gpio_put(25, 1); /* 点亮LED */
    }
    else
    {
        gpio_put(25, 0); /* 熄灭LED */        
    }


    /* 停止GPIO中断 */  
//    gpio_set_irq_enabled(PIN_TRIG, GPIO_IRQ_EDGE_RISE, false);
   
    // 立即读取触发位置
    dma_channel_hw_t *hw = dma_channel_hw_addr(g_tDSO.data_chan);
    g_tDSO.TrigPosDma = hw->write_addr;
    g_tDSO.Trigged = 2;
//   
    /* 计算触发结束时刻 */
    int64_t t0;
   
    t0 = 50000;   //


    add_alarm_in_ms(t0, TrigAlarmCallbak, 0, true);
}


alarm_callback_t TrigAlarmCallbak(alarm_id_t id, void *user_data)
{
    if (gpio_get(25) == 0)
    {
        gpio_put(25, 1); /* 点亮LED */
    }
    else
    {
        gpio_put(25, 0); /* 熄灭LED */        
    }
   
    //dma_channel_abort(g_tDSO.ctrl_chan);
    //dma_channel_abort(g_tDSO.data_chan);
   
    dma_hw->abort = (1u << g_tDSO.ctrl_chan) | (1u << g_tDSO.data_chan);
    while (dma_hw->abort & ((1ul << g_tDSO.ctrl_chan) | (1u << g_tDSO.data_chan))) ;
   
    bsp_StopDso();
}




开启定时器时产生异常,异常位置:
image.png
[C] 纯文本查看 复制代码
/*! \brief Acquire a spin lock without disabling interrupts (hence unsafe)
 *  \ingroup hardware_sync
 *
 * \param lock Spinlock instance
 */
__force_inline static void spin_lock_unsafe_blocking(spin_lock_t *lock) {
    // Note we don't do a wfe or anything, because by convention these spin_locks are VERY SHORT LIVED and NEVER BLOCK and run
    // with INTERRUPTS disabled (to ensure that)... therefore nothing on our core could be blocking us, so we just need to wait on another core
    // anyway which should be finished soon
    while (__builtin_expect(!*lock, 0));
    __mem_fence_acquire();
}


解释

1.引言
在很多源码如Linux内核、Glib等,我们都能看到likely()和unlikely()这两个宏,通常这两个宏定义是下面这样的形式。
#define likely(x)      __builtin_expect(!!(x), 1)#define unlikely(x)    __builtin_expect(!!(x), 0)
可以看出这2个宏都是使用函数 __builtin_expect()实现的, __builtin_expect()函数是GCC的一个内建函数(build-in function).

2. 函数声明函数__builtin_expect()是GCC v2.96版本引入的, 其声明如下:long __builtin_expect(long exp, long c);
2.1. 功能描述
由于大部分程序员在分支预测方面做得很糟糕,所以GCC 提供了这个内建函数来帮助程序员处理分支预测.
你期望 exp 表达式的值等于常量 c, c 的值, 如果 c 的值为0(即期望的函数返回值), 那么 执行 if 分支的的可能性小, 否则执行 else 分支的可能性小(函数的返回值等于第一个参数 exp).
GCC在编译过程中,会将可能性更大的代码紧跟着前面的代码,从而减少指令跳转带来的性能上的下降, 达到优化程序的目的.
通常,你也许会更喜欢使用 gcc 的一个参数 '-fprofile-arcs' 来收集程序运行的关于执行流程和分支走向的实际反馈信息,但是对于很多程序来说,数据是很难收集的。
2.2. 参数详解  ① exp
   exp 为一个整型表达式, 例如: (ptr != NULL)
   ② c
     c 必须是一个编译期常量, 不能使用变量
2.3. 返回值
  返回值等于 第一个参数 exp






回复

使用道具 举报

747

主题

1049

回帖

3295

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
3295
 楼主| 发表于 2022-3-26 01:34:08 | 显示全部楼层
硬汉  1:08:21
如果锁定了,树莓派论坛网友遇到了同样问题。与我们不同的是,他用的定时器中断,只要调试状态运行必定卡顿在lock函数里面,而将下载器拔掉,板子正常上电运行就可以,这个您有测试过没。
另外那个while大循环里面最好简单执行点东西,不让CPU闲置下来,还有就是优化等级,当前应该一直用的最高等级优化。

他还测试了官方的例子hello_timer就存在这种问题。
https://forums.raspberrypi.com/viewtopic.php?p=1984685
这个帖子是这个月发布的。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-4-20 21:27 , Processed in 0.206331 second(s), 27 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表