stm32之hal库i2s驱动的封装记录

前言

  1. 本次使用的框架为threadx+filex+shell组件

程序

共用驱动文件(dma和nvic)

nvic文件

头文件
/*
 * Copyright (c) 2024-2024,shchl
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2024-5-3     shchl   first version
 */

#ifndef TX_STM32_F4_DRV_NVIC_OS_H
#define TX_STM32_F4_DRV_NVIC_OS_H
#include "drv_common.h"

void stm32_nvic_common_enable(uint32_t instance,uint32_t preempt,uint32_t sub);
void stm32_nvic_common_disable(uint32_t instance);
uint8_t stm32_nvic_common_enabled_check(uint32_t instance);
#endif //TX_STM32_F4_DRV_NVIC_OS_H

源文件
/*
 * Copyright (c) 2024-2024,shchl
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2024-5-3     shchl   first version
 */

#include "drv_nvic_os.h"

enum stm32_irq_op_enum {
    OPEN_IRQn,
    CLOSE_IRQn,
    READ_IRQn
};

/**
 * @brief
 * @param instance
 * @param preempt
 * @param sub
 * @param open_flag
 */
static uint32_t stm32_nvic_common(uint32_t instance, uint32_t preempt, uint32_t sub, enum stm32_irq_op_enum mode) {
    uint32_t irq;
    switch (instance) {
#define irq_set(IRQn) {irq=IRQn;}break
        case (uint32_t) SPI1: irq_set(SPI1_IRQn);
        case (uint32_t) SPI2: irq_set(SPI2_IRQn);
        case (uint32_t) SPI3: irq_set(SPI3_IRQn);
        case (uint32_t) USART1: irq_set(USART1_IRQn);
        case (uint32_t) USART2: irq_set(USART2_IRQn);
        case (uint32_t) USART3: irq_set(USART3_IRQn);
        case (uint32_t) UART4: irq_set(UART4_IRQn);
        case (uint32_t) UART5: irq_set(UART5_IRQn);
        case (uint32_t) SDIO: irq_set(SDIO_IRQn);
        case (uint32_t) DMA2_Stream0: irq_set(DMA2_Stream0_IRQn);
        case (uint32_t) DMA2_Stream1: irq_set(DMA2_Stream1_IRQn);
        case (uint32_t) DMA2_Stream2: irq_set(DMA2_Stream2_IRQn);
        case (uint32_t) DMA2_Stream3: irq_set(DMA2_Stream3_IRQn);
        case (uint32_t) DMA2_Stream4: irq_set(DMA2_Stream4_IRQn);
        case (uint32_t) DMA2_Stream5: irq_set(DMA2_Stream5_IRQn);
        case (uint32_t) DMA2_Stream6: irq_set(DMA2_Stream6_IRQn);
        case (uint32_t) DMA2_Stream7: irq_set(DMA2_Stream7_IRQn);
        case (uint32_t) DMA1_Stream0: irq_set(DMA1_Stream0_IRQn);
        case (uint32_t) DMA1_Stream1: irq_set(DMA1_Stream1_IRQn);
        case (uint32_t) DMA1_Stream2: irq_set(DMA1_Stream2_IRQn);
        case (uint32_t) DMA1_Stream3: irq_set(DMA1_Stream3_IRQn);
        case (uint32_t) DMA1_Stream4: irq_set(DMA1_Stream4_IRQn);
        case (uint32_t) DMA1_Stream5: irq_set(DMA1_Stream5_IRQn);
        case (uint32_t) DMA1_Stream6: irq_set(DMA1_Stream6_IRQn);
        case (uint32_t) DMA1_Stream7: irq_set(DMA1_Stream7_IRQn);
        default: {
            return UINT32_MAX;
        }

    }
#undef  irq_set

    switch (mode) {

        case OPEN_IRQn: {
            HAL_NVIC_SetPriority(irq, preempt, sub);
            HAL_NVIC_EnableIRQ(irq);
        }
            break;
        case CLOSE_IRQn: {
            HAL_NVIC_DisableIRQ(irq);
        }
            break;
        default: {
            break;
        }
    }

    return irq;
}


/**
 * @brief 中断使能
 * @param instance
 * @param preempt
 * @param sub
 */
void stm32_nvic_common_enable(uint32_t instance, uint32_t preempt, uint32_t sub) {

    stm32_nvic_common(instance, preempt, sub, OPEN_IRQn);
}

void stm32_nvic_common_disable(uint32_t instance) {
    stm32_nvic_common(instance, 0, 0, CLOSE_IRQn);
}
/**
 * @brief nvic 启用状态检测
 * @param instance
 * @return 0 未启用,1 启用
 */
uint8_t stm32_nvic_common_enabled_check(uint32_t instance) {
    uint32_t irq = stm32_nvic_common(instance, 0, 0, READ_IRQn);
    return NVIC_GetEnableIRQ(irq);
}

DMA文件

头文件
/*
 * Copyright (c) 2024-2024,shchl
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2024-5-3     shchl   first version
 */

#ifndef TX_STM32_F4_DRV_DMA_OS_H
#define TX_STM32_F4_DRV_DMA_OS_H

#include "drv_common.h"

struct stm32_dma_info {
    uint32_t instance;
    DMA_HandleTypeDef *dma_tx;
    DMA_HandleTypeDef *dma_rx;
};

struct stm32_dma_info *stm32_dma_info_get(uint32_t instance);

