开启pio 50M 后台DMA采集程序后,触发GPIO中断时引起异常
发现新问题, 一晚上没解决。开启pio 50M 后台DMA采集程序后,触发中断后引起异常。中断程序也没执行。
不写 bsp_StartDso(DSO_MODE_CH1CH2, DSO_FREQ_50M, 8 * 1024); 这句话,中断正常响应(仅仅熄灭LED), 写了就触发异常。
pio程序启用不开dma是正常可执行GPIO中断。
只要开启DMA,当GPIO触发中断时,CPU异常停机。(已排除超频问题)
开启DMA的代码如下:双DMA循环触发
/*
*********************************************************************************************************
* 函 数 名: 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);
}
else if (_mode == DSO_MODE_CH1) /* 单通道交错 */
{
offset = pio_add_program(pio0, &dso_ch1_program);
pio_dso_ch1_init(pio, sm, offset, g_PioClkDiv);
}
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.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, /* 源地址: pio fifo 读 */
0, /* DMA控制通道将填充这个域 */
false /* 暂时不启动DMA传输 */
);
dma_channel_start(ctrl_chan);/* 启动DMA传输 */
}
因为GPIO中断会引起异常,暂时用软件方式判断先跳过去。结果定时器中断也异常,一步一步地填坑。
我的代码:
/*
*********************************************************************************************************
* 函 数 名: 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();
}
开启定时器时产生异常,异常位置:
/*! \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
硬汉1:08:21
如果锁定了,树莓派论坛网友遇到了同样问题。与我们不同的是,他用的定时器中断,只要调试状态运行必定卡顿在lock函数里面,而将下载器拔掉,板子正常上电运行就可以,这个您有测试过没。
另外那个while大循环里面最好简单执行点东西,不让CPU闲置下来,还有就是优化等级,当前应该一直用的最高等级优化。
他还测试了官方的例子hello_timer就存在这种问题。
https://forums.raspberrypi.com/viewtopic.php?p=1984685
这个帖子是这个月发布的。
页:
[1]