fmodf() 是不是会导致 stm32 出现硬故障?

Posted

技术标签:

【中文标题】fmodf() 是不是会导致 stm32 出现硬故障?【英文标题】:Does fmodf() cause a hardfault in stm32?fmodf() 是否会导致 stm32 出现硬故障? 【发布时间】:2021-01-29 21:44:31 【问题描述】:

我正在尝试用 2 个正弦波创建一个调制波形。 为此,我需要模(fmodf)来知道具有特定频率(lo_frequency)的正弦在那个时间(t)的幅度。但是当执行以下行时我遇到了一个硬错误:

j = fmodf(2 * PI * lo_frequency * t, 2 * PI);

你知道为什么这会给我一个硬故障吗?

编辑 1:

我用 my_fmodf 交换了 fmodf:

float my_fmodf(float x, float y)
    if(y == 0)
        return 0;
    
    float n = x / y;
    return x - n * y;

但仍然出现硬故障,当我调试它时,它甚至没有跳转到这个函数(my_fmodf)。

这是发生此错误的整个函数:

int* create_wave(int* message)
    /* Mixes the message signal at 10kHz and the carrier at 40kHz.
     * When a bit of the message is 0 the amplitude is lowered to 10%.
     * When a bit of the message is 1 the amplitude is 100%.
     * The output of the STM32 can't be negative, thats why the wave swings between
     * 0 and 256 (8bit precision for faster DAC)
     */
    static int rf_frequency = 10000;
    static int lo_frequency = 40000;
    static int sample_rate = 100000;
    int output[sample_rate];
    int index, mix;
    float j, t;
    for(int i = 0; i <= sample_rate; i++)
        t = i * 0.00000001f; // i * 10^-8
        j = my_fmodf(2 * PI * lo_frequency * t, 2 * PI);

        if (j < 0)
            j += (float) 2 * PI;
        
        index = floor((16.0f / (lo_frequency/rf_frequency * 0.0001f)) * t);
        if (index < 16) 
            if (!message[index]) 
                mix = 115 + sin1(j) * 0.1f;
             else 
                mix = sin1(j);
            
         else 
            break;
        
        output[i] = mix;
    
    return output;

编辑 2:

我修复了警告:函数以“chux - Reinstate Monica”建议的方式返回局部变量 [-Wreturn-local-addr] 的地址。

int* create_wave(int* message)
    static uint16_t rf_frequency = 10000;
    static uint32_t lo_frequency = 40000;
    static uint32_t sample_rate = 100000;
    int *output = malloc(sizeof *output * sample_rate);
    uint8_t index, mix;
    float j, n, t;
    for(int i = 0; i < sample_rate; i++)
        t = i * 0.00000001f; // i * 10^-8
        j = fmodf(2 * PI * lo_frequency * t, 2 * PI);
        if (j < 0)
            j += 2 * PI;
        
        index = floor((16.0f / (lo_frequency/rf_frequency * 0.0001f)) * t);
        if (index < 16) 
            if (!message[index]) 
                mix = (uint8_t) floor(115 + sin1(j) * 0.1f);
             else 
                mix = sin1(j);
            
         else 
            break;
        
        output[i] = mix;
    
    return output;

但现在我在这条线上遇到了硬故障:

output[i] = mix;

编辑 3:

因为之前的代码包含一个非常大的缓冲区数组,不适合 STM32F303K8 的 16KB SRAM,我需要对其进行更改。

现在我使用“乒乓”缓冲区,在其中我使用 DMA 的回调来进行“前半传输”和“完全传输”:

void HAL_DAC_ConvHalfCpltCallbackCh1(DAC_HandleTypeDef * hdac)
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);
    for(uint16_t i = 0; i < 128; i++)
        new_value = sin_table[(i * 8) % 256];
        if (message[message_index] == 0x0)
            dac_buf[i] = new_value * 0.1f + 115;
         else 
            dac_buf[i] = new_value;
        
    


void HAL_DAC_ConvCpltCallbackCh1 (DAC_HandleTypeDef * hdac)
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);
    for(uint16_t i = 128; i < 256; i++)
        new_value = sin_table[(i * 8) % 256];
        if (message[message_index] == 0x0)
            dac_buf[i] = new_value * 0.1f + 115;
         else 
            dac_buf[i] = new_value;
        
    
    message_index++;
    if (message_index >= 16) 
        message_index = 0;
        // HAL_DAC_Stop_DMA (&hdac1, DAC_CHANNEL_1);
    