void stm32_dma_clk_enable(const DMA_HandleTypeDef *handle);
void stm32_dma_clk_disable(const DMA_HandleTypeDef *handle);
#endif //TX_STM32_F4_DRV_DMA_OS_H

源文件
/*
 * Copyright (c) 2024-2024,shchl
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2024-5-3     shchl   first version
 */

#include "drv_dma_os.h"


#if SPI1_DMA
static DMA_HandleTypeDef spi1_dma_tx = {.Instance=DMA2_Stream3, .Init.Channel=DMA_CHANNEL_3};
static DMA_HandleTypeDef spi1_dma_rx = {.Instance=DMA2_Stream0, .Init.Channel=DMA_CHANNEL_3};
#endif

static DMA_HandleTypeDef sdio_dma_tx = {.Instance = DMA2_Stream6, .Init.Channel = DMA_CHANNEL_4};
static DMA_HandleTypeDef sdio_dma_rx = {.Instance = DMA2_Stream3, .Init.Channel = DMA_CHANNEL_4};
static DMA_HandleTypeDef i2s_spi2_dma_tx = {.Instance = DMA1_Stream4, .Init.Channel = DMA_CHANNEL_0};
static DMA_HandleTypeDef i2s_spi2_dma_rx = {.Instance = DMA1_Stream3, .Init.Channel = DMA_CHANNEL_3};

static struct stm32_dma_info dma_info_map[] = {
#if SPI1_DMA
        {(uint32_t) SPI1, &spi1_dma_tx, &spi1_dma_rx},
#endif
    {(uint32_t) SDIO, &sdio_dma_tx, &sdio_dma_rx},
    {(uint32_t) SPI2, &i2s_spi2_dma_tx,&i2s_spi2_dma_rx}

};
#define DMA_MAP_CNT ( sizeof(dma_info_map)/ sizeof(dma_info_map[0]))

struct stm32_dma_info *stm32_dma_info_get(uint32_t instance) {
    for (int i = 0; i < DMA_MAP_CNT; ++i) {
        if (dma_info_map[i].instance == instance) {
            return dma_info_map + i;
        }
    }
    return NULL;
}

void stm32_dma_clk_enable(const DMA_HandleTypeDef *handle) {
    switch ((uint32_t) handle->Instance) {
        case (uint32_t) DMA2_Stream0:
        case (uint32_t) DMA2_Stream1:
        case (uint32_t) DMA2_Stream2:
        case (uint32_t) DMA2_Stream3:
        case (uint32_t) DMA2_Stream4:
        case (uint32_t) DMA2_Stream5:
        case (uint32_t) DMA2_Stream6:
        case (uint32_t) DMA2_Stream7:
            __HAL_RCC_DMA2_CLK_ENABLE();
            break;
        case (uint32_t) DMA1_Stream0:
        case (uint32_t) DMA1_Stream1:
        case (uint32_t) DMA1_Stream2:
        case (uint32_t) DMA1_Stream3:
        case (uint32_t) DMA1_Stream4:
        case (uint32_t) DMA1_Stream5:
        case (uint32_t) DMA1_Stream6:
        case (uint32_t) DMA1_Stream7:
            __HAL_RCC_DMA1_CLK_ENABLE();
            break;
        default:

            return;
    }
}

void stm32_dma_clk_disable(const DMA_HandleTypeDef *handle) {
    switch ((uint32_t) handle->Instance) {
        case (uint32_t) DMA2_Stream0:
        case (uint32_t) DMA2_Stream1:
        case (uint32_t) DMA2_Stream2:
        case (uint32_t) DMA2_Stream3:
        case (uint32_t) DMA2_Stream4:
        case (uint32_t) DMA2_Stream5:
        case (uint32_t) DMA2_Stream6:
        case (uint32_t) DMA2_Stream7:
            __HAL_RCC_DMA2_CLK_DISABLE();
            break;
        case (uint32_t) DMA1_Stream0:
        case (uint32_t) DMA1_Stream1:
        case (uint32_t) DMA1_Stream2:
        case (uint32_t) DMA1_Stream3:
        case (uint32_t) DMA1_Stream4:
        case (uint32_t) DMA1_Stream5:
        case (uint32_t) DMA1_Stream6:
        case (uint32_t) DMA1_Stream7:
            __HAL_RCC_DMA1_CLK_DISABLE();
            break;
        default:

            return;
    }
}

#if SPI1_DMA
void DMA2_Stream0_IRQHandler(void) {
    TX_INTERRUPT_SAVE_AREA
    TX_DISABLE
    HAL_DMA_IRQHandler(&spi1_dma_rx);
    TX_RESTORE
}

void DMA2_Stream3_IRQHandler(void) {
    TX_INTERRUPT_SAVE_AREA
    TX_DISABLE
    HAL_DMA_IRQHandler(&spi1_dma_tx);
    TX_RESTORE
}
#endif


void DMA2_Stream3_IRQHandler(void) {
    TX_INTERRUPT_SAVE_AREA
    TX_DISABLE
    HAL_DMA_IRQHandler(&sdio_dma_rx);
    TX_RESTORE
}

void DMA2_Stream6_IRQHandler(void) {
    TX_INTERRUPT_SAVE_AREA
    TX_DISABLE
    HAL_DMA_IRQHandler(&sdio_dma_tx);
    TX_RESTORE
}


