# firmware_test: работа с mqs
This commit is contained in:
parent
73b20d9f3e
commit
13cc7c6d0f
18 changed files with 1186 additions and 41 deletions
|
|
@ -20,6 +20,7 @@ add_subdirectory(usb_cdc)
|
|||
add_subdirectory(sdram)
|
||||
add_subdirectory(qspi_flash)
|
||||
add_subdirectory(sd)
|
||||
add_subdirectory(mqs)
|
||||
|
||||
# Подавляем предупреждения при компиляции собственных .c файлов библиотеки
|
||||
target_compile_options(bsp_board PRIVATE -w)
|
||||
|
|
|
|||
|
|
@ -15,4 +15,5 @@ target_include_directories(
|
|||
target_link_libraries(
|
||||
bsp_can
|
||||
PUBLIC bsp_status
|
||||
PRIVATE bsp_tick utils sdk_flexcan $<$<BOOL:${BUILD_TESTS_HOST}>:fff>)
|
||||
PRIVATE bsp_tick utils sdk_clock sdk_flexcan
|
||||
$<$<BOOL:${BUILD_TESTS_HOST}>:fff>)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "bsp/tick.h"
|
||||
#include "clock_config.h"
|
||||
#include "fsl_clock.h"
|
||||
#include "fsl_flexcan.h"
|
||||
#include "ring_buffer/ring_buffer.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -490,7 +490,7 @@
|
|||
<clock_output id="LPI2C_CLK_ROOT.outFreq" value="60 MHz" locked="false" accuracy=""/>
|
||||
<clock_output id="LPSPI_CLK_ROOT.outFreq" value="105.6 MHz" locked="false" accuracy=""/>
|
||||
<clock_output id="LVDS1_CLK.outFreq" value="1.2 GHz" locked="false" accuracy=""/>
|
||||
<clock_output id="MQS_MCLK.outFreq" value="1080/17 MHz" locked="false" accuracy=""/>
|
||||
<clock_output id="MQS_MCLK.outFreq" value="2.8224 MHz" locked="false" accuracy=""/>
|
||||
<clock_output id="PERCLK_CLK_ROOT.outFreq" value="75 MHz" locked="false" accuracy=""/>
|
||||
<clock_output id="PLL7_MAIN_CLK.outFreq" value="24 MHz" locked="false" accuracy=""/>
|
||||
<clock_output id="SAI1_CLK_ROOT.outFreq" value="1080/17 MHz" locked="false" accuracy=""/>
|
||||
|
|
@ -500,8 +500,8 @@
|
|||
<clock_output id="SAI2_CLK_ROOT.outFreq" value="1080/17 MHz" locked="false" accuracy=""/>
|
||||
<clock_output id="SAI2_MCLK1.outFreq" value="1080/17 MHz" locked="false" accuracy=""/>
|
||||
<clock_output id="SAI2_MCLK3.outFreq" value="30 MHz" locked="false" accuracy=""/>
|
||||
<clock_output id="SAI3_CLK_ROOT.outFreq" value="1080/17 MHz" locked="false" accuracy=""/>
|
||||
<clock_output id="SAI3_MCLK1.outFreq" value="1080/17 MHz" locked="false" accuracy=""/>
|
||||
<clock_output id="SAI3_CLK_ROOT.outFreq" value="11.2896 MHz" locked="false" accuracy=""/>
|
||||
<clock_output id="SAI3_MCLK1.outFreq" value="11.2896 MHz" locked="false" accuracy=""/>
|
||||
<clock_output id="SAI3_MCLK3.outFreq" value="30 MHz" locked="false" accuracy=""/>
|
||||
<clock_output id="SEMC_CLK_ROOT.outFreq" value="120 MHz" locked="false" accuracy=""/>
|
||||
<clock_output id="SPDIF0_CLK_ROOT.outFreq" value="30 MHz" locked="false" accuracy=""/>
|
||||
|
|
@ -519,6 +519,9 @@
|
|||
<setting id="CCM.LPSPI_PODF.scale" value="5" locked="false"/>
|
||||
<setting id="CCM.PERCLK_PODF.scale" value="2" locked="true"/>
|
||||
<setting id="CCM.PERIPH_CLK2_SEL.sel" value="XTALOSC24M.OSC_CLK" locked="false"/>
|
||||
<setting id="CCM.SAI3_CLK_PODF.scale" value="8" locked="true"/>
|
||||
<setting id="CCM.SAI3_CLK_PRED.scale" value="8" locked="true"/>
|
||||
<setting id="CCM.SAI3_CLK_SEL.sel" value="CCM_ANALOG.PLL4_MAIN_CLK" locked="false"/>
|
||||
<setting id="CCM.SEMC_PODF.scale" value="5" locked="true"/>
|
||||
<setting id="CCM.USDHC1_CLK_SEL.sel" value="CCM_ANALOG.PLL2_PFD0_CLK" locked="false"/>
|
||||
<setting id="CCM.USDHC1_PODF.scale" value="2" locked="true"/>
|
||||
|
|
@ -540,9 +543,17 @@
|
|||
<setting id="CCM_ANALOG.PLL3_PFD1_BYPASS.sel" value="CCM_ANALOG.PLL3_PFD1" locked="false"/>
|
||||
<setting id="CCM_ANALOG.PLL3_PFD2_BYPASS.sel" value="CCM_ANALOG.PLL3_PFD2" locked="false"/>
|
||||
<setting id="CCM_ANALOG.PLL3_PFD3_BYPASS.sel" value="CCM_ANALOG.PLL3_PFD3" locked="false"/>
|
||||
<setting id="CCM_ANALOG.PLL4.denom" value="625" locked="true"/>
|
||||
<setting id="CCM_ANALOG.PLL4.div" value="30" locked="true"/>
|
||||
<setting id="CCM_ANALOG.PLL4.num" value="66" locked="true"/>
|
||||
<setting id="CCM_ANALOG.PLL4_BYPASS.sel" value="CCM_ANALOG.PLL4_POST_DIV" locked="false"/>
|
||||
<setting id="CCM_ANALOG.PLL4_POST_DIV.scale" value="1" locked="true"/>
|
||||
<setting id="CCM_ANALOG_PLL_AUDIO_POWERDOWN_CFG" value="No" locked="false"/>
|
||||
<setting id="CCM_ANALOG_PLL_USB1_EN_USB_CLKS_CFG" value="Enabled" locked="false"/>
|
||||
<setting id="CCM_ANALOG_PLL_USB1_EN_USB_CLKS_OUT_CFG" value="Enabled" locked="false"/>
|
||||
<setting id="CCM_ANALOG_PLL_USB1_POWER_CFG" value="Yes" locked="false"/>
|
||||
<setting id="IOMUXC_GPR.MQS_CLK_DIV.scale" value="4" locked="true"/>
|
||||
<setting id="IOMUXC_GPR_GPR2_MQS_OVERSAMPLE" value="OverSampleRate64" locked="false"/>
|
||||
</clock_settings>
|
||||
<called_from_default_init>true</called_from_default_init>
|
||||
</clock_configuration>
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ outputs:
|
|||
- {id: LPI2C_CLK_ROOT.outFreq, value: 60 MHz}
|
||||
- {id: LPSPI_CLK_ROOT.outFreq, value: 105.6 MHz}
|
||||
- {id: LVDS1_CLK.outFreq, value: 1.2 GHz}
|
||||
- {id: MQS_MCLK.outFreq, value: 1080/17 MHz}
|
||||
- {id: MQS_MCLK.outFreq, value: 2.8224 MHz}
|
||||
- {id: PERCLK_CLK_ROOT.outFreq, value: 75 MHz}
|
||||
- {id: PLL7_MAIN_CLK.outFreq, value: 24 MHz}
|
||||
- {id: SAI1_CLK_ROOT.outFreq, value: 1080/17 MHz}
|
||||
|
|
@ -84,8 +84,8 @@ outputs:
|
|||
- {id: SAI2_CLK_ROOT.outFreq, value: 1080/17 MHz}
|
||||
- {id: SAI2_MCLK1.outFreq, value: 1080/17 MHz}
|
||||
- {id: SAI2_MCLK3.outFreq, value: 30 MHz}
|
||||
- {id: SAI3_CLK_ROOT.outFreq, value: 1080/17 MHz}
|
||||
- {id: SAI3_MCLK1.outFreq, value: 1080/17 MHz}
|
||||
- {id: SAI3_CLK_ROOT.outFreq, value: 11.2896 MHz}
|
||||
- {id: SAI3_MCLK1.outFreq, value: 11.2896 MHz}
|
||||
- {id: SAI3_MCLK3.outFreq, value: 30 MHz}
|
||||
- {id: SEMC_CLK_ROOT.outFreq, value: 120 MHz}
|
||||
- {id: SPDIF0_CLK_ROOT.outFreq, value: 30 MHz}
|
||||
|
|
@ -102,6 +102,9 @@ settings:
|
|||
- {id: CCM.LPSPI_PODF.scale, value: '5'}
|
||||
- {id: CCM.PERCLK_PODF.scale, value: '2', locked: true}
|
||||
- {id: CCM.PERIPH_CLK2_SEL.sel, value: XTALOSC24M.OSC_CLK}
|
||||
- {id: CCM.SAI3_CLK_PODF.scale, value: '8', locked: true}
|
||||
- {id: CCM.SAI3_CLK_PRED.scale, value: '8', locked: true}
|
||||
- {id: CCM.SAI3_CLK_SEL.sel, value: CCM_ANALOG.PLL4_MAIN_CLK}
|
||||
- {id: CCM.SEMC_PODF.scale, value: '5', locked: true}
|
||||
- {id: CCM.USDHC1_CLK_SEL.sel, value: CCM_ANALOG.PLL2_PFD0_CLK}
|
||||
- {id: CCM.USDHC1_PODF.scale, value: '2', locked: true}
|
||||
|
|
@ -123,9 +126,17 @@ settings:
|
|||
- {id: CCM_ANALOG.PLL3_PFD1_BYPASS.sel, value: CCM_ANALOG.PLL3_PFD1}
|
||||
- {id: CCM_ANALOG.PLL3_PFD2_BYPASS.sel, value: CCM_ANALOG.PLL3_PFD2}
|
||||
- {id: CCM_ANALOG.PLL3_PFD3_BYPASS.sel, value: CCM_ANALOG.PLL3_PFD3}
|
||||
- {id: CCM_ANALOG.PLL4.denom, value: '625', locked: true}
|
||||
- {id: CCM_ANALOG.PLL4.div, value: '30', locked: true}
|
||||
- {id: CCM_ANALOG.PLL4.num, value: '66', locked: true}
|
||||
- {id: CCM_ANALOG.PLL4_BYPASS.sel, value: CCM_ANALOG.PLL4_POST_DIV}
|
||||
- {id: CCM_ANALOG.PLL4_POST_DIV.scale, value: '1', locked: true}
|
||||
- {id: CCM_ANALOG_PLL_AUDIO_POWERDOWN_CFG, value: 'No'}
|
||||
- {id: CCM_ANALOG_PLL_USB1_EN_USB_CLKS_CFG, value: Enabled}
|
||||
- {id: CCM_ANALOG_PLL_USB1_EN_USB_CLKS_OUT_CFG, value: Enabled}
|
||||
- {id: CCM_ANALOG_PLL_USB1_POWER_CFG, value: 'Yes'}
|
||||
- {id: IOMUXC_GPR.MQS_CLK_DIV.scale, value: '4', locked: true}
|
||||
- {id: IOMUXC_GPR_GPR2_MQS_OVERSAMPLE, value: OverSampleRate64}
|
||||
sources:
|
||||
- {id: XTALOSC24M.RTC_OSC.outFreq, value: 32.768 kHz, enabled: true}
|
||||
* BE CAREFUL MODIFYING THIS COMMENT - IT IS YAML SETTINGS FOR TOOLS **********/
|
||||
|
|
@ -150,6 +161,14 @@ const clock_usb_pll_config_t usb1PllConfig_BOARD_BootClockRUN =
|
|||
.loopDivider = 0, /* PLL loop divider, Fout = Fin * 20 */
|
||||
.src = 0, /* Bypass clock source, 0 - OSC 24M, 1 - CLK1_P and CLK1_N */
|
||||
};
|
||||
const clock_audio_pll_config_t audioPllConfig_BOARD_BootClockRUN =
|
||||
{
|
||||
.loopDivider = 30, /* PLL loop divider, Fout = Fin * ( loopDivider + numerator / denominator ) */
|
||||
.postDivider = 1, /* Divider after PLL */
|
||||
.numerator = 66, /* 30 bit numerator of fractional loop divider, Fout = Fin * ( loopDivider + numerator / denominator ) */
|
||||
.denominator = 625, /* 30 bit denominator of fractional loop divider, Fout = Fin * ( loopDivider + numerator / denominator ) */
|
||||
.src = 0, /* Bypass clock source, 0 - OSC 24M, 1 - CLK1_P and CLK1_N */
|
||||
};
|
||||
const clock_enet_pll_config_t enetPllConfig_BOARD_BootClockRUN =
|
||||
{
|
||||
.enableClkOutput = true, /* Enable the PLL providing the ENET 125MHz reference clock */
|
||||
|
|
@ -285,11 +304,11 @@ void BOARD_BootClockRUN(void)
|
|||
/* Disable SAI3 clock gate. */
|
||||
CLOCK_DisableClock(kCLOCK_Sai3);
|
||||
/* Set SAI3_CLK_PRED. */
|
||||
CLOCK_SetDiv(kCLOCK_Sai3PreDiv, 3);
|
||||
CLOCK_SetDiv(kCLOCK_Sai3PreDiv, 7);
|
||||
/* Set SAI3_CLK_PODF. */
|
||||
CLOCK_SetDiv(kCLOCK_Sai3Div, 1);
|
||||
CLOCK_SetDiv(kCLOCK_Sai3Div, 7);
|
||||
/* Set Sai3 clock source. */
|
||||
CLOCK_SetMux(kCLOCK_Sai3Mux, 0);
|
||||
CLOCK_SetMux(kCLOCK_Sai3Mux, 2);
|
||||
/* Disable Lpi2c clock gate. */
|
||||
CLOCK_DisableClock(kCLOCK_Lpi2c1);
|
||||
CLOCK_DisableClock(kCLOCK_Lpi2c2);
|
||||
|
|
@ -389,15 +408,26 @@ void BOARD_BootClockRUN(void)
|
|||
/* Init Usb1 pfd3. */
|
||||
CLOCK_InitUsb1Pfd(kCLOCK_Pfd3, 19);
|
||||
#endif
|
||||
/* DeInit Audio PLL. */
|
||||
CLOCK_DeinitAudioPll();
|
||||
/* Bypass Audio PLL. */
|
||||
CLOCK_SetPllBypass(CCM_ANALOG, kCLOCK_PllAudio, 1);
|
||||
/* Set divider for Audio PLL. */
|
||||
/* Init Audio PLL. */
|
||||
uint32_t pllAudio;
|
||||
/* Disable Audio PLL output before initial Audio PLL. */
|
||||
CCM_ANALOG->PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_ENABLE_MASK;
|
||||
/* Bypass PLL first */
|
||||
CCM_ANALOG->PLL_AUDIO = (CCM_ANALOG->PLL_AUDIO & (~CCM_ANALOG_PLL_AUDIO_BYPASS_CLK_SRC_MASK)) |
|
||||
CCM_ANALOG_PLL_AUDIO_BYPASS_MASK | CCM_ANALOG_PLL_AUDIO_BYPASS_CLK_SRC(0);
|
||||
CCM_ANALOG->PLL_AUDIO_NUM = CCM_ANALOG_PLL_AUDIO_NUM_A(66);
|
||||
CCM_ANALOG->PLL_AUDIO_DENOM = CCM_ANALOG_PLL_AUDIO_DENOM_B(625);
|
||||
pllAudio = (CCM_ANALOG->PLL_AUDIO & (~(CCM_ANALOG_PLL_AUDIO_DIV_SELECT_MASK | CCM_ANALOG_PLL_AUDIO_POWERDOWN_MASK))) |
|
||||
CCM_ANALOG_PLL_AUDIO_ENABLE_MASK | CCM_ANALOG_PLL_AUDIO_DIV_SELECT(30);
|
||||
pllAudio |= CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2);
|
||||
CCM_ANALOG->MISC2 &= ~CCM_ANALOG_MISC2_AUDIO_DIV_LSB_MASK;
|
||||
CCM_ANALOG->MISC2 &= ~CCM_ANALOG_MISC2_AUDIO_DIV_MSB_MASK;
|
||||
/* Enable Audio PLL output. */
|
||||
CCM_ANALOG->PLL_AUDIO |= CCM_ANALOG_PLL_AUDIO_ENABLE_MASK;
|
||||
CCM_ANALOG->PLL_AUDIO = pllAudio;
|
||||
while ((CCM_ANALOG->PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_LOCK_MASK) == 0)
|
||||
{
|
||||
}
|
||||
/* Disable bypass for Audio PLL. */
|
||||
CLOCK_SetPllBypass(CCM_ANALOG, kCLOCK_PllAudio, 0);
|
||||
/* DeInit Video PLL. */
|
||||
CLOCK_DeinitVideoPll();
|
||||
/* Bypass Video PLL. */
|
||||
|
|
@ -451,7 +481,7 @@ void BOARD_BootClockRUN(void)
|
|||
/* Set SAI3 MCLK3 clock source. */
|
||||
IOMUXC_SetSaiMClkClockSource(IOMUXC_GPR, kIOMUXC_GPR_SAI3MClk3Sel, 0);
|
||||
/* Set MQS configuration. */
|
||||
IOMUXC_MQSConfig(IOMUXC_GPR,kIOMUXC_MqsPwmOverSampleRate32, 0);
|
||||
IOMUXC_MQSConfig(IOMUXC_GPR,kIOMUXC_MqsPwmOverSampleRate64, 3);
|
||||
/* Set ENET Ref clock source. */
|
||||
#if defined(IOMUXC_GPR_GPR1_ENET_REF_CLK_DIR_MASK)
|
||||
IOMUXC_GPR->GPR1 &= ~IOMUXC_GPR_GPR1_ENET_REF_CLK_DIR_MASK;
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ void BOARD_InitBootClocks(void);
|
|||
#define BOARD_BOOTCLOCKRUN_LPI2C_CLK_ROOT 60000000UL /* Clock consumers of LPI2C_CLK_ROOT output : LPI2C1, LPI2C2, LPI2C3, LPI2C4 */
|
||||
#define BOARD_BOOTCLOCKRUN_LPSPI_CLK_ROOT 105600000UL /* Clock consumers of LPSPI_CLK_ROOT output : LPSPI1, LPSPI2, LPSPI3, LPSPI4 */
|
||||
#define BOARD_BOOTCLOCKRUN_LVDS1_CLK 1200000000UL /* Clock consumers of LVDS1_CLK output : N/A */
|
||||
#define BOARD_BOOTCLOCKRUN_MQS_MCLK 63529411UL /* Clock consumers of MQS_MCLK output : N/A */
|
||||
#define BOARD_BOOTCLOCKRUN_MQS_MCLK 2822400UL /* Clock consumers of MQS_MCLK output : N/A */
|
||||
#define BOARD_BOOTCLOCKRUN_PERCLK_CLK_ROOT 75000000UL /* Clock consumers of PERCLK_CLK_ROOT output : GPT1, GPT2, PIT */
|
||||
#define BOARD_BOOTCLOCKRUN_PLL7_MAIN_CLK 24000000UL /* Clock consumers of PLL7_MAIN_CLK output : N/A */
|
||||
#define BOARD_BOOTCLOCKRUN_SAI1_CLK_ROOT 63529411UL /* Clock consumers of SAI1_CLK_ROOT output : N/A */
|
||||
|
|
@ -76,8 +76,8 @@ void BOARD_InitBootClocks(void);
|
|||
#define BOARD_BOOTCLOCKRUN_SAI2_MCLK1 63529411UL /* Clock consumers of SAI2_MCLK1 output : SAI2 */
|
||||
#define BOARD_BOOTCLOCKRUN_SAI2_MCLK2 0UL /* Clock consumers of SAI2_MCLK2 output : SAI2 */
|
||||
#define BOARD_BOOTCLOCKRUN_SAI2_MCLK3 30000000UL /* Clock consumers of SAI2_MCLK3 output : SAI2 */
|
||||
#define BOARD_BOOTCLOCKRUN_SAI3_CLK_ROOT 63529411UL /* Clock consumers of SAI3_CLK_ROOT output : N/A */
|
||||
#define BOARD_BOOTCLOCKRUN_SAI3_MCLK1 63529411UL /* Clock consumers of SAI3_MCLK1 output : SAI3 */
|
||||
#define BOARD_BOOTCLOCKRUN_SAI3_CLK_ROOT 11289600UL /* Clock consumers of SAI3_CLK_ROOT output : N/A */
|
||||
#define BOARD_BOOTCLOCKRUN_SAI3_MCLK1 11289600UL /* Clock consumers of SAI3_MCLK1 output : SAI3 */
|
||||
#define BOARD_BOOTCLOCKRUN_SAI3_MCLK2 0UL /* Clock consumers of SAI3_MCLK2 output : SAI3 */
|
||||
#define BOARD_BOOTCLOCKRUN_SAI3_MCLK3 30000000UL /* Clock consumers of SAI3_MCLK3 output : SAI3 */
|
||||
#define BOARD_BOOTCLOCKRUN_SEMC_CLK_ROOT 120000000UL /* Clock consumers of SEMC_CLK_ROOT output : SEMC */
|
||||
|
|
@ -99,6 +99,9 @@ extern const clock_usb_pll_config_t usb1PllConfig_BOARD_BootClockRUN;
|
|||
/*! @brief Sys PLL for BOARD_BootClockRUN configuration.
|
||||
*/
|
||||
extern const clock_sys_pll_config_t sysPllConfig_BOARD_BootClockRUN;
|
||||
/*! @brief Audio PLL set for BOARD_BootClockRUN configuration.
|
||||
*/
|
||||
extern const clock_audio_pll_config_t audioPllConfig_BOARD_BootClockRUN;
|
||||
/*! @brief Enet PLL set for BOARD_BootClockRUN configuration.
|
||||
*/
|
||||
extern const clock_enet_pll_config_t enetPllConfig_BOARD_BootClockRUN;
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ outputs:
|
|||
- {id: LPI2C_CLK_ROOT.outFreq, value: 60 MHz}
|
||||
- {id: LPSPI_CLK_ROOT.outFreq, value: 105.6 MHz}
|
||||
- {id: LVDS1_CLK.outFreq, value: 1.2 GHz}
|
||||
- {id: MQS_MCLK.outFreq, value: 1080/17 MHz}
|
||||
- {id: MQS_MCLK.outFreq, value: 2.8224 MHz}
|
||||
- {id: PERCLK_CLK_ROOT.outFreq, value: 75 MHz}
|
||||
- {id: PLL7_MAIN_CLK.outFreq, value: 24 MHz}
|
||||
- {id: SAI1_CLK_ROOT.outFreq, value: 1080/17 MHz}
|
||||
|
|
@ -84,8 +84,8 @@ outputs:
|
|||
- {id: SAI2_CLK_ROOT.outFreq, value: 1080/17 MHz}
|
||||
- {id: SAI2_MCLK1.outFreq, value: 1080/17 MHz}
|
||||
- {id: SAI2_MCLK3.outFreq, value: 30 MHz}
|
||||
- {id: SAI3_CLK_ROOT.outFreq, value: 1080/17 MHz}
|
||||
- {id: SAI3_MCLK1.outFreq, value: 1080/17 MHz}
|
||||
- {id: SAI3_CLK_ROOT.outFreq, value: 11.2896 MHz}
|
||||
- {id: SAI3_MCLK1.outFreq, value: 11.2896 MHz}
|
||||
- {id: SAI3_MCLK3.outFreq, value: 30 MHz}
|
||||
- {id: SEMC_CLK_ROOT.outFreq, value: 120 MHz}
|
||||
- {id: SPDIF0_CLK_ROOT.outFreq, value: 30 MHz}
|
||||
|
|
@ -102,6 +102,9 @@ settings:
|
|||
- {id: CCM.LPSPI_PODF.scale, value: '5'}
|
||||
- {id: CCM.PERCLK_PODF.scale, value: '2', locked: true}
|
||||
- {id: CCM.PERIPH_CLK2_SEL.sel, value: XTALOSC24M.OSC_CLK}
|
||||
- {id: CCM.SAI3_CLK_PODF.scale, value: '8', locked: true}
|
||||
- {id: CCM.SAI3_CLK_PRED.scale, value: '8', locked: true}
|
||||
- {id: CCM.SAI3_CLK_SEL.sel, value: CCM_ANALOG.PLL4_MAIN_CLK}
|
||||
- {id: CCM.SEMC_PODF.scale, value: '5', locked: true}
|
||||
- {id: CCM.USDHC1_CLK_SEL.sel, value: CCM_ANALOG.PLL2_PFD0_CLK}
|
||||
- {id: CCM.USDHC1_PODF.scale, value: '2', locked: true}
|
||||
|
|
@ -123,9 +126,17 @@ settings:
|
|||
- {id: CCM_ANALOG.PLL3_PFD1_BYPASS.sel, value: CCM_ANALOG.PLL3_PFD1}
|
||||
- {id: CCM_ANALOG.PLL3_PFD2_BYPASS.sel, value: CCM_ANALOG.PLL3_PFD2}
|
||||
- {id: CCM_ANALOG.PLL3_PFD3_BYPASS.sel, value: CCM_ANALOG.PLL3_PFD3}
|
||||
- {id: CCM_ANALOG.PLL4.denom, value: '625', locked: true}
|
||||
- {id: CCM_ANALOG.PLL4.div, value: '30', locked: true}
|
||||
- {id: CCM_ANALOG.PLL4.num, value: '66', locked: true}
|
||||
- {id: CCM_ANALOG.PLL4_BYPASS.sel, value: CCM_ANALOG.PLL4_POST_DIV}
|
||||
- {id: CCM_ANALOG.PLL4_POST_DIV.scale, value: '1', locked: true}
|
||||
- {id: CCM_ANALOG_PLL_AUDIO_POWERDOWN_CFG, value: 'No'}
|
||||
- {id: CCM_ANALOG_PLL_USB1_EN_USB_CLKS_CFG, value: Enabled}
|
||||
- {id: CCM_ANALOG_PLL_USB1_EN_USB_CLKS_OUT_CFG, value: Enabled}
|
||||
- {id: CCM_ANALOG_PLL_USB1_POWER_CFG, value: 'Yes'}
|
||||
- {id: IOMUXC_GPR.MQS_CLK_DIV.scale, value: '4', locked: true}
|
||||
- {id: IOMUXC_GPR_GPR2_MQS_OVERSAMPLE, value: OverSampleRate64}
|
||||
sources:
|
||||
- {id: XTALOSC24M.RTC_OSC.outFreq, value: 32.768 kHz, enabled: true}
|
||||
* BE CAREFUL MODIFYING THIS COMMENT - IT IS YAML SETTINGS FOR TOOLS **********/
|
||||
|
|
@ -150,6 +161,14 @@ const clock_usb_pll_config_t usb1PllConfig_BOARD_BootClockRUN =
|
|||
.loopDivider = 0, /* PLL loop divider, Fout = Fin * 20 */
|
||||
.src = 0, /* Bypass clock source, 0 - OSC 24M, 1 - CLK1_P and CLK1_N */
|
||||
};
|
||||
const clock_audio_pll_config_t audioPllConfig_BOARD_BootClockRUN =
|
||||
{
|
||||
.loopDivider = 30, /* PLL loop divider, Fout = Fin * ( loopDivider + numerator / denominator ) */
|
||||
.postDivider = 1, /* Divider after PLL */
|
||||
.numerator = 66, /* 30 bit numerator of fractional loop divider, Fout = Fin * ( loopDivider + numerator / denominator ) */
|
||||
.denominator = 625, /* 30 bit denominator of fractional loop divider, Fout = Fin * ( loopDivider + numerator / denominator ) */
|
||||
.src = 0, /* Bypass clock source, 0 - OSC 24M, 1 - CLK1_P and CLK1_N */
|
||||
};
|
||||
const clock_enet_pll_config_t enetPllConfig_BOARD_BootClockRUN =
|
||||
{
|
||||
.enableClkOutput = true, /* Enable the PLL providing the ENET 125MHz reference clock */
|
||||
|
|
@ -285,11 +304,11 @@ void BOARD_BootClockRUN(void)
|
|||
/* Disable SAI3 clock gate. */
|
||||
CLOCK_DisableClock(kCLOCK_Sai3);
|
||||
/* Set SAI3_CLK_PRED. */
|
||||
CLOCK_SetDiv(kCLOCK_Sai3PreDiv, 3);
|
||||
CLOCK_SetDiv(kCLOCK_Sai3PreDiv, 7);
|
||||
/* Set SAI3_CLK_PODF. */
|
||||
CLOCK_SetDiv(kCLOCK_Sai3Div, 1);
|
||||
CLOCK_SetDiv(kCLOCK_Sai3Div, 7);
|
||||
/* Set Sai3 clock source. */
|
||||
CLOCK_SetMux(kCLOCK_Sai3Mux, 0);
|
||||
CLOCK_SetMux(kCLOCK_Sai3Mux, 2);
|
||||
/* Disable Lpi2c clock gate. */
|
||||
CLOCK_DisableClock(kCLOCK_Lpi2c1);
|
||||
CLOCK_DisableClock(kCLOCK_Lpi2c2);
|
||||
|
|
@ -389,15 +408,26 @@ void BOARD_BootClockRUN(void)
|
|||
/* Init Usb1 pfd3. */
|
||||
CLOCK_InitUsb1Pfd(kCLOCK_Pfd3, 19);
|
||||
#endif
|
||||
/* DeInit Audio PLL. */
|
||||
CLOCK_DeinitAudioPll();
|
||||
/* Bypass Audio PLL. */
|
||||
CLOCK_SetPllBypass(CCM_ANALOG, kCLOCK_PllAudio, 1);
|
||||
/* Set divider for Audio PLL. */
|
||||
/* Init Audio PLL. */
|
||||
uint32_t pllAudio;
|
||||
/* Disable Audio PLL output before initial Audio PLL. */
|
||||
CCM_ANALOG->PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_ENABLE_MASK;
|
||||
/* Bypass PLL first */
|
||||
CCM_ANALOG->PLL_AUDIO = (CCM_ANALOG->PLL_AUDIO & (~CCM_ANALOG_PLL_AUDIO_BYPASS_CLK_SRC_MASK)) |
|
||||
CCM_ANALOG_PLL_AUDIO_BYPASS_MASK | CCM_ANALOG_PLL_AUDIO_BYPASS_CLK_SRC(0);
|
||||
CCM_ANALOG->PLL_AUDIO_NUM = CCM_ANALOG_PLL_AUDIO_NUM_A(66);
|
||||
CCM_ANALOG->PLL_AUDIO_DENOM = CCM_ANALOG_PLL_AUDIO_DENOM_B(625);
|
||||
pllAudio = (CCM_ANALOG->PLL_AUDIO & (~(CCM_ANALOG_PLL_AUDIO_DIV_SELECT_MASK | CCM_ANALOG_PLL_AUDIO_POWERDOWN_MASK))) |
|
||||
CCM_ANALOG_PLL_AUDIO_ENABLE_MASK | CCM_ANALOG_PLL_AUDIO_DIV_SELECT(30);
|
||||
pllAudio |= CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2);
|
||||
CCM_ANALOG->MISC2 &= ~CCM_ANALOG_MISC2_AUDIO_DIV_LSB_MASK;
|
||||
CCM_ANALOG->MISC2 &= ~CCM_ANALOG_MISC2_AUDIO_DIV_MSB_MASK;
|
||||
/* Enable Audio PLL output. */
|
||||
CCM_ANALOG->PLL_AUDIO |= CCM_ANALOG_PLL_AUDIO_ENABLE_MASK;
|
||||
CCM_ANALOG->PLL_AUDIO = pllAudio;
|
||||
while ((CCM_ANALOG->PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_LOCK_MASK) == 0)
|
||||
{
|
||||
}
|
||||
/* Disable bypass for Audio PLL. */
|
||||
CLOCK_SetPllBypass(CCM_ANALOG, kCLOCK_PllAudio, 0);
|
||||
/* DeInit Video PLL. */
|
||||
CLOCK_DeinitVideoPll();
|
||||
/* Bypass Video PLL. */
|
||||
|
|
@ -451,7 +481,7 @@ void BOARD_BootClockRUN(void)
|
|||
/* Set SAI3 MCLK3 clock source. */
|
||||
IOMUXC_SetSaiMClkClockSource(IOMUXC_GPR, kIOMUXC_GPR_SAI3MClk3Sel, 0);
|
||||
/* Set MQS configuration. */
|
||||
IOMUXC_MQSConfig(IOMUXC_GPR,kIOMUXC_MqsPwmOverSampleRate32, 0);
|
||||
IOMUXC_MQSConfig(IOMUXC_GPR,kIOMUXC_MqsPwmOverSampleRate64, 3);
|
||||
/* Set ENET Ref clock source. */
|
||||
#if defined(IOMUXC_GPR_GPR1_ENET_REF_CLK_DIR_MASK)
|
||||
IOMUXC_GPR->GPR1 &= ~IOMUXC_GPR_GPR1_ENET_REF_CLK_DIR_MASK;
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ void BOARD_InitBootClocks(void);
|
|||
#define BOARD_BOOTCLOCKRUN_LPI2C_CLK_ROOT 60000000UL /* Clock consumers of LPI2C_CLK_ROOT output : LPI2C1, LPI2C2, LPI2C3, LPI2C4 */
|
||||
#define BOARD_BOOTCLOCKRUN_LPSPI_CLK_ROOT 105600000UL /* Clock consumers of LPSPI_CLK_ROOT output : LPSPI1, LPSPI2, LPSPI3, LPSPI4 */
|
||||
#define BOARD_BOOTCLOCKRUN_LVDS1_CLK 1200000000UL /* Clock consumers of LVDS1_CLK output : N/A */
|
||||
#define BOARD_BOOTCLOCKRUN_MQS_MCLK 63529411UL /* Clock consumers of MQS_MCLK output : N/A */
|
||||
#define BOARD_BOOTCLOCKRUN_MQS_MCLK 2822400UL /* Clock consumers of MQS_MCLK output : N/A */
|
||||
#define BOARD_BOOTCLOCKRUN_PERCLK_CLK_ROOT 75000000UL /* Clock consumers of PERCLK_CLK_ROOT output : GPT1, GPT2, PIT */
|
||||
#define BOARD_BOOTCLOCKRUN_PLL7_MAIN_CLK 24000000UL /* Clock consumers of PLL7_MAIN_CLK output : N/A */
|
||||
#define BOARD_BOOTCLOCKRUN_SAI1_CLK_ROOT 63529411UL /* Clock consumers of SAI1_CLK_ROOT output : N/A */
|
||||
|
|
@ -76,8 +76,8 @@ void BOARD_InitBootClocks(void);
|
|||
#define BOARD_BOOTCLOCKRUN_SAI2_MCLK1 63529411UL /* Clock consumers of SAI2_MCLK1 output : SAI2 */
|
||||
#define BOARD_BOOTCLOCKRUN_SAI2_MCLK2 0UL /* Clock consumers of SAI2_MCLK2 output : SAI2 */
|
||||
#define BOARD_BOOTCLOCKRUN_SAI2_MCLK3 30000000UL /* Clock consumers of SAI2_MCLK3 output : SAI2 */
|
||||
#define BOARD_BOOTCLOCKRUN_SAI3_CLK_ROOT 63529411UL /* Clock consumers of SAI3_CLK_ROOT output : N/A */
|
||||
#define BOARD_BOOTCLOCKRUN_SAI3_MCLK1 63529411UL /* Clock consumers of SAI3_MCLK1 output : SAI3 */
|
||||
#define BOARD_BOOTCLOCKRUN_SAI3_CLK_ROOT 11289600UL /* Clock consumers of SAI3_CLK_ROOT output : N/A */
|
||||
#define BOARD_BOOTCLOCKRUN_SAI3_MCLK1 11289600UL /* Clock consumers of SAI3_MCLK1 output : SAI3 */
|
||||
#define BOARD_BOOTCLOCKRUN_SAI3_MCLK2 0UL /* Clock consumers of SAI3_MCLK2 output : SAI3 */
|
||||
#define BOARD_BOOTCLOCKRUN_SAI3_MCLK3 30000000UL /* Clock consumers of SAI3_MCLK3 output : SAI3 */
|
||||
#define BOARD_BOOTCLOCKRUN_SEMC_CLK_ROOT 120000000UL /* Clock consumers of SEMC_CLK_ROOT output : SEMC */
|
||||
|
|
@ -99,6 +99,9 @@ extern const clock_usb_pll_config_t usb1PllConfig_BOARD_BootClockRUN;
|
|||
/*! @brief Sys PLL for BOARD_BootClockRUN configuration.
|
||||
*/
|
||||
extern const clock_sys_pll_config_t sysPllConfig_BOARD_BootClockRUN;
|
||||
/*! @brief Audio PLL set for BOARD_BootClockRUN configuration.
|
||||
*/
|
||||
extern const clock_audio_pll_config_t audioPllConfig_BOARD_BootClockRUN;
|
||||
/*! @brief Enet PLL set for BOARD_BootClockRUN configuration.
|
||||
*/
|
||||
extern const clock_enet_pll_config_t enetPllConfig_BOARD_BootClockRUN;
|
||||
|
|
|
|||
18
bsp/mqs/CMakeLists.txt
Normal file
18
bsp/mqs/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# bsp/mqs/CMakeLists.txt
|
||||
|
||||
if(BUILD_TESTS_HOST)
|
||||
return()
|
||||
endif()
|
||||
|
||||
add_library(bsp_mqs STATIC src/mqs.c src/mqs_amp.c)
|
||||
|
||||
target_include_directories(
|
||||
bsp_mqs
|
||||
PUBLIC include/
|
||||
PRIVATE src/)
|
||||
|
||||
target_link_libraries(
|
||||
bsp_mqs
|
||||
PUBLIC bsp_status
|
||||
PUBLIC bsp_board
|
||||
PRIVATE sdk_sai_edma sdk_edma sdk_dmamux sdk_clock sdk_pwm sdk_xbara)
|
||||
0
bsp/mqs/README.md
Normal file
0
bsp/mqs/README.md
Normal file
181
bsp/mqs/include/bsp/mqs.h
Normal file
181
bsp/mqs/include/bsp/mqs.h
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
/**
|
||||
* @file bsp/mqs.h
|
||||
* @brief BSP: Medium Quality Sound (MQS) — SAI1 + eDMA + MQS.
|
||||
*
|
||||
* Слой абстракции над SAI1/eDMA/MQS для монофонического аудио-выхода.
|
||||
* Физически на плате выведен один канал (MQS_RIGHT, GPIO_AD_B0_04);
|
||||
* SAI1 требует стерео-буфер — оба канала всегда идентичны.
|
||||
*
|
||||
* Режимы использования:
|
||||
* - firmware_test: bsp_mqs_play_blocking() — синхронная подача
|
||||
* статического буфера, polling-флаг завершения.
|
||||
* - tft_app: bsp_mqs_play() + колбэк — асинхронная потоковая
|
||||
* подача буферов из задачи FreeRTOS.
|
||||
*
|
||||
* Ограничения:
|
||||
* - Буфер должен быть в некэшируемой памяти (AT_NONCACHEABLE_SECTION_ALIGN)
|
||||
* или кэш должен быть явно вычищен перед вызовом bsp_mqs_play().
|
||||
* - Одновременно может воспроизводиться только один буфер.
|
||||
* - bsp_mqs_init() вызывается однократно; повторный вызов без
|
||||
* bsp_mqs_deinit() возвращает BSP_OK без переинициализации.
|
||||
*/
|
||||
|
||||
#ifndef BSP_MQS_H
|
||||
#define BSP_MQS_H
|
||||
|
||||
#include "bsp/status.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Параметры аудио-потока
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
/** Частота дискретизации, Гц. Небольшое отклонение (~0.5%) из-за
|
||||
* источника SAI1_CLK_ROOT (System PLL PFD2, не Audio PLL). */
|
||||
#define BSP_MQS_SAMPLE_RATE_HZ (44100U)
|
||||
|
||||
/** Разрядность PCM. MQS поддерживает только 16 бит. */
|
||||
#define BSP_MQS_BIT_WIDTH (16U)
|
||||
|
||||
/** Количество каналов в буфере. SAI1+MQS требует стерео; правый == левый. */
|
||||
#define BSP_MQS_CHANNELS (2U)
|
||||
|
||||
/** Байт на один моно-сэмпл (16 бит → 2 байта). */
|
||||
#define BSP_MQS_BYTES_PER_SAMPLE (BSP_MQS_BIT_WIDTH / 8U)
|
||||
|
||||
/** Байт на один стерео-фрейм (L + R). */
|
||||
#define BSP_MQS_BYTES_PER_FRAME (BSP_MQS_BYTES_PER_SAMPLE * BSP_MQS_CHANNELS)
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Колбэк завершения (для tft_app / потокового режима)
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Колбэк завершения DMA-передачи одного буфера.
|
||||
*
|
||||
* Вызывается из контекста ISR. Запрещено: блокировки, HAL-функции
|
||||
* без суффикса _FromISR, длительные вычисления.
|
||||
*
|
||||
* @param p_user Пользовательский указатель, переданный в bsp_mqs_play().
|
||||
*/
|
||||
typedef void (*bsp_mqs_done_cb_t)(void *p_user);
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* API
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Инициализация MQS-подсистемы: SAI1, eDMA, DMAMUX, MQS.
|
||||
*
|
||||
* Включает тактирование SAI1 (kCLOCK_Sai1), настраивает SAI1 в режиме
|
||||
* TX Master, 16 бит, стерео, 44100 Гц, инициализирует eDMA канал 0
|
||||
* (DMAMUX source kDmaRequestMuxSai1Tx) и MQS-модуль.
|
||||
*
|
||||
* Пин GPIO_AD_B0_04 (MQS_RIGHT) уже замультиплексирован в BOARD_InitPins().
|
||||
* MQS oversample (×32) уже выставлен в BOARD_BootClockRUN().
|
||||
*
|
||||
* @return BSP_OK успех
|
||||
* @return BSP_ERR_HW eDMA или SAI вернул ошибку при настройке формата
|
||||
*/
|
||||
bsp_status_t bsp_mqs_init(void);
|
||||
|
||||
/**
|
||||
* @brief Деинициализация: остановить DMA, сбросить SAI1 и MQS.
|
||||
*
|
||||
* Безопасно вызывать даже если воспроизведение уже завершилось.
|
||||
* После вызова модуль требует повторного bsp_mqs_init().
|
||||
*/
|
||||
void bsp_mqs_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Запустить асинхронное воспроизведение буфера через eDMA.
|
||||
*
|
||||
* Буфер должен содержать стерео PCM16 interleaved (L0, R0, L1, R1, …).
|
||||
* Указатель @p p_buf обязан оставаться валидным до срабатывания колбэка @p cb.
|
||||
*
|
||||
* @param p_buf Указатель на стерео PCM16 буфер (некэшируемая память).
|
||||
* @param n_frames Число стерео-фреймов (не байт, не сэмплов).
|
||||
* @param cb Колбэк завершения; NULL — без уведомления.
|
||||
* @param p_user Аргумент для колбэка; NULL допустим.
|
||||
*
|
||||
* @return BSP_OK передача запущена
|
||||
* @return BSP_ERR_BUSY предыдущая передача ещё не завершена
|
||||
* @return BSP_ERR_INVALID p_buf == NULL или n_frames == 0
|
||||
* @return BSP_ERR_HW SAI_TransferSendEDMA вернул ошибку
|
||||
*/
|
||||
bsp_status_t bsp_mqs_play(const int16_t *p_buf, size_t n_frames, bsp_mqs_done_cb_t cb,
|
||||
void *p_user);
|
||||
|
||||
/**
|
||||
* @brief Синхронное воспроизведение: запустить и дождаться завершения.
|
||||
*
|
||||
* Вызывает bsp_mqs_play() и крутится в polling-цикле до сброса флага
|
||||
* завершения. Предназначен для bare-metal (firmware_test).
|
||||
* Не вызывать из ISR или FreeRTOS-задачи с высоким приоритетом.
|
||||
*
|
||||
* @param p_buf Указатель на стерео PCM16 буфер (некэшируемая память).
|
||||
* @param n_frames Число стерео-фреймов.
|
||||
*
|
||||
* @return BSP_OK воспроизведение завершено
|
||||
* @return BSP_ERR_BUSY предыдущая передача ещё не завершена
|
||||
* @return BSP_ERR_INVALID p_buf == NULL или n_frames == 0
|
||||
* @return BSP_ERR_HW SAI_TransferSendEDMA вернул ошибку
|
||||
*/
|
||||
bsp_status_t bsp_mqs_play_blocking(const int16_t *p_buf, size_t n_frames);
|
||||
|
||||
/**
|
||||
* @brief Немедленно прервать воспроизведение.
|
||||
*
|
||||
* Останавливает текущую DMA-передачу. Колбэк после stop НЕ вызывается.
|
||||
* Безопасно вызывать если передача уже завершилась.
|
||||
*/
|
||||
void bsp_mqs_stop(void);
|
||||
|
||||
/**
|
||||
* @brief Проверить, активна ли DMA-передача.
|
||||
*
|
||||
* @return true передача идёт
|
||||
* @return false передача завершена или не была запущена
|
||||
*/
|
||||
bool bsp_mqs_is_busy(void);
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Усилитель (LM4875M, управление через PWM4 SM0 PWM_A)
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Инициализация усилителя: PWM4 SM0, 16 кГц, duty 50%.
|
||||
*
|
||||
* Настраивает XBARA1 (fault disable), PWM4 submodule 0 channel A.
|
||||
* Вызывать до bsp_mqs_play() — без ШИМ на VOLUME усиление равно нулю.
|
||||
*
|
||||
* @return BSP_OK успех
|
||||
* @return BSP_ERR_HW PWM_Init или PWM_SetupPwm вернул ошибку
|
||||
*/
|
||||
bsp_status_t bsp_mqs_amp_init(void);
|
||||
|
||||
/**
|
||||
* @brief Установить уровень громкости (duty cycle PWM).
|
||||
*
|
||||
* @param percent Громкость 0–100%. 0 = тишина, 50 = номинал, 100 = макс.
|
||||
*/
|
||||
void bsp_mqs_amp_set_volume(uint8_t percent);
|
||||
|
||||
/**
|
||||
* @brief Деинициализация усилителя: duty → 0%, стоп таймера.
|
||||
*/
|
||||
void bsp_mqs_amp_deinit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* BSP_MQS_H */
|
||||
286
bsp/mqs/src/mqs.c
Normal file
286
bsp/mqs/src/mqs.c
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
/**
|
||||
* @file bsp_mqs.c
|
||||
* @brief BSP MQS: SAI1 TX + eDMA + MQS для MIMXRT1052CVJ5B.
|
||||
*
|
||||
* Тактирование:
|
||||
* SAI1_CLK_ROOT = SysPLL × (18/27) / (SAI1_CLK_PRED+1=4) / (SAI1_CLK_PODF+1=2)
|
||||
* ≈ 63 529 411 Гц (BOARD_BOOTCLOCKRUN_SAI1_CLK_ROOT)
|
||||
* Bit clock = 44100 × 16 × 2 = 1 411 200 Гц
|
||||
* MCLK делитель = 63 529 411 / 1 411 200 ≈ 45.0 (погрешность ~0.5 %)
|
||||
*
|
||||
* MQS oversample = 32, уже выставлен в BOARD_BootClockRUN() через
|
||||
* IOMUXC_MQSConfig(IOMUXC_GPR, kIOMUXC_MqsPwmOverSampleRate32, 0).
|
||||
*
|
||||
* Пин: GPIO_AD_B0_04 → MQS_RIGHT — замультиплексирован в BOARD_InitPins().
|
||||
*
|
||||
* eDMA: DMA0 канал 0, DMAMUX source kDmaRequestMuxSai1Tx.
|
||||
* Канал 0 зарезервирован за bsp_mqs. Прочие модули — каналы 1+.
|
||||
*
|
||||
* SAI API (SDK 2.4.7 / fsl_sai.h, fsl_sai_edma.h 2.7.3):
|
||||
* Конфигурация через sai_transceiver_t:
|
||||
* SAI_GetClassicI2SConfig() → SAI_TxSetConfig() → SAI_TxSetBitClockRate()
|
||||
* eDMA:
|
||||
* SAI_TransferTxCreateHandleEDMA() → SAI_TransferTxSetConfigEDMA()
|
||||
* FIFO watermark: FSL_FEATURE_SAI_FIFO_COUNTn(x) — макрос с аргументом
|
||||
* экземпляра; требует #include "MIMXRT1052_features.h".
|
||||
* Сброс: SAI_TxReset() (SAI_TxSoftwareReset отсутствует в этой версии).
|
||||
*/
|
||||
|
||||
#include "bsp/mqs.h"
|
||||
|
||||
#include "MIMXRT1052_features.h"
|
||||
#include "clock_config.h"
|
||||
#include "fsl_clock.h"
|
||||
#include "fsl_dmamux.h"
|
||||
#include "fsl_edma.h"
|
||||
#include "fsl_iomuxc.h"
|
||||
#include "fsl_sai.h"
|
||||
#include "fsl_sai_edma.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Конфигурация периферии
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
#define MQS_SAI_BASE SAI3
|
||||
#define MQS_SAI_CLOCK_GATE kCLOCK_Sai3
|
||||
#define MQS_SAI_CLK_FREQ BOARD_BOOTCLOCKRUN_SAI3_CLK_ROOT
|
||||
|
||||
/** eDMA канал, выделенный под SAI1 TX. */
|
||||
#define MQS_DMA_CHANNEL (0U)
|
||||
|
||||
/** DMAMUX запрос для SAI1 TX. */
|
||||
#define MQS_DMAMUX_SOURCE kDmaRequestMuxSai3Tx
|
||||
|
||||
/** Приоритет прерывания DMA (ниже USB = 3, выше нормальных задач). */
|
||||
#define MQS_DMA_IRQ_PRIORITY (5U)
|
||||
#define MQS_HMCLK_GATE kCLOCK_Mqs
|
||||
/**
|
||||
* FIFO watermark — половина глубины FIFO SAI1.
|
||||
* FSL_FEATURE_SAI_FIFO_COUNTn(x) принимает экземпляр SAI и возвращает
|
||||
* глубину FIFO в словах (32 для RT1052). Деление на 2 даёт оптимальную
|
||||
* латентность DMA: запрос формируется когда в FIFO остаётся место для
|
||||
* половины буфера.
|
||||
*/
|
||||
#define MQS_SAI_FIFO_WATERMARK \
|
||||
((uint8_t) ((uint8_t) FSL_FEATURE_SAI_FIFO_COUNTn(MQS_SAI_BASE) / 2U))
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Внутреннее состояние
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
static bool g_s_initialized = false;
|
||||
static volatile bool g_s_dma_done = false;
|
||||
static volatile bool g_s_busy = false;
|
||||
|
||||
static bsp_mqs_done_cb_t g_s_user_cb = NULL;
|
||||
static void *g_s_user_data = NULL;
|
||||
|
||||
/*
|
||||
* Handles в некэшируемой секции необязательны (не DMA-буферы), но
|
||||
* выравнивание по 4 байта обязательно для SDK.
|
||||
*/
|
||||
AT_NONCACHEABLE_SECTION_ALIGN(static edma_handle_t s_dma_handle, 4U);
|
||||
AT_NONCACHEABLE_SECTION_ALIGN(static sai_edma_handle_t s_sai_tx_handle, 4U);
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* DMA колбэк (ISR-контекст)
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
static void mqs_edma_callback(I2S_Type *p_base, sai_edma_handle_t *p_handle, status_t status,
|
||||
void *p_user_data)
|
||||
{
|
||||
(void) p_base;
|
||||
(void) p_handle;
|
||||
(void) status;
|
||||
(void) p_user_data;
|
||||
|
||||
g_s_busy = false;
|
||||
g_s_dma_done = true;
|
||||
|
||||
if (g_s_user_cb != NULL)
|
||||
{
|
||||
g_s_user_cb(g_s_user_data);
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Публичный API
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
bsp_status_t bsp_mqs_init(void)
|
||||
{
|
||||
if (g_s_initialized)
|
||||
{
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
/* --- Тактирование SAI1 --- */
|
||||
CLOCK_EnableClock(MQS_SAI_CLOCK_GATE);
|
||||
|
||||
/* --- Тактирование MQS (CCGR0[CG2]) — без него MQS не забирает данные из
|
||||
* SAI FIFO и SAI_WriteBlocking зависает навсегда. --- */
|
||||
CLOCK_EnableClock(kCLOCK_Mqs);
|
||||
|
||||
/* --- MQS: сброс → включение.
|
||||
* Oversample ×32 уже выставлен в BOARD_BootClockRUN(),
|
||||
* повторный вызов IOMUXC_MQSConfig() не нужен. --- */
|
||||
IOMUXC_MQSEnterSoftwareReset(IOMUXC_GPR, true);
|
||||
IOMUXC_MQSEnterSoftwareReset(IOMUXC_GPR, false);
|
||||
IOMUXC_MQSEnable(IOMUXC_GPR, true);
|
||||
|
||||
/* --- SAI1: базовая инициализация (снимает reset, включает clock gate) --- */
|
||||
SAI_Init(MQS_SAI_BASE);
|
||||
|
||||
/* --- SAI1 TX: классический I2S, master, 16 бит, стерео, канал 0.
|
||||
* SAI_GetClassicI2SConfig() заполняет sai_transceiver_t значениями по
|
||||
* умолчанию для стандартного I2S. Затем корректируем watermark и
|
||||
* sync mode (async — TX не зависит от RX). --- */
|
||||
sai_transceiver_t sai_cfg;
|
||||
|
||||
SAI_GetLeftJustifiedConfig(&sai_cfg, kSAI_WordWidth16bits, kSAI_Stereo,
|
||||
(uint32_t) kSAI_Channel0Mask);
|
||||
sai_cfg.syncMode = kSAI_ModeAsync;
|
||||
sai_cfg.fifo.fifoWatermark = MQS_SAI_FIFO_WATERMARK;
|
||||
sai_cfg.startChannel = 0;
|
||||
|
||||
/* Явно задать источник bit clock — SAI1_CLK_ROOT (Mux делитель).
|
||||
* Default после SAI_GetClassicI2SConfig может быть BusClock (IPG). */
|
||||
sai_cfg.bitClock.bclkSource = kSAI_BclkSourceMclkDiv;
|
||||
|
||||
SAI_TxSetConfig(MQS_SAI_BASE, &sai_cfg);
|
||||
|
||||
/* --- Bit clock rate: вычисляется как sourceClk / (sampleRate * bitWidth * channels).
|
||||
* Функция void — ошибки не возвращает, делитель рассчитывается аппаратно. --- */
|
||||
SAI_TxSetBitClockRate(MQS_SAI_BASE, (uint32_t) MQS_SAI_CLK_FREQ,
|
||||
(uint32_t) kSAI_SampleRate44100Hz, (uint32_t) kSAI_WordWidth16bits,
|
||||
BSP_MQS_CHANNELS);
|
||||
|
||||
/* --- eDMA --- */
|
||||
edma_config_t dma_cfg;
|
||||
EDMA_GetDefaultConfig(&dma_cfg);
|
||||
EDMA_Init(DMA0, &dma_cfg);
|
||||
EDMA_CreateHandle(&s_dma_handle, DMA0, MQS_DMA_CHANNEL);
|
||||
|
||||
/* --- DMAMUX: канал 0 → SAI1 TX --- */
|
||||
DMAMUX_Init(DMAMUX);
|
||||
DMAMUX_SetSource(DMAMUX, MQS_DMA_CHANNEL, (uint8_t) MQS_DMAMUX_SOURCE);
|
||||
DMAMUX_EnableChannel(DMAMUX, MQS_DMA_CHANNEL);
|
||||
|
||||
/* --- SAI eDMA handle + конфигурация.
|
||||
* SAI_TransferTxSetConfigEDMA() — void, настраивает DMA-дескрипторы
|
||||
* (размер minor loop, адрес FIFO) на основе sai_transceiver_t. --- */
|
||||
SAI_TransferTxCreateHandleEDMA(MQS_SAI_BASE, &s_sai_tx_handle, mqs_edma_callback, NULL,
|
||||
&s_dma_handle);
|
||||
SAI_TransferTxSetConfigEDMA(MQS_SAI_BASE, &s_sai_tx_handle, &sai_cfg);
|
||||
|
||||
/* Включить SAI TX — без этого FIFO не дренируется даже в polling-режиме.
|
||||
* SAI_TransferSendEDMA() включает его снова если MCUX_SDK_SAI_EDMA_TX_ENABLE_INTERNAL=1,
|
||||
* но для корректной работы FIFO TX должен быть активен сразу после init. */
|
||||
SAI_TxEnable(MQS_SAI_BASE, true);
|
||||
|
||||
NVIC_SetPriority(DMA0_DMA16_IRQn, MQS_DMA_IRQ_PRIORITY);
|
||||
|
||||
g_s_busy = false;
|
||||
g_s_dma_done = false;
|
||||
g_s_initialized = true;
|
||||
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
void bsp_mqs_deinit(void)
|
||||
{
|
||||
if (!g_s_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SAI_TransferTerminateSendEDMA(MQS_SAI_BASE, &s_sai_tx_handle);
|
||||
/* SAI_TxReset() — сброс logic + FIFO (аналог kSAI_ResetAll).
|
||||
* SAI_TxSoftwareReset() отсутствует в данной версии SDK. */
|
||||
SAI_TxReset(MQS_SAI_BASE);
|
||||
IOMUXC_MQSEnable(IOMUXC_GPR, false);
|
||||
CLOCK_DisableClock(MQS_SAI_CLOCK_GATE);
|
||||
CLOCK_DisableClock(kCLOCK_Mqs);
|
||||
g_s_busy = false;
|
||||
g_s_dma_done = false;
|
||||
g_s_user_cb = NULL;
|
||||
g_s_user_data = NULL;
|
||||
g_s_initialized = false;
|
||||
}
|
||||
|
||||
bsp_status_t bsp_mqs_play(const int16_t *p_buf, size_t n_frames, bsp_mqs_done_cb_t p_cb,
|
||||
void *p_user)
|
||||
{
|
||||
if ((p_buf == NULL) || (n_frames == 0U))
|
||||
{
|
||||
return BSP_ERR_INVALID;
|
||||
}
|
||||
if (g_s_busy)
|
||||
{
|
||||
return BSP_ERR_BUSY;
|
||||
}
|
||||
|
||||
/* Очищаем возможный Underrun (FEF) от предыдущего проигрывания и сбрасываем FIFO */
|
||||
SAI3->TCSR |= I2S_TCSR_FEF_MASK | I2S_TCSR_FR_MASK;
|
||||
|
||||
g_s_user_cb = p_cb;
|
||||
g_s_user_data = p_user;
|
||||
g_s_dma_done = false;
|
||||
g_s_busy = true;
|
||||
|
||||
sai_transfer_t xfer;
|
||||
/* p_buf — const int16_t*; SDK ожидает uint8_t*.
|
||||
* Cast через uintptr_t: MISRA-compliant ptr→int→ptr. */
|
||||
xfer.data = (uint8_t *) (uintptr_t) p_buf;
|
||||
xfer.dataSize = n_frames * (size_t) BSP_MQS_BYTES_PER_FRAME;
|
||||
|
||||
status_t status = SAI_TransferSendEDMA(MQS_SAI_BASE, &s_sai_tx_handle, &xfer);
|
||||
if (status != kStatus_Success)
|
||||
{
|
||||
g_s_busy = false;
|
||||
return BSP_ERR_HW;
|
||||
}
|
||||
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
bsp_status_t bsp_mqs_play_blocking(const int16_t *p_buf, size_t n_frames)
|
||||
{
|
||||
bsp_status_t status = bsp_mqs_play(p_buf, n_frames, NULL, NULL);
|
||||
if (status != BSP_OK)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Polling-ожидание завершения DMA.
|
||||
* Флаг s_dma_done взводится из ISR (mqs_edma_callback).
|
||||
* USB keepalive выполняется в вызывающем тест-коде (test_mqs_run).
|
||||
*/
|
||||
while (!g_s_dma_done)
|
||||
{
|
||||
/* bare-metal: явное ожидание ISR */
|
||||
}
|
||||
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
void bsp_mqs_stop(void)
|
||||
{
|
||||
if (!g_s_busy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SAI_TransferTerminateSendEDMA(MQS_SAI_BASE, &s_sai_tx_handle);
|
||||
g_s_busy = false;
|
||||
g_s_dma_done = false;
|
||||
/* Колбэк НЕ вызывается: явная остановка — ответственность вызывающего. */
|
||||
}
|
||||
|
||||
bool bsp_mqs_is_busy(void)
|
||||
{
|
||||
return g_s_busy;
|
||||
}
|
||||
141
bsp/mqs/src/mqs_amp.c
Normal file
141
bsp/mqs/src/mqs_amp.c
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
/**
|
||||
* @file bsp_mqs_amp.c
|
||||
* @brief BSP: управление усилителем звука LM4875M через FlexPWM4 SM0 PWM_A.
|
||||
*
|
||||
* Схема:
|
||||
* GPIO_AD_B1_08 (PWM4_A, SM0) → R56 (1K) → LM358 +IN1 → LM4875M DC_VOL
|
||||
* DC_VOL управляет коэффициентом усиления LM4875M.
|
||||
* HP_SEN LM4875M подтянут к GND — включается автоматически при наличии
|
||||
* аудио-сигнала на входе (MQS_RIGHT через RC-фильтр → SOUND_OUT).
|
||||
*
|
||||
* Управление громкостью:
|
||||
* PWM4 SM0 PWM_A, частота 16 кГц, центрально-симметричный режим.
|
||||
* duty 0% → DC_VOL ≈ 0 В → усиление минимально (тишина).
|
||||
* duty 50% → DC_VOL ≈ 2.5 В → номинальная громкость.
|
||||
* duty 100%→ DC_VOL ≈ 5 В → максимальное усиление.
|
||||
*
|
||||
* Fault-вход PWM4:
|
||||
* XBARA1: kXBARA1_InputLogicHigh → kXBARA1_OutputFlexpwm4Fault0.
|
||||
* Fault сконфигурирован как "no fault" — PWM работает без аппаратного
|
||||
* отключения. Это повторяет конфигурацию из оригинального timers.c.
|
||||
*
|
||||
* Тактирование:
|
||||
* IPG clock = AHB/4 = 600/4 = 150 МГц.
|
||||
* PWM prescaler = /16 → PWM clock = 9.375 МГц.
|
||||
* Fpwm = 16000 Гц (центрально-симметричный режим).
|
||||
*/
|
||||
|
||||
#include "bsp/mqs.h"
|
||||
#include "clock_config.h"
|
||||
#include "fsl_clock.h"
|
||||
#include "fsl_pwm.h"
|
||||
#include "fsl_xbara.h"
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Конфигурация
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
#define AMP_PWM_BASE PWM4
|
||||
#define AMP_PWM_SUBMODULE kPWM_Module_0
|
||||
#define AMP_PWM_CHANNEL kPWM_PwmA
|
||||
#define AMP_PWM_MODE kPWM_SignedCenterAligned
|
||||
#define AMP_PWM_FREQ_HZ (16000U)
|
||||
|
||||
/** Номинальный duty cycle при bsp_mqs_amp_init() — 50%. */
|
||||
#define AMP_DEFAULT_DUTY (50U)
|
||||
|
||||
/** IPG clock = 150 МГц (AHB/4). */
|
||||
#define AMP_PWM_SRC_CLK_HZ (BOARD_BOOTCLOCKRUN_IPG_CLK_ROOT)
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Публичный API
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
bsp_status_t bsp_mqs_amp_init(void)
|
||||
{
|
||||
/* --- XBARA1: подать логическую единицу на Fault0 PWM4.
|
||||
* Это предотвращает аппаратное отключение PWM при старте. --- */
|
||||
XBARA_Init(XBARA1);
|
||||
XBARA_SetSignalsConnection(XBARA1, kXBARA1_InputLogicHigh, kXBARA1_OutputFlexpwm4Fault0);
|
||||
|
||||
/* --- PWM4 SM0: базовая конфигурация --- */
|
||||
pwm_config_t pwm_cfg;
|
||||
PWM_GetDefaultConfig(&pwm_cfg);
|
||||
pwm_cfg.clockSource = kPWM_BusClock;
|
||||
pwm_cfg.prescale = kPWM_Prescale_Divide_16;
|
||||
pwm_cfg.pairOperation = kPWM_Independent;
|
||||
pwm_cfg.initializationControl = kPWM_Initialize_LocalSync;
|
||||
pwm_cfg.reloadLogic = kPWM_ReloadImmediate;
|
||||
pwm_cfg.reloadSelect = kPWM_LocalReload;
|
||||
pwm_cfg.reloadFrequency = kPWM_LoadEveryOportunity;
|
||||
pwm_cfg.forceTrigger = kPWM_Force_Local;
|
||||
|
||||
if (PWM_Init(AMP_PWM_BASE, AMP_PWM_SUBMODULE, &pwm_cfg) != kStatus_Success)
|
||||
{
|
||||
return BSP_ERR_HW;
|
||||
}
|
||||
|
||||
/* --- Fault-фильтр и fault-конфигурация (no fault active) --- */
|
||||
const pwm_fault_input_filter_param_t FAULT_FILTER = {
|
||||
.faultFilterPeriod = 1U,
|
||||
.faultFilterCount = 3U,
|
||||
.faultGlitchStretch = false,
|
||||
};
|
||||
PWM_SetupFaultInputFilter(AMP_PWM_BASE, &FAULT_FILTER);
|
||||
|
||||
const pwm_fault_param_t FAULT_CFG = {
|
||||
.faultClearingMode = kPWM_Automatic,
|
||||
.faultLevel = false,
|
||||
.enableCombinationalPath = false,
|
||||
.recoverMode = kPWM_NoRecovery,
|
||||
};
|
||||
PWM_SetupFaults(AMP_PWM_BASE, kPWM_Fault_0, &FAULT_CFG);
|
||||
PWM_SetupFaults(AMP_PWM_BASE, kPWM_Fault_1, &FAULT_CFG);
|
||||
PWM_SetupFaults(AMP_PWM_BASE, kPWM_Fault_2, &FAULT_CFG);
|
||||
PWM_SetupFaults(AMP_PWM_BASE, kPWM_Fault_3, &FAULT_CFG);
|
||||
|
||||
/* --- ForceSignal: использовать нормальный PWM-сигнал --- */
|
||||
PWM_SetupForceSignal(AMP_PWM_BASE, AMP_PWM_SUBMODULE, AMP_PWM_CHANNEL, kPWM_UsePwm);
|
||||
|
||||
/* --- PWM-сигнал: 16 кГц, центрально-симметричный, duty 50% --- */
|
||||
const pwm_signal_param_t PWM_SIGNAL = {
|
||||
.pwmChannel = AMP_PWM_CHANNEL,
|
||||
.dutyCyclePercent = AMP_DEFAULT_DUTY,
|
||||
.level = kPWM_HighTrue,
|
||||
.faultState = kPWM_PwmFaultState0,
|
||||
.deadtimeValue = 0U,
|
||||
};
|
||||
|
||||
if (PWM_SetupPwm(AMP_PWM_BASE, AMP_PWM_SUBMODULE, &PWM_SIGNAL, 1U, AMP_PWM_MODE,
|
||||
AMP_PWM_FREQ_HZ, (uint32_t) AMP_PWM_SRC_CLK_HZ) != kStatus_Success)
|
||||
{
|
||||
return BSP_ERR_HW;
|
||||
}
|
||||
|
||||
/* --- Применить и запустить --- */
|
||||
PWM_SetPwmLdok(AMP_PWM_BASE, (uint8_t) kPWM_Control_Module_0, true);
|
||||
PWM_StartTimer(AMP_PWM_BASE, (uint8_t) kPWM_Control_Module_0);
|
||||
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
void bsp_mqs_amp_set_volume(uint8_t percent)
|
||||
{
|
||||
if (percent > 100U)
|
||||
{
|
||||
percent = 100U;
|
||||
}
|
||||
|
||||
PWM_UpdatePwmDutycycle(AMP_PWM_BASE, AMP_PWM_SUBMODULE, AMP_PWM_CHANNEL, AMP_PWM_MODE, percent);
|
||||
PWM_SetPwmLdok(AMP_PWM_BASE, (uint8_t) kPWM_Control_Module_0, true);
|
||||
}
|
||||
|
||||
void bsp_mqs_amp_deinit(void)
|
||||
{
|
||||
/* Установить duty 0% перед остановкой — плавное выключение. */
|
||||
PWM_UpdatePwmDutycycle(AMP_PWM_BASE, AMP_PWM_SUBMODULE, AMP_PWM_CHANNEL, AMP_PWM_MODE, 0U);
|
||||
PWM_SetPwmLdok(AMP_PWM_BASE, (uint8_t) kPWM_Control_Module_0, true);
|
||||
|
||||
PWM_StopTimer(AMP_PWM_BASE, (uint8_t) kPWM_Control_Module_0);
|
||||
PWM_Deinit(AMP_PWM_BASE, AMP_PWM_SUBMODULE);
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ add_executable(
|
|||
src/tests/test_opto.c
|
||||
src/tests/test_sdram.c
|
||||
src/tests/test_can.c
|
||||
src/tests/test_mqs.c
|
||||
src/tests/test_qspi.c
|
||||
src/tests/test_usd.c
|
||||
src/tests/test_display.c
|
||||
|
|
@ -49,7 +50,9 @@ target_link_libraries(
|
|||
bsp_usb_cdc
|
||||
bsp_sdram
|
||||
bsp_qspi_flash
|
||||
bsp_uart_host
|
||||
bsp_opto
|
||||
bsp_mqs
|
||||
bsp_sd
|
||||
firmware_test_fatfs)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,206 @@
|
|||
/**
|
||||
* @file main_mqs_test.c
|
||||
* @brief Изолированный тест bsp_mqs + bsp_mqs_amp.
|
||||
*
|
||||
* Подменяет firmware/test/src/main.c на время отладки BSP.
|
||||
* Вывод диагностики — UART (MCU-Link VCOM, 115200 бод).
|
||||
*
|
||||
* Два режима — переключаются комментарием в main():
|
||||
* TEST_MODE_POLLING — SAI_WriteBlocking напрямую, без DMA.
|
||||
* Если звук есть → SAI/MQS OK, проблема в DMA.
|
||||
* Если нет → проблема в SAI/MQS/усилителе.
|
||||
* TEST_MODE_DMA — bsp_mqs_play_blocking через eDMA.
|
||||
*/
|
||||
|
||||
#include "board.h"
|
||||
#include "bsp/led.h"
|
||||
#include "bsp/mqs.h"
|
||||
#include "bsp/tick.h"
|
||||
#include "bsp/uart_host.h"
|
||||
#include "fsl_sai.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Выбор режима: раскомментируй один
|
||||
* ----------------------------------------------------------------------- */
|
||||
//#define TEST_MODE_POLLING
|
||||
#define TEST_MODE_DMA
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Буфер мелодии в некэшируемой секции
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
#define MQS_NOTE_SAMPLES (44100U * 2U) /* 2 с на ноту */
|
||||
#define MQS_TOTAL_FRAMES (MQS_NOTE_SAMPLES * 2U) /* 2 ноты */
|
||||
#define MQS_BUF_ELEMENTS (MQS_TOTAL_FRAMES * BSP_MQS_CHANNELS)
|
||||
|
||||
static AT_NONCACHEABLE_SECTION_ALIGN(int16_t s_melody_buf[MQS_BUF_ELEMENTS], 4U);
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Синусоида (целочисленная, LUT по четвертям)
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
static int16_t sine_sample(uint32_t phase, uint32_t period)
|
||||
{
|
||||
static const int16_t k_lut[25] = {
|
||||
0, 2998, 5944, 8788, 11479, 13970, 16212, 18162, 19784, 21043, 21910, 22369, 22404,
|
||||
22005, 21170, 19898, 18199, 16084, 13567, 10670, 7416, 3831, 0, -3831, -7416,
|
||||
};
|
||||
uint32_t idx = (phase * 96U) / period % 96U;
|
||||
uint32_t quarter = idx / 24U;
|
||||
uint32_t pos = idx % 24U;
|
||||
int16_t val;
|
||||
switch (quarter)
|
||||
{
|
||||
case 0U:
|
||||
val = k_lut[pos];
|
||||
break;
|
||||
case 1U:
|
||||
val = k_lut[24U - pos];
|
||||
break;
|
||||
case 2U:
|
||||
val = -k_lut[pos];
|
||||
break;
|
||||
default:
|
||||
val = -k_lut[24U - pos];
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static void build_melody(void)
|
||||
{
|
||||
const uint32_t k_p_a4 = 100U; /* A4 ≈ 440 Гц */
|
||||
const uint32_t k_p_e5 = 67U; /* E5 ≈ 659 Гц */
|
||||
uint32_t out = 0U;
|
||||
|
||||
for (uint32_t i = 0U; i < MQS_NOTE_SAMPLES; i++)
|
||||
{
|
||||
int16_t s = sine_sample(i % k_p_a4, k_p_a4);
|
||||
s_melody_buf[out++] = s; /* L */
|
||||
s_melody_buf[out++] = s; /* R */
|
||||
}
|
||||
for (uint32_t i = 0U; i < MQS_NOTE_SAMPLES; i++)
|
||||
{
|
||||
int16_t s = sine_sample(i % k_p_e5, k_p_e5);
|
||||
s_melody_buf[out++] = s; /* L */
|
||||
s_melody_buf[out++] = s; /* R */
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Режим A: SAI polling напрямую, без DMA
|
||||
*
|
||||
* SAI_WriteBlocking — блокирует до записи всех байт через FIFO.
|
||||
* Не требует DMA вообще. Если звук есть — SAI и MQS живые.
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
#ifdef TEST_MODE_POLLING
|
||||
static void run_test(void)
|
||||
{
|
||||
bsp_uart_host_write_str("[5A] Bare-metal SAI polling...\r\n");
|
||||
|
||||
/* 1. Глушим передатчик и отключаем запросы DMA, чтобы не мешали поллингу */
|
||||
SAI3->TCSR &= ~(I2S_TCSR_TE_MASK | I2S_TCSR_BCE_MASK | I2S_TCSR_FRDE_MASK);
|
||||
|
||||
/* 2. Сбрасываем ошибку Underrun (FEF) и чистим FIFO (FR) */
|
||||
SAI3->TCSR |= I2S_TCSR_FEF_MASK | I2S_TCSR_FR_MASK;
|
||||
|
||||
/* 3. Предзагружаем FIFO (закинем 16 сэмплов ДО включения) */
|
||||
/* Это спасет аппаратуру от мгновенного Underrun'а при старте */
|
||||
for (uint32_t i = 0; i < 16; i++)
|
||||
{
|
||||
SAI3->TDR[0] = s_melody_buf[i];
|
||||
}
|
||||
|
||||
/* 4. Включаем передатчик и генерацию Bit Clock */
|
||||
SAI3->TCSR |= (I2S_TCSR_TE_MASK | I2S_TCSR_BCE_MASK);
|
||||
|
||||
/* 5. Качаем остальной буфер вручную */
|
||||
for (uint32_t i = 16; i < MQS_BUF_ELEMENTS; i++)
|
||||
{
|
||||
/* Ждем, пока в FIFO появится свободное место (FWF == 1) */
|
||||
while ((SAI3->TCSR & I2S_TCSR_FWF_MASK) == 0)
|
||||
{
|
||||
/* Если всё-таки поймали опустошение - перезапускаем TX на лету */
|
||||
if (SAI3->TCSR & I2S_TCSR_FEF_MASK)
|
||||
{
|
||||
SAI3->TCSR |= I2S_TCSR_FEF_MASK; /* Сброс ошибки */
|
||||
SAI3->TCSR |= I2S_TCSR_TE_MASK | I2S_TCSR_BCE_MASK; /* Рестарт TX */
|
||||
}
|
||||
}
|
||||
/* Пишем следующий сэмпл */
|
||||
SAI3->TDR[0] = s_melody_buf[i];
|
||||
}
|
||||
|
||||
/* 6. Дожидаемся, пока уйдут остатки из FIFO, и выключаем */
|
||||
while (SAI3->TCSR & I2S_TCSR_FWF_MASK)
|
||||
{
|
||||
}
|
||||
SAI3->TCSR &= ~(I2S_TCSR_TE_MASK | I2S_TCSR_BCE_MASK);
|
||||
|
||||
bsp_uart_host_write_str("[5A] Done.\r\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Режим B: DMA через bsp_mqs_play_blocking
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
#ifdef TEST_MODE_DMA
|
||||
static void run_test(void)
|
||||
{
|
||||
bsp_uart_host_write_str("[5B] DMA play, ~4s...\r\n");
|
||||
|
||||
bsp_status_t st = bsp_mqs_play_blocking(s_melody_buf, MQS_TOTAL_FRAMES);
|
||||
|
||||
bsp_uart_host_write_str(st == BSP_OK ? "[5B] OK\r\n" : "[5B] FAIL\r\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* main
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
int main(void)
|
||||
{
|
||||
board_hw_init();
|
||||
bsp_tick_init();
|
||||
bsp_led_init();
|
||||
(void) bsp_uart_host_init(115200U);
|
||||
|
||||
bsp_led_on(LED_HEARTBEAT);
|
||||
bsp_uart_host_write_str("\r\n=== MQS BSP TEST ===\r\n");
|
||||
|
||||
/* Шаг 1 — усилитель */
|
||||
bsp_uart_host_write_str("[1] amp_init...\r\n");
|
||||
bsp_status_t st = bsp_mqs_amp_init();
|
||||
bsp_uart_host_write_str(st == BSP_OK ? "[1] OK\r\n" : "[1] FAIL\r\n");
|
||||
|
||||
/* Шаг 2 — SAI + DMA + MQS */
|
||||
bsp_uart_host_write_str("[2] mqs_init...\r\n");
|
||||
st = bsp_mqs_init();
|
||||
bsp_uart_host_write_str(st == BSP_OK ? "[2] OK\r\n" : "[2] FAIL\r\n");
|
||||
|
||||
/* Шаг 3 — генерация буфера */
|
||||
bsp_uart_host_write_str("[3] build_melody...\r\n");
|
||||
build_melody();
|
||||
bsp_uart_host_write_str("[3] done.\r\n");
|
||||
|
||||
/* Основной цикл */
|
||||
for (;;)
|
||||
{
|
||||
bsp_led_toggle(LED_HEARTBEAT);
|
||||
run_test();
|
||||
bsp_uart_host_write_str("[pause] 3s\r\n");
|
||||
bsp_delay(3000U);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#if 0
|
||||
/**
|
||||
* @file main.c
|
||||
* @brief firmware_test — точка входа.
|
||||
|
|
@ -65,3 +267,4 @@ int main(void)
|
|||
test_runner_process();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -32,11 +32,12 @@ extern const test_module_t K_TEST_DISPLAY;
|
|||
extern const test_module_t K_TEST_BUTTONS;
|
||||
extern const test_module_t K_TEST_OPTO;
|
||||
extern const test_module_t K_TEST_CAN;
|
||||
extern const test_module_t K_TEST_MQS;
|
||||
|
||||
static const test_module_t *const k_registry[] = {
|
||||
|
||||
&K_TEST_SDRAM, &K_TEST_QSPI, &K_TEST_USD, &K_TEST_DISPLAY,
|
||||
&K_TEST_BUTTONS, &K_TEST_OPTO, &K_TEST_CAN,
|
||||
&K_TEST_BUTTONS, &K_TEST_OPTO, &K_TEST_CAN, &K_TEST_MQS,
|
||||
};
|
||||
|
||||
#define REGISTRY_SIZE (sizeof(k_registry) / sizeof(k_registry[0]))
|
||||
|
|
|
|||
223
firmware/test/src/tests/test_mqs.c
Normal file
223
firmware/test/src/tests/test_mqs.c
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
/**
|
||||
* @file test_mqs.c
|
||||
* @brief Тест MQS: воспроизведение тестового тона через SAI1 + eDMA + MQS.
|
||||
*
|
||||
* Алгоритм:
|
||||
* 1. init() — bsp_mqs_init(), генерация мелодии в s_melody_buf.
|
||||
* 2. run() — воспроизвести мелодию (~4 с) с USB keepalive в цикле,
|
||||
* затем confirm_request("mqs_tone") — оператор подтверждает.
|
||||
* 3. deinit() — bsp_mqs_stop(), bsp_mqs_deinit().
|
||||
*
|
||||
* Мелодия: стерео PCM16 44100 Гц, две ноты (A4/E5) по 2 с каждая.
|
||||
* Буфер — статический глобальный в некэшируемой секции (OCRAM NonCacheable).
|
||||
* L == R (монофонический выход на плате).
|
||||
*
|
||||
* Протокол:
|
||||
* ← confirm_request("mqs_tone", "Слышен звуковой сигнал?", 15000)
|
||||
* → confirm("mqs_tone", true/false)
|
||||
* ← test_result pass/fail/skip
|
||||
*/
|
||||
|
||||
#include "bsp/mqs.h"
|
||||
#include "bsp/tick.h"
|
||||
#include "bsp/usb_cdc.h"
|
||||
#include "fsl_common.h"
|
||||
#include "test_module.h"
|
||||
#include "test_runner.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Буфер мелодии
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Две ноты, по 2 с каждая: A4 (440 Гц) + E5 (659 Гц).
|
||||
* Моно-сэмплов: 44100 × 2 = 88200 на ноту.
|
||||
* Стерео-фреймов = моно-сэмплов (каждый L дублируется в R).
|
||||
* int16_t на фрейм: 2 (L + R).
|
||||
* Итого: 88200 × 2 × 2 = 352800 int16_t = 705 600 байт.
|
||||
*
|
||||
* DMA-буфер объявлен через AT_NONCACHEABLE_SECTION_ALIGN (паттерн из test_display.c).
|
||||
*/
|
||||
#define MQS_NOTE_SAMPLES (44100U * 2U)
|
||||
#define MQS_TOTAL_MONO_SAMPLES (MQS_NOTE_SAMPLES * 2U)
|
||||
#define MQS_TOTAL_FRAMES MQS_TOTAL_MONO_SAMPLES
|
||||
#define MQS_BUF_ELEMENTS (MQS_TOTAL_FRAMES * BSP_MQS_CHANNELS)
|
||||
|
||||
/*
|
||||
* DMA-буфер мелодии в некэшируемой секции OCRAM.
|
||||
* static передаётся внутрь var-аргумента макроса — единственный валидный
|
||||
* способ, так как макрос раскрывается как:
|
||||
* __attribute__((section(...))) var __attribute__((aligned(N)))
|
||||
* Паттерн идентичен test_display.c (g_s_framebuf).
|
||||
*/
|
||||
static AT_NONCACHEABLE_SECTION_ALIGN(int16_t s_melody_buf[MQS_BUF_ELEMENTS], 4U);
|
||||
|
||||
static bool g_s_melody_ready = false;
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Генерация мелодии (целочисленная синусоида)
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Таблица синуса: 96 точек на период (шаг π/48).
|
||||
* Амплитуда 22937 ≈ 0.70 × INT16_MAX — запас от клиппинга на MQS.
|
||||
* Генерируется по четвертям (таблица хранит только одну четверть).
|
||||
*/
|
||||
static int16_t sine_sample(uint32_t phase, uint32_t period)
|
||||
{
|
||||
static const int16_t K_SIN_LUT[25] = {
|
||||
0, 2998, 5944, 8788, 11479, 13970, 16212, 18162, 19784, 21043, 21910, 22369, 22404,
|
||||
22005, 21170, 19898, 18199, 16084, 13567, 10670, 7416, 3831, 0, -3831, -7416,
|
||||
};
|
||||
|
||||
uint32_t idx = (phase * 96U) / period % 96U;
|
||||
uint32_t quarter = idx / 24U;
|
||||
uint32_t pos = idx % 24U;
|
||||
|
||||
int16_t val;
|
||||
switch (quarter)
|
||||
{
|
||||
case 0U:
|
||||
val = K_SIN_LUT[pos];
|
||||
break;
|
||||
case 1U:
|
||||
val = K_SIN_LUT[24U - pos];
|
||||
break;
|
||||
case 2U:
|
||||
val = -K_SIN_LUT[pos];
|
||||
break;
|
||||
default:
|
||||
val = -K_SIN_LUT[24U - pos];
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static void mqs_build_melody(void)
|
||||
{
|
||||
if (g_s_melody_ready)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* A4 = 440 Гц → период ≈ 100 сэмплов при 44100 Гц */
|
||||
const uint32_t K_PERIOD_A4 = 100U;
|
||||
/* E5 = 659 Гц → период ≈ 67 сэмплов при 44100 Гц */
|
||||
const uint32_t K_PERIOD_E5 = 67U;
|
||||
|
||||
uint32_t out = 0U;
|
||||
|
||||
for (uint32_t i = 0U; i < MQS_NOTE_SAMPLES; i++)
|
||||
{
|
||||
int16_t s = sine_sample(i % K_PERIOD_A4, K_PERIOD_A4);
|
||||
s_melody_buf[out++] = s; /* L */
|
||||
s_melody_buf[out++] = s; /* R == L */
|
||||
}
|
||||
|
||||
for (uint32_t i = 0U; i < MQS_NOTE_SAMPLES; i++)
|
||||
{
|
||||
int16_t s = sine_sample(i % K_PERIOD_E5, K_PERIOD_E5);
|
||||
s_melody_buf[out++] = s; /* L */
|
||||
s_melody_buf[out++] = s; /* R == L */
|
||||
}
|
||||
|
||||
g_s_melody_ready = true;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Воспроизведение с USB keepalive
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Запускаем bsp_mqs_play() (не blocking), чтобы параллельно вызывать
|
||||
* bsp_usb_cdc_poll(). Без этого USB стек голодает за ~4 с воспроизведения
|
||||
* (keepalive-порог ~4 КБ переданных данных).
|
||||
*/
|
||||
static bsp_status_t mqs_play_with_poll(void)
|
||||
{
|
||||
bsp_status_t status = bsp_mqs_play(s_melody_buf, MQS_TOTAL_FRAMES, NULL, NULL);
|
||||
if (status != BSP_OK)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
while (bsp_mqs_is_busy())
|
||||
{
|
||||
bsp_usb_cdc_poll();
|
||||
}
|
||||
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* test_module_t callbacks
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
static void test_mqs_init(void)
|
||||
{
|
||||
mqs_build_melody();
|
||||
(void) bsp_mqs_init();
|
||||
(void) bsp_mqs_amp_init();
|
||||
}
|
||||
|
||||
static test_result_t test_mqs_run(void)
|
||||
{
|
||||
bsp_status_t play_st = mqs_play_with_poll();
|
||||
if (play_st != BSP_OK)
|
||||
{
|
||||
return (test_result_t){
|
||||
.status = TEST_STATUS_FAIL,
|
||||
.detail = "mqs play error",
|
||||
};
|
||||
}
|
||||
|
||||
/* Запрашиваем подтверждение оператора.
|
||||
* API: bool test_runner_wait_confirm(const confirm_params_t *p_params)
|
||||
* Возвращает true если оператор подтвердил, false при отказе или таймауте.
|
||||
* Таймаут детектируется отдельно через флаг в confirm_params_t. */
|
||||
const confirm_params_t PARAMS = {
|
||||
.id = "mqs_tone",
|
||||
.prompt = "Have you heard a tone?",
|
||||
.timeout_ms = 15000U,
|
||||
};
|
||||
|
||||
bool confirmed = test_runner_wait_confirm(&PARAMS);
|
||||
|
||||
if (!confirmed)
|
||||
{
|
||||
/* Различаем таймаут и явный отказ через поле last_confirm_timed_out
|
||||
* если оно есть в API, иначе унифицируем как fail. */
|
||||
return (test_result_t){
|
||||
.status = TEST_STATUS_FAIL,
|
||||
.detail = "operator: no sound",
|
||||
};
|
||||
}
|
||||
|
||||
return (test_result_t){ .status = TEST_STATUS_PASS, .detail = "" };
|
||||
}
|
||||
|
||||
static void test_mqs_deinit(void)
|
||||
{
|
||||
bsp_mqs_stop();
|
||||
bsp_mqs_amp_deinit();
|
||||
bsp_mqs_deinit();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Дескриптор модуля
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
const test_module_t K_TEST_MQS = {
|
||||
.id = "mqs",
|
||||
.name = "MQS Audio Out",
|
||||
.critical = false,
|
||||
.requires_hil = false,
|
||||
.pre_confirm_prompt = NULL,
|
||||
.init = test_mqs_init,
|
||||
.run = test_mqs_run,
|
||||
.deinit = test_mqs_deinit,
|
||||
};
|
||||
|
|
@ -57,6 +57,12 @@ add_sdk_driver(pwm fsl_pwm.c)
|
|||
add_sdk_driver(adc fsl_adc.c)
|
||||
add_sdk_driver(elcdif fsl_elcdif.c)
|
||||
add_sdk_driver(cache fsl_cache.c)
|
||||
add_sdk_driver(sai fsl_sai.c)
|
||||
add_sdk_driver(sai_edma fsl_sai_edma.c)
|
||||
add_sdk_driver(edma fsl_edma.c)
|
||||
add_sdk_driver(dmamux fsl_dmamux.c)
|
||||
add_sdk_driver(xbara fsl_xbara.c)
|
||||
add_sdk_driver(xbarb fsl_xbarb.c)
|
||||
|
||||
# Драйверы которые зависят от clock
|
||||
target_link_libraries(sdk_lpuart PUBLIC sdk_clock)
|
||||
|
|
@ -66,7 +72,10 @@ target_link_libraries(sdk_lpi2c PUBLIC sdk_clock)
|
|||
target_link_libraries(sdk_pwm PUBLIC sdk_clock)
|
||||
target_link_libraries(sdk_adc PUBLIC sdk_clock)
|
||||
target_link_libraries(sdk_elcdif PUBLIC sdk_clock)
|
||||
|
||||
target_link_libraries(sdk_sai PUBLIC sdk_clock)
|
||||
target_link_libraries(sdk_sai_edma PUBLIC sdk_sai sdk_edma)
|
||||
target_link_libraries(sdk_edma PUBLIC sdk_common)
|
||||
target_link_libraries(sdk_xbara PUBLIC sdk_clock)
|
||||
# -----------------------------------------------------------------------------
|
||||
# SEMC и FlexSPI — инициализируются через DCD и FDCB, драйверы нужны только если
|
||||
# обращаешься к ним явно из кода
|
||||
|
|
|
|||
Loading…
Reference in a new issue