它按照我想要的方式工作:

但是创建的正弦频率太低了。 我的上限在 20kHz 左右,但我需要 40kHz。 我已经将时钟增加了 8 倍,以使时钟达到最大值: 。 我仍然可以减少计数器周期(目前是 50),但是当我这样做时,中断回调似乎需要比下一个周期更长的时间。 至少看起来如此,因为当我这样做时输出变得非常失真。

我还尝试通过只取每 8 个正弦值来降低精度,但是 我不能再这样做了,因为输出看起来不再像正弦波了。

有什么想法可以优化回调以减少时间吗? 还有其他想法吗?

【问题讨论】:

你能发布为my_fmodf发出的程序集吗? in stm32 究竟是哪个 stm32? return output; 无效。你用的是什么编译器?对于初学者,启用所有编译器警告-Wall -Wextra -Werror 并修复它们。 STM32F303K8。我正在使用 MinGW。我正在使用 STM Cube IDE 进行调试。 感谢您提醒我“返回输出”无效,我仍然需要思考如何在来自 Java 和 Python 的 C 中返回数组等。 @le_lemon A C 函数无法返回 数组。它可以返回一个指向已分配内存的指针。它可以在struct 中返回一个数组。 【参考方案1】:

fmodf() 是否会导致 stm32 出现硬故障?

这里是其他代码问题导致硬故障。

编译失败并出现大量警告

最佳代码提示:启用所有警告。 @KamilCuk 比 *** 更快的反馈。

我希望在启用良好的编译器上出现如下所示的内容。

return output;
warning: function returns address of local variable [-Wreturn-local-addr]

返回本地对象

无法返回本地数组。改为分配。

// int output[sample_rate];
int *output = malloc(sizeof *output * sample_rate);
return output;

调用代码需要free()指针。

超出范围的数组访问

static int sample_rate = 100000;
int output[sample_rate];

// for(int i = 0; i <= sample_rate; i++)
for(int i = 0; i < sample_rate; i++)
    ...
    output[i] = mix;

堆栈溢出?

static int sample_rate = 100000; int output[sample_rate]; 是一个很大的局部变量。也许分配或尝试更小的东西?

高级:精度损失

一个好的fmodf() 不会丢失精度。要获得更精确的答案,请考虑 double math 以获得中间结果。更好的方法涉及更多。

float my_fmodf(float x, float y)
    if(y == 0)
        return 0;
    
    double n = 1.0 * x / y;
    return (float) (x - n * y);

我不能在另一个函数中使用任何函数吗?

是的。代码还有其他问题。

【讨论】:

我从来不需要手动分配,因为我以前只使用过 java 或 python。为什么需要这样做?为什么声明和初始化不足以让编译器知道它需要为这个变量分配空间?输出包含调制波的幅度,所以如果我减少样本数量,我会失去精度,所以如果可能的话,我想避免这种情况。 @le_lemon "为什么需要这样做?"听起来为什么C和其他语言不同?太宽泛。 C 假设编码器正在编写合理大小的代码就足够了。除了sample_rate 考虑之外,代码还有其他各种精度问题。所以只关注那个会隐藏其他的。 "return output" 是的,我可以看到那个警告,也许我应该先修复那个警告,但是因为错误发生在返回之前,我直到现在才担心。 @le_lemon "错误发生在返回之前..." --> 执行前解决所有警告的好习惯——节省时间。 好的,我按照您建议的方式修复了它(参见编辑 2)。我看不到任何其他警告。【参考方案2】:

每 10uS 1 个值只能产生 100kSPS,这对于这个宏来说并不算多。在我的设计中,我生成 > 5MSPS 的信号没有任何问题。通常我在循环模式下有一个缓冲区和 DMA。首先我填充缓冲区并开始生成。当触发半传输 DMA 中断时,我用新数据填充缓冲区的前半部分。传输完成中断被触发,我填充了后半部分,这个过程再次重复。

【讨论】:

谢谢,我已经在 ADC 这边做这个了,所以我会在星期一试试这个。

以上是关于fmodf() 是不是会导致 stm32 出现硬故障?的主要内容,如果未能解决你的问题,请参考以下文章

STM32被禁用SW口导致程序下载不了问题

STM32被禁用SW口导致程序下载不了问题

STM32被禁用SW口导致程序下载不了问题

stm32的spi低温下初始化失败

stm32数组越界一定会进硬件错误中断吗

STM32对memcpy的调用导致hardfault(对memcpy本身的调用,而不是memcpy的执行)