void DMA1_Stream4_IRQHandler(void) {
    HAL_DMA_IRQHandler(&i2s_spi2_dma_tx);
}
void DMA1_Stream3_IRQHandler(void) {
    HAL_DMA_IRQHandler(&i2s_spi2_dma_rx);
}

i2s底层驱动(使用的是双缓冲方式)

头文件

/*
 * Copyright (c) 2024-2024,shchl
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2024-5-6     shchl   first version
 */

#ifndef DRV_I2S_OS_H
#define DRV_I2S_OS_H
#include <drv_common.h>
#define I2S_AUDIO_INSTANCE SPI2
void bsp_I2sParSet(uint32_t sample_freq, uint16_t bps);
void bsp_I2sDmaEnable(uint8_t dma_tx_enable, uint8_t dma_rx_enable);
void bsp_I2sDmaParSet(DMA_InitTypeDef *tx_cfg, DMA_InitTypeDef *rx_cfg);

void bsp_I2sInit(void);
void i2s_dma_tx_trans_set(const uint8_t *m0, const uint8_t *m1, uint16_t size);

void i2s_play_start();
void i2s_play_stop();
void i2s_play_pause();
void i2s_play_resume();

void i2s_dma_rx_trans_set(const uint8_t *m0, const uint8_t *m1, uint16_t size);
void i2s_record_start();
void i2s_record_stop();
void i2s_record_pause();
void i2s_record_resume();
#endif //DRV_I2S_OS_H

源文件

/*
 * Copyright (c) 2024-2024,shchl
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2024-5-6     shchl   first version
 */

#include "drv_i2s_os.h"
#include <drv_dma_os.h>
#include <drv_nvic_os.h>

extern void i2s_dma_m0_cpltcallback(struct __DMA_HandleTypeDef *hdma) __attribute__((weak));

extern void i2s_dma_m1_cpltcallback(struct __DMA_HandleTypeDef *hdma) __attribute__((weak));

extern void i2s_dma_errcallback(struct __DMA_HandleTypeDef *hdma)__attribute__((weak));


static I2S_HandleTypeDef handle = {.Instance = I2S_AUDIO_INSTANCE};
#define _i2s_handle (handle)

/**
 *
 * @param sample_freq 采样频率(hz)
 * @param bps 数据长度
 */
void bsp_I2sParSet(const uint32_t sample_freq, uint16_t bps) {
    // 采样频率时钟设置
    RCC_PeriphCLKInitTypeDef RCCI2S2_ClkInitSture;
    uint32_t AudioFreq = I2S_AUDIOFREQ_44K;;
    if (_i2s_handle.Init.AudioFreq != sample_freq) {
        uint32_t PLLI2SN;
        uint32_t PLLI2SR;
#define SWITCH_SAMPLE_FREQ(sn,sr,freq)    {PLLI2SN = sn;PLLI2SR = sr;AudioFreq =freq;}break;
        // 参数配置
        switch (sample_freq) {
            case I2S_AUDIOFREQ_8K: SWITCH_SAMPLE_FREQ(256, 5, sample_freq)
            case I2S_AUDIOFREQ_11K: SWITCH_SAMPLE_FREQ(429, 4, sample_freq)
            case I2S_AUDIOFREQ_16K: SWITCH_SAMPLE_FREQ(213, 2, sample_freq)
            case I2S_AUDIOFREQ_22K: SWITCH_SAMPLE_FREQ(429, 4, sample_freq)
            case I2S_AUDIOFREQ_32K: SWITCH_SAMPLE_FREQ(213, 2, sample_freq)
            case I2S_AUDIOFREQ_44K: SWITCH_SAMPLE_FREQ(271, 2, sample_freq)
            case I2S_AUDIOFREQ_48K: SWITCH_SAMPLE_FREQ(258, 3, sample_freq)
            case I2S_AUDIOFREQ_96K: SWITCH_SAMPLE_FREQ(344, 2, sample_freq)
            case I2S_AUDIOFREQ_192K: SWITCH_SAMPLE_FREQ(393, 2, sample_freq)
            default: SWITCH_SAMPLE_FREQ(271, 2, 44100) // 使用44100
        }
#undef SWITCH_SAMPLE_FREQ
        RCCI2S2_ClkInitSture.PeriphClockSelection = RCC_PERIPHCLK_I2S; //外设时钟源选择
        RCCI2S2_ClkInitSture.PLLI2S.PLLI2SN = PLLI2SN; //设置PLLI2SN
        RCCI2S2_ClkInitSture.PLLI2S.PLLI2SR = PLLI2SR; //设置PLLI2SR
        HAL_RCCEx_PeriphCLKConfig(&RCCI2S2_ClkInitSture); //设置时钟,并启用
    }
    // i2s 配置
    {
        _i2s_handle.Instance = I2S_AUDIO_INSTANCE;
        _i2s_handle.Init.AudioFreq = AudioFreq; //IIS频率设置
        _i2s_handle.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE; //主时钟输出使能
        _i2s_handle.Init.ClockSource = I2S_CLOCK_PLL; //IIS时钟源为PLL
        switch (bps) {
            //("飞利浦标准,24位数据长度");
            case 24:

                _i2s_handle.Init.Mode = I2S_MODE_MASTER_TX; //IIS模式
                _i2s_handle.Init.Standard = I2S_STANDARD_PHILIPS; //IIS标准
                _i2s_handle.Init.CPOL = I2S_CPOL_LOW; //空闲状态时钟电平
                _i2s_handle.Init.DataFormat = I2S_DATAFORMAT_24B; //IIS数据长度
                break;
            case 32:
                //飞利浦标准,主机发送,时钟低电平有效,24位扩展帧长度
                _i2s_handle.Init.Mode = I2S_MODE_MASTER_TX;
                _i2s_handle.Init.Standard = I2S_STANDARD_PHILIPS;
                _i2s_handle.Init.CPOL = I2S_CPOL_LOW;
                _i2s_handle.Init.DataFormat = I2S_DATAFORMAT_32B;
                break;
            default:
                // 飞利浦标准,主机发送,时钟低电平有效,16位帧长度
                _i2s_handle.Init.Mode = I2S_MODE_MASTER_TX;
                _i2s_handle.Init.Standard = I2S_STANDARD_PHILIPS;
                _i2s_handle.Init.CPOL = I2S_CPOL_LOW;
                _i2s_handle.Init.DataFormat = I2S_DATAFORMAT_16B_EXTENDED;
                break;
        }
    }



}

void bsp_I2sDmaEnable(uint8_t dma_tx_enable, uint8_t dma_rx_enable) {
    struct stm32_dma_info *dma_info = stm32_dma_info_get((uint32_t) _i2s_handle.Instance);
    if (dma_tx_enable && dma_info->dma_tx) {
        __HAL_LINKDMA(&_i2s_handle, hdmatx, *(dma_info->dma_tx)); //将DMA与I2S联系起来
    }

    if (dma_rx_enable && dma_info->dma_rx) {
        __HAL_LINKDMA(&_i2s_handle, hdmarx, *(dma_info->dma_rx)); //将DMA与I2S联系起来
    }
}

/**
 * @brief dma 配置设置   使用此函数之前,必须调用 @ref bsp_I2sDmaEnable ;
 * @param tx_cfg   tx dma 配置
 * @param rx_cfg   rx dma 配置
 */
void bsp_I2sDmaParSet(DMA_InitTypeDef *tx_cfg, DMA_InitTypeDef *rx_cfg) {
    if (_i2s_handle.hdmatx) {
        if (tx_cfg) {
            // 统一在dma配置源文件中配置,不在此处进行配置
            _i2s_handle.hdmatx->Init.Direction = tx_cfg->Direction;
            _i2s_handle.hdmatx->Init.PeriphInc = tx_cfg->PeriphInc;
            _i2s_handle.hdmatx->Init.MemInc = tx_cfg->MemInc;
            _i2s_handle.hdmatx->Init.PeriphDataAlignment = tx_cfg->PeriphDataAlignment;
            _i2s_handle.hdmatx->Init.MemDataAlignment = tx_cfg->MemDataAlignment;
            _i2s_handle.hdmatx->Init.Mode = tx_cfg->Mode;
            _i2s_handle.hdmatx->Init.Priority = tx_cfg->Priority;
            _i2s_handle.hdmatx->Init.FIFOMode = tx_cfg->FIFOMode;
            _i2s_handle.hdmatx->Init.FIFOThreshold = tx_cfg->FIFOThreshold;
            _i2s_handle.hdmatx->Init.MemBurst = tx_cfg->MemBurst;
            _i2s_handle.hdmatx->Init.PeriphBurst = tx_cfg->PeriphBurst;
        } else {
            // 统一在dma配置源文件中配置,不在此处进行配置
            // hi2s_ptr->hdmatx->Instance = DMA1_Stream4; //DMA1数据流4
            // hi2s_ptr->hdmatx->Init.Channel = DMA_CHANNEL_0; //通道0
            _i2s_handle.hdmatx->Init.Direction = DMA_MEMORY_TO_PERIPH; //存储器到外设模式
            _i2s_handle.hdmatx->Init.PeriphInc = DMA_PINC_DISABLE; //外设非增量模式
            _i2s_handle.hdmatx->Init.MemInc = DMA_MINC_ENABLE; //存储器增量模式
            _i2s_handle.hdmatx->Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //外设数据长度:16位
            _i2s_handle.hdmatx->Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; //存储器数据长度:16位
            _i2s_handle.hdmatx->Init.Mode = DMA_CIRCULAR; //使用循环模式
            _i2s_handle.hdmatx->Init.Priority = DMA_PRIORITY_MEDIUM; //高优先级
            _i2s_handle.hdmatx->Init.FIFOMode = DMA_FIFOMODE_ENABLE; //使用FIFO
            _i2s_handle.hdmatx->Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; // 使用FIFO阈值完全配置
            _i2s_handle.hdmatx->Init.MemBurst = DMA_MBURST_INC8; //存储器单次突发传输
            _i2s_handle.hdmatx->Init.PeriphBurst = DMA_MBURST_INC8; //外设突发单次传输
        }
    }
    if (_i2s_handle.hdmarx) {
        if (rx_cfg) {
            _i2s_handle.hdmarx->Init.Direction = rx_cfg->Direction;
            _i2s_handle.hdmarx->Init.PeriphInc = rx_cfg->PeriphInc;
            _i2s_handle.hdmarx->Init.MemInc = rx_cfg->MemInc;
            _i2s_handle.hdmarx->Init.PeriphDataAlignment = rx_cfg->PeriphDataAlignment;
            _i2s_handle.hdmarx->Init.MemDataAlignment = rx_cfg->MemDataAlignment;
            _i2s_handle.hdmarx->Init.Mode = rx_cfg->Mode;
            _i2s_handle.hdmarx->Init.Priority = rx_cfg->Priority;
            _i2s_handle.hdmarx->Init.FIFOMode = rx_cfg->FIFOMode;
            _i2s_handle.hdmarx->Init.FIFOThreshold = rx_cfg->FIFOThreshold;
            _i2s_handle.hdmarx->Init.MemBurst = rx_cfg->MemBurst;
            _i2s_handle.hdmarx->Init.PeriphBurst = rx_cfg->PeriphBurst;
        } else {
            // todo 录音部分
            // _i2s_handle.hdmarx->Instance=DMA1_Stream3;                       		//DMA1数据流3
            // _i2s_handle.hdmarx->Init.Channel=DMA_CHANNEL_3;                  		//通道3
            _i2s_handle.hdmarx->Init.Direction = DMA_PERIPH_TO_MEMORY; //外设到存储器模式
            _i2s_handle.hdmarx->Init.PeriphInc = DMA_PINC_DISABLE; //外设非增量模式
            _i2s_handle.hdmarx->Init.MemInc = DMA_MINC_ENABLE; //存储器增量模式
            _i2s_handle.hdmarx->Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //外设数据长度:16位
            _i2s_handle.hdmarx->Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; //存储器数据长度:16位
            _i2s_handle.hdmatx->Init.Mode = DMA_CIRCULAR; //使用循环模式
            _i2s_handle.hdmatx->Init.Priority = DMA_PRIORITY_MEDIUM; //高优先级
            _i2s_handle.hdmatx->Init.FIFOMode = DMA_FIFOMODE_ENABLE; //使用FIFO
            _i2s_handle.hdmatx->Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; // 使用FIFO阈值完全配置
            _i2s_handle.hdmatx->Init.MemBurst = DMA_MBURST_INC8; //存储器单次突发传输
            _i2s_handle.hdmatx->Init.PeriphBurst = DMA_MBURST_INC8; //外设突发单次传输
        }
    }
}

void bsp_I2sInit(void) {
    // 初始化
    {
        HAL_I2S_Init(&_i2s_handle);
    }

}
void i2s_play_start() {
    // 判断是否初始化i2s
    DISABLE_INT();
    if (_i2s_handle.Instance) {
        __HAL_DMA_ENABLE(_i2s_handle.hdmatx); //打开dma
        /*使能相应标志位*/
        __HAL_I2S_ENABLE(&_i2s_handle); //使能I2S2
        /* Enable Tx DMA Request */
        __HAL_I2S_ENABLE_IT(&_i2s_handle, SPI_CR2_TXDMAEN); //SPI2 TX DMA请求使能.
    }
    ENABLE_INT();
}

void i2s_play_stop() {
    // 判断是否初始化,并只初始化一次
    if (_i2s_handle.Instance) {
        // 复位i2s,内部已经对相应的DMA进行复位,所以这里就不需要
        __HAL_I2S_DISABLE_IT(&_i2s_handle, SPI_CR2_TXDMAEN); //SPI2 TX DMA请求使能.
        HAL_I2S_DeInit(&_i2s_handle);
        _i2s_handle.Instance = NULL;
    }
}

void i2s_play_pause() {
    if ((_i2s_handle.hdmatx->Instance->CR & DMA_SxCR_EN)) {
        /* Disable the stream */
        __HAL_DMA_DISABLE(_i2s_handle.hdmatx);
        /* Check if the DMA Stream is effectively disabled */
        while ((_i2s_handle.hdmatx->Instance->CR & DMA_SxCR_EN) != RESET) {
        }
    }
}

void i2s_play_resume() {
    if ((_i2s_handle.hdmatx->Instance->CR & DMA_SxCR_EN) == RESET) {
        __HAL_DMA_ENABLE(_i2s_handle.hdmatx);
    }
}

/**
 * 用于播放
 * @param m0
 * @param m1
 * @param size
 */
void i2s_dma_tx_trans_set(const uint8_t *m0, const uint8_t *m1, uint16_t size) {
    if (_i2s_handle.hdmatx == NULL) {
        Error_Handler();
    }
    /*配置回调函数*/
    _i2s_handle.hdmatx->XferCpltCallback = i2s_dma_m0_cpltcallback;
    _i2s_handle.hdmatx->XferM1CpltCallback = i2s_dma_m1_cpltcallback;
    _i2s_handle.hdmatx->XferErrorCallback = i2s_dma_errcallback;
    /*开启dma双缓冲模式*/
    /* 开启双缓冲模式 */
    _i2s_handle.hdmatx->Instance->CR |= (uint32_t) DMA_SxCR_DBM;
    /* m1地址 */
    _i2s_handle.hdmatx->Instance->M1AR = (uint32_t) m1;
    /* m0地址 */
    _i2s_handle.hdmatx->Instance->M0AR = (uint32_t) m0;
    /* 传输数据长度 */
    _i2s_handle.hdmatx->Instance->NDTR = size;
    /* 目标地址 */
    _i2s_handle.hdmatx->Instance->PAR = (uint32_t) &(_i2s_handle.Instance->DR);
    /* 清除所有标志位 */
    __HAL_DMA_CLEAR_FLAG(_i2s_handle.hdmatx, __HAL_DMA_GET_TC_FLAG_INDEX( _i2s_handle.hdmatx));
    __HAL_DMA_CLEAR_FLAG(_i2s_handle.hdmatx, __HAL_DMA_GET_HT_FLAG_INDEX( _i2s_handle.hdmatx));
    __HAL_DMA_CLEAR_FLAG(_i2s_handle.hdmatx, __HAL_DMA_GET_TE_FLAG_INDEX( _i2s_handle.hdmatx));
    __HAL_DMA_CLEAR_FLAG(_i2s_handle.hdmatx, __HAL_DMA_GET_DME_FLAG_INDEX( _i2s_handle.hdmatx));
    __HAL_DMA_CLEAR_FLAG(_i2s_handle.hdmatx, __HAL_DMA_GET_FE_FLAG_INDEX( _i2s_handle.hdmatx));
    /* 使能中断标志位*/
    _i2s_handle.hdmatx->Instance->CR |= DMA_IT_TC | DMA_IT_TE | DMA_IT_DME;
    _i2s_handle.hdmatx->Instance->FCR |= DMA_IT_FE;
}


void i2s_dma_rx_trans_set(const uint8_t *m0, const uint8_t *m1, uint16_t size) {
    if (_i2s_handle.hdmarx == NULL) {
        Error_Handler();
    }
    /*配置回调函数*/
    _i2s_handle.hdmarx->XferCpltCallback = i2s_dma_m0_cpltcallback;
    _i2s_handle.hdmarx->XferM1CpltCallback = i2s_dma_m1_cpltcallback;
    _i2s_handle.hdmarx->XferErrorCallback = i2s_dma_errcallback;
    /*开启dma双缓冲模式*/
    /* 开启双缓冲模式 */
    _i2s_handle.hdmarx->Instance->CR |= (uint32_t) DMA_SxCR_DBM;
    /* m1地址 */
    _i2s_handle.hdmarx->Instance->M1AR = (uint32_t) m1;
    /* m0地址 */
    _i2s_handle.hdmarx->Instance->M0AR = (uint32_t) m0;
    /* 传输数据长度 */
    _i2s_handle.hdmarx->Instance->NDTR = size;
    /* 源地址 */
    _i2s_handle.hdmarx->Instance->PAR = (uint32_t) &(_i2s_handle.Instance->DR);
    /* 清除所有标志位 */
    __HAL_DMA_CLEAR_FLAG(_i2s_handle.hdmarx, __HAL_DMA_GET_TC_FLAG_INDEX( _i2s_handle.hdmarx));
    __HAL_DMA_CLEAR_FLAG(_i2s_handle.hdmarx, __HAL_DMA_GET_HT_FLAG_INDEX( _i2s_handle.hdmarx));
    __HAL_DMA_CLEAR_FLAG(_i2s_handle.hdmarx, __HAL_DMA_GET_TE_FLAG_INDEX( _i2s_handle.hdmarx));
    __HAL_DMA_CLEAR_FLAG(_i2s_handle.hdmarx, __HAL_DMA_GET_DME_FLAG_INDEX( _i2s_handle.hdmarx));
    __HAL_DMA_CLEAR_FLAG(_i2s_handle.hdmarx, __HAL_DMA_GET_FE_FLAG_INDEX( _i2s_handle.hdmarx));
    /* 使能中断标志位*/
    _i2s_handle.hdmarx->Instance->CR |= DMA_IT_TC | DMA_IT_TE | DMA_IT_DME;
    _i2s_handle.hdmarx->Instance->FCR |= DMA_IT_FE;
}

void i2s_record_start() {
    // 判断是否初始化i2s
    DISABLE_INT();
    if (_i2s_handle.Instance) {
        __HAL_DMA_ENABLE(_i2s_handle.hdmarx); //打开dma
        /*使能相应标志位*/
        __HAL_I2S_ENABLE(&_i2s_handle); //使能I2S2
        /* Enable Tx DMA Request */
        __HAL_I2S_ENABLE_IT(&_i2s_handle, SPI_CR2_RXDMAEN); //SPI2 TX DMA请求使能.
    }
    ENABLE_INT();
}

void i2s_record_stop() {
    // 判断是否初始化,并只初始化一次
    if (_i2s_handle.Instance) {
        // 复位i2s,内部已经对相应的DMA进行复位,所以这里就不需要
        __HAL_I2S_DISABLE_IT(&_i2s_handle, SPI_CR2_RXDMAEN); //SPI2 TX DMA请求使能.
        HAL_I2S_DeInit(&_i2s_handle);
        _i2s_handle.Instance = NULL;
    }
}

void i2s_record_pause() {
    if ((_i2s_handle.hdmarx->Instance->CR & DMA_SxCR_EN)) {
        /* Disable the stream */
        __HAL_DMA_DISABLE(_i2s_handle.hdmarx);
        /* Check if the DMA Stream is effectively disabled */
        while ((_i2s_handle.hdmarx->Instance->CR & DMA_SxCR_EN) != RESET) {
        }
    }
}

void i2s_record_resume() {
    if ((_i2s_handle.hdmarx->Instance->CR & DMA_SxCR_EN) == RESET) {
        __HAL_DMA_ENABLE(_i2s_handle.hdmarx);
    }
}

硬件io文件

/*
 * Copyright (c) 2024-2024,shchl
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2024-5-2     shchl   first version
 */
#include "bsp.h"

/**
  * @brief I2S MSP Init
  * @param  hi2s_ptr pointer to a I2S_HandleTypeDef structure that contains
  *         the configuration information for I2S module
  * @retval None
  */
void HAL_I2S_MspInit(I2S_HandleTypeDef *hi2s_ptr) {
    if (hi2s_ptr->Instance == SPI2) {
        /*I2S GPIO 初始化*/
        {
            GPIO_InitTypeDef GPIO_InitStruct;
            /* Peripheral clock enable */
            __HAL_RCC_SPI2_CLK_ENABLE();
            __HAL_RCC_GPIOC_CLK_ENABLE();
            __HAL_RCC_GPIOB_CLK_ENABLE();
            /**I2S2 GPIO Configuration
            PC2     ------> I2S2_ext_SD
            PC3     ------> I2S2_SD
            PB12     ------> I2S2_WS
            PB13     ------> I2S2_CK
            PC6     ------> I2S2_MCK
            */
            GPIO_InitStruct.Pin = GPIO_PIN_2;
            GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
            GPIO_InitStruct.Pull = GPIO_NOPULL;
            GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
            GPIO_InitStruct.Alternate = GPIO_AF6_I2S2ext;
            HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
            GPIO_InitStruct.Pin = GPIO_PIN_3 | GPIO_PIN_6;
            GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
            GPIO_InitStruct.Pull = GPIO_NOPULL;
            GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
            GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
            HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
            GPIO_InitStruct.Pin = GPIO_PIN_12 | GPIO_PIN_13;
            GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
            GPIO_InitStruct.Pull = GPIO_NOPULL;
            GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
            GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
            HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
        }
        /* I2S DMA发送配置 */
        if (hi2s_ptr->hdmatx) {
            stm32_dma_clk_enable(hi2s_ptr->hdmatx); //使能DMA时钟
            HAL_DMA_Init(hi2s_ptr->hdmatx); //初始化DMA
            stm32_nvic_common_enable((uint32_t) hi2s_ptr->hdmatx->Instance, 0, 0);
        };
    }
}


void HAL_I2S_MspDeInit(I2S_HandleTypeDef *handle_ptr) {
    if (handle_ptr->Instance == SPI2) {
        /**/
        /* Peripheral clock disable */
        __HAL_RCC_SPI2_CLK_DISABLE();
        /**I2S2 GPIO Configuration
          PC2     ------> I2S2_ext_SD
          PC3     ------> I2S2_SD
          PB12     ------> I2S2_WS
          PB13     ------> I2S2_CK
          PC6     ------> I2S2_MCK
        */
        HAL_GPIO_DeInit(GPIOC, GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_6);
        HAL_GPIO_DeInit(GPIOB, GPIO_PIN_10 | GPIO_PIN_12);
        if (handle_ptr->hdmatx) {
            /* I2S2 DMA DeInit */
            HAL_DMA_DeInit(handle_ptr->hdmatx);
            stm32_nvic_common_disable((uint32_t) handle_ptr->hdmatx->Instance);
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/597330.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

开式双比例泵控制放大器

比例泵PQ控制放大器的主要作用是通过接收来自控制器的信号&#xff0c;并将其转换为适当的电流信号&#xff0c;以驱动变量泵。这种控制方式可以实现对泵输出流量和压力的精确控制&#xff0c;从而实现节能和提高效率的目的。比例泵PQ控制放大器通常用于节能型泵控制系统中&…

【Linux系列】tail查询使用

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

【6D位姿估计】ZebraPose 层次化分组策略 由粗到细的表面编码

前言 本文介绍6D位姿估计的方法ZebraPose&#xff0c;也可以称为六自由度物体姿态估计&#xff0c;输入单张图片&#xff0c;输出物体的三维位置和三维方向。 它来自CVPR2022的论文&#xff0c;通过层次化分组策略&#xff0c;高效地编码物体表面的信息。 ZebraPose提出了一…

运维自动化之 ansible

目录 一 常见的自动化运维工具 &#xff08;一&#xff09;有哪些常见的 自动化运维工具 &#xff08;二&#xff09;为什么后面都变成用 ansible 二 ansible 基本介绍 1&#xff0c; ansible 是什么 2&#xff0c;ansible能干什么 3&#xff0c;ansible 工作原…

Linux网络—PXE高效批量网络装机

目录 一、部署PXE远程安装服务 1、搭建PXE远程安装服务器 1&#xff09;安装并启用 TFTP 服务 2&#xff09;安装并启用 DHCP 服务 3&#xff09;准备 Linux 内核、初始化镜像文件 4&#xff09;准备 PXE 引导程序 5&#xff09;安装FTP服务&#xff0c;准备CentOS 7 安…

OpenCV 入门(一) —— OpenCV 基础

OpenCV 入门系列&#xff1a; OpenCV 入门&#xff08;一&#xff09;—— OpenCV 基础 OpenCV 入门&#xff08;二&#xff09;—— 车牌定位 OpenCV 入门&#xff08;三&#xff09;—— 车牌筛选 OpenCV 入门&#xff08;四&#xff09;—— 车牌号识别 OpenCV 入门&#xf…

Springboot框架web开发实用功能-02

在些模块中汇总了一些web开发常用的配置和功能。 涉及的模块 springboot-common-config&#xff0c; 端口号&#xff1a;17000 Springboot框架web开发常用功能 Restful接口定义 查询参数 Data public class QueryParam {private String key;private String value; }Control…

MATLAB实现杜拉德公式和凯夫公式的计算固液混合料浆临界流速

MATLAB实现杜拉德公式和凯夫公式的计算固液混合料浆临界流速: 杜拉德公式是用来计算非均质固液混合料浆在输送管中的临界速度的公式&#xff0c;具体形式为&#xff1a; uL FL (2gD / (ρ0 - ρ1))^(1/2) 其中&#xff1a; uL&#xff1a;表示料浆的临界速度&#xff0c;…

Hbase 常用shell操作

目录 1、创建表 1.1、启动HBase Shell 1.2、创建表 1.3、查看表 1.4、删除表 2、插入数据 2.1、put命令 3、查看数据 3.1、get命令 3.2、查询数据中文显示 4、更新数据 4.1、使用put来更新数据 5、删除数据 5.1、delete命令 5.2、删除指定列的数据 5.3、delete…

Pycharm debug 运行报错 (RuntimeError: cannot release un-acquired lock)

问题描述&#xff1a; 最近再跑一个 flask应用&#xff0c;Pycharm 运行没问题&#xff0c;debug断点启动时报错 如下&#xff1a; 解决方案&#xff1a; 在环境变量中增加 GEVENT_SUPPORTTrue 启动成功&#xff01;

libcity笔记:添加新模型(以RNN.py为例)

创建的新模型应该继承AbstractModel或AbstractTrafficStateModel 交通状态预测任务——>继承 AbstractTrafficStateModel类轨迹位置预测任务——>继承AbstractModel类 1 AbstractTrafficStateModel 2 RNN 2.1 构造函数 2.2 predict 2.3 calculate_loss

博客系统项目测试报告

文章目录 一.报告概要二.测试环境三.手工测试用例四.编写测试用例五.自动化测试Selenium测试项目主要特点 一.报告概要 项目概要 本项目是一个全功能的个人博客系统&#xff0c;旨在提供一个用户友好、功能全面的平台&#xff0c;允许用户注册、登录、浏览博客、查看详细内容、…

Mac跑llama.cpp过程中遇到的问题

原repo 在华为手机上安装termux、下载库&#xff1a;顺利在电脑上安装Android NDK&#xff1a;先下载Android Studio&#xff0c;再在里面下载Android SDK 安装Android Studio时&#xff0c;SDK的某些组件总是下载不成功。后来关了梯子、改了hosts&#xff0c;重新安装就成功了…

Golang | Leetcode Golang题解之第73题矩阵置零

题目&#xff1a; 题解&#xff1a; func setZeroes(matrix [][]int) {n, m : len(matrix), len(matrix[0])col0 : falsefor _, r : range matrix {if r[0] 0 {col0 true}for j : 1; j < m; j {if r[j] 0 {r[0] 0matrix[0][j] 0}}}for i : n - 1; i > 0; i-- {for …

Go实现树莓派控制舵机

公式说明 毫秒&#xff08;ms&#xff09;是时间的单位&#xff0c;赫兹&#xff08;Hz&#xff09;是频率的单位&#xff0c;而DutyMax通常是一个PWM&#xff08;脉冲宽度调制&#xff09;信号中表示最大占空比的值。以下是它们之间的关系和一些相关公式&#xff1a; 频率&…

【华为】路由策略小实验

【华为】软考中级-路由策略实验 实验需求拓扑配置AR1AR2需求1需求2 AR3 检验 实验需求 1、让 R3 可以学到R1的 192.168.10.0/24和192.168.20.0/24的 路由&#xff0c;不能学到192.168.30.0/24。 2、让 R1可以学到 R3 的 172.16.20.0/24和172.16.30.0/24的路由&#xff0c;不能…

opencv图像处理详细讲(二)

联通组件分析 联通组件定义&#xff1a;像素值相同&#xff0c;通过四邻域或者八邻域相互连通的像素块。 换句话说&#xff0c;就是使用四邻域或八邻域的连通性&#xff0c;遍历图像的像素&#xff0c;并确定像素值相同并且连通的像素块&#xff0c;将它们标记为一个联通组件 两…

虚拟机VM VirtualBox安装openEuler+UKUI的安装和卸载_2024

虚拟机VM VirtualBox安装openEuler ps. 建议先看最后的其他 下载openEuler openEuler官网下载 一般来说标准版就够用了 使用虚拟机VM VirtualBox安装openEuler 新建虚拟机 修改用户名密码&#xff0c;建议修改&#xff0c;虽然之后还可以通过命令行修改&#xff08;注意密…

pyecharts绘制世界动态轨迹图(v0.5.X与v1.X版本对比)

一、问题引入 pyecharts官网&#xff1a;https://pyecharts.org/#/zh-cn/intro 在使用Geo或者GeoLines绘制动态轨迹图时&#xff0c;如果所选地区是中国的省份或者城市&#xff0c;是能够匹配到对应的经纬度并且正常绘制的&#xff1b;如果所选地区涉及到其他国家或者国外城市&…

Docker-harbor

一、搭建本地私有仓库 1.1 下载Registry镜像 1.2 添加本地私有仓库配置 1.3 重启服务并运行Registry容器 1.4.容器的操作 1.4.1 拉取Nginx镜像并为镜像打标签 1.4.2 上传到私有仓库 1.4.3 列出私有仓库所有镜像 1.4.4 列出私有仓库的镜像的所有标签 1.4.5 先删除原有…
最新文章