mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
416 lines
14 KiB
C
416 lines
14 KiB
C
/* Copyright (C) 2023 Alif Semiconductor - All Rights Reserved.
|
|
* Use, distribution and modification of this code is permitted under the
|
|
* terms stated in the Alif Semiconductor Software License Agreement
|
|
*
|
|
* You should have received a copy of the Alif Semiconductor Software
|
|
* License Agreement with this file. If not, please write to:
|
|
* contact@alifsemi.com, or visit: https://alifsemi.com/license
|
|
*
|
|
*/
|
|
|
|
/******************************************************************************
|
|
* @file PDM_testApp.c
|
|
* @author Nisarga A M
|
|
* @email nisarga.am@alifsemi.com
|
|
* @version V1.0.0
|
|
* @date 29-May-2023
|
|
* @brief TestApp to verify PDM(Pulse Density Modulation) interface using
|
|
* FreeRTOS as an operating system.
|
|
* -> Select the PDM channel
|
|
* -> Select the mode of operation in the Control API.
|
|
* -> Select the mode of operation in the Control API.
|
|
* -> Then build the project and flash the generated axf file on the target
|
|
* -> Then Start playing some audio or speak near to the PDM microphone which
|
|
* is on B0 Flat board.
|
|
* -> Once the sample count reaches the maximum value,the PCM samples will be
|
|
* stored in the particular buffer.
|
|
* -> Export the memory and To play the PCM data, use pcmplay.c file which
|
|
* will generate the pcm_samples.pcm audio file
|
|
* -> Use ffplay command to play the audio.
|
|
* -> No hardware setup is required for PDM channel 4 and channel 5.
|
|
* Hardware setup:
|
|
* -> Flat board has internal PDM Mics which is connected to channel 4
|
|
* and channel 5 internally, no hardware connection is required for
|
|
* channel 4 and 5.
|
|
* For channel 4 and channel 5
|
|
* -> Clock line:
|
|
* PDM_C2 (PDM_C2_A) -> P6_7
|
|
* -> Data line:
|
|
* PDM_D2 (PDM_D2_B) -> P5_4
|
|
*
|
|
******************************************************************************/
|
|
/* System Includes */
|
|
#include <stdio.h>
|
|
|
|
/* Project Includes */
|
|
/* include for PDM Driver */
|
|
#include "Driver_PDM.h"
|
|
#include "pinconf.h"
|
|
|
|
#include "FreeRTOS.h"
|
|
#include "FreeRTOSConfig.h"
|
|
#include "task.h"
|
|
#include "RTE_Components.h"
|
|
#include "system_utils.h"
|
|
#if defined(RTE_Compiler_IO_STDOUT)
|
|
#include "retarget_stdout.h"
|
|
#endif /* RTE_Compiler_IO_STDOUT */
|
|
|
|
|
|
/* PDM driver instance */
|
|
extern ARM_DRIVER_PDM Driver_PDM;
|
|
static ARM_DRIVER_PDM *PDMdrv = &Driver_PDM;
|
|
|
|
/*Define for FreeRTOS*/
|
|
#define STACK_SIZE 1024
|
|
#define TIMER_SERVICE_TASK_STACK_SIZE configTIMER_TASK_STACK_DEPTH
|
|
#define IDLE_TASK_STACK_SIZE configMINIMAL_STACK_SIZE
|
|
|
|
StackType_t IdleStack[2 * IDLE_TASK_STACK_SIZE];
|
|
StaticTask_t IdleTcb;
|
|
StackType_t TimerStack[2 * TIMER_SERVICE_TASK_STACK_SIZE];
|
|
StaticTask_t TimerTcb;
|
|
|
|
/* Store the number of samples */
|
|
/* For 30000 samples user can hear maximum up to 4 sec of audio
|
|
* to store maximum samples then change the scatter file and increase the memory */
|
|
#define NUM_SAMPLE 30000
|
|
|
|
/* channel number used for channel configuration and status register */
|
|
#define CHANNEL_4 4
|
|
#define CHANNEL_5 5
|
|
|
|
/* PDM Channel 4 configurations */
|
|
#define CH4_PHASE 0x0000001F
|
|
#define CH4_GAIN 0x0000000D
|
|
#define CH4_PEAK_DETECT_TH 0x00060002
|
|
#define CH4_PEAK_DETECT_ITV 0x0004002D
|
|
|
|
/* PDM Channel 5 configurations */
|
|
#define CH5_PHASE 0x00000003
|
|
#define CH5_GAIN 0x00000013
|
|
#define CH5_PEAK_DETECT_TH 0x00060002
|
|
#define CH5_PEAK_DETECT_ITV 0x00020027
|
|
|
|
TaskHandle_t pdm_xHandle;
|
|
|
|
/****************************** FreeRTOS functions **********************/
|
|
|
|
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
|
|
StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize) {
|
|
*ppxIdleTaskTCBBuffer = &IdleTcb;
|
|
*ppxIdleTaskStackBuffer = IdleStack;
|
|
*pulIdleTaskStackSize = IDLE_TASK_STACK_SIZE;
|
|
}
|
|
|
|
void vApplicationStackOverflowHook(TaskHandle_t pxTask, char *pcTaskName)
|
|
{
|
|
(void) pxTask;
|
|
|
|
for (;;);
|
|
}
|
|
|
|
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
|
|
StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize)
|
|
{
|
|
*ppxTimerTaskTCBBuffer = &TimerTcb;
|
|
*ppxTimerTaskStackBuffer = TimerStack;
|
|
*pulTimerTaskStackSize = TIMER_SERVICE_TASK_STACK_SIZE;
|
|
}
|
|
|
|
void vApplicationIdleHook(void)
|
|
{
|
|
for (;;);
|
|
}
|
|
|
|
typedef enum{
|
|
PDM_CALLBACK_ERROR_EVENT = (1 << 0),
|
|
PDM_CALLBACK_WARNING_EVENT = (1 << 1),
|
|
PDM_CALLBACK_AUDIO_DETECTION_EVENT = (1 << 2)
|
|
}PDM_CB_EVENTS;
|
|
|
|
PDM_CH_CONFIG pdm_coef_reg;
|
|
|
|
/* For Demo purpose use channel 4 and channel 5 */
|
|
/* To store the PCM samples for any Channel */
|
|
uint16_t sample_buf[NUM_SAMPLE];
|
|
|
|
/* Channel 4 FIR coefficient */
|
|
uint32_t ch4_fir[18] = { 0x00000001,0x00000003,0x00000003,0x000007F4,0x00000004,0x000007ED,0x000007F5,0x000007F4,0x000007D3,
|
|
0x000007FE,0x000007BC,0x000007E5,0x000007D9,0x00000793,0x00000029,0x0000072C,0x00000072,0x000002FD};
|
|
|
|
/* Channel 5 FIR coefficient */
|
|
uint32_t ch5_fir[18] = {0x00000000, 0x000007FF,0x00000000,0x00000004,0x00000004,0x000007FC,0x00000000,0x000007FB,0x000007E4,
|
|
0x00000000,0x0000002B,0x00000009,0x00000016,0x00000049,0x00000793,0x000006F8,0x00000045,0x00000178};
|
|
|
|
static void PDM_fifo_callback(uint32_t event)
|
|
{
|
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE, xResult = pdFALSE;
|
|
/* Save received events */
|
|
/* Optionally, user can define specific actions for an event */
|
|
|
|
if(event & ARM_PDM_EVENT_ERROR )
|
|
{
|
|
xTaskNotifyFromISR(pdm_xHandle, PDM_CALLBACK_ERROR_EVENT, eSetBits, &xHigherPriorityTaskWoken);
|
|
|
|
if (xResult == pdTRUE) { portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); }
|
|
}
|
|
|
|
if(event & ARM_PDM_EVENT_CAPTURE_COMPLETE)
|
|
{
|
|
xTaskNotifyFromISR(pdm_xHandle, PDM_CALLBACK_WARNING_EVENT, eSetBits, &xHigherPriorityTaskWoken);
|
|
|
|
if (xResult == pdTRUE) { portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); }
|
|
}
|
|
|
|
if(event & ARM_PDM_EVENT_AUDIO_DETECTION)
|
|
{
|
|
xTaskNotifyFromISR(pdm_xHandle, PDM_CALLBACK_AUDIO_DETECTION_EVENT, eSetBits, &xHigherPriorityTaskWoken);
|
|
|
|
if (xResult == pdTRUE) { portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @fn : void pdm_demo_thread_entry(void *pvParameters)
|
|
* @brief : PDM fifo callback
|
|
* -> Initialize the PDM module.
|
|
* -> Enable the Power for the PDM module
|
|
* -> Select the mode of operation in Control API.The
|
|
* mode which user has selected will be applies to
|
|
* all the channel which user has selected.
|
|
* -> Select the Bypass DC blocking IIR filter for reference.
|
|
* -> Select the PDM channel and use the selected channel
|
|
* configuration and status register values.
|
|
* -> Play some audio and start capturing the data.
|
|
* -> Once all data has stored in the particular buffer ,
|
|
* call back event will be set and it will stop capturing
|
|
* data.
|
|
* -> Once all the data capture is done , go to the particular
|
|
* memory location which user has given for storing PCM data
|
|
* samples.
|
|
* -> Then export the memory and give the total size of
|
|
* the buffer memory and select the particular bin file and export
|
|
* the memory.
|
|
* -> Play the PCM sample file using ffplay command.
|
|
* @return : none
|
|
*/
|
|
void pdm_demo_thread_entry(void *pvParameters)
|
|
{
|
|
int32_t ret = 0;
|
|
ARM_DRIVER_VERSION version;
|
|
int32_t retval;
|
|
uint32_t ulNotificationValue;
|
|
|
|
printf("\r\n >>> PDM demo starting up!!! <<< \r\n");
|
|
|
|
version = PDMdrv->GetVersion();
|
|
printf("\r\n PDM version api:%X driver:%X...\r\n", version.api, version.drv);
|
|
|
|
/* Data line for Channel 4 and 5 (PDM_D2_B -> pin P5_4) */
|
|
retval = pinconf_set(PORT_5, PIN_4, PINMUX_ALTERNATE_FUNCTION_3, PADCTRL_READ_ENABLE);
|
|
if(retval)
|
|
printf("pinconf_set failed\n");
|
|
|
|
/* Clock line for Channel 4 and 5 (PDM_C2_A -> pin P6_7) */
|
|
retval = pinconf_set(PORT_6, PIN_7, PINMUX_ALTERNATE_FUNCTION_3, 0x0);
|
|
if(retval)
|
|
printf("pinconf_set failed\n");
|
|
|
|
/* Initialize PDM driver */
|
|
ret = PDMdrv->Initialize(PDM_fifo_callback);
|
|
if(ret != ARM_DRIVER_OK){
|
|
printf("\r\n Error: PDM init failed\n");
|
|
return;
|
|
}
|
|
|
|
/* Enable the power for PDM */
|
|
ret = PDMdrv->PowerControl(ARM_POWER_FULL);
|
|
if(ret != ARM_DRIVER_OK){
|
|
printf("\r\n Error: PDM Power up failed\n");
|
|
goto error_uninitialize;
|
|
}
|
|
|
|
/* To select the PDM channel 4 and channel 5 */
|
|
ret = PDMdrv->Control(ARM_PDM_SELECT_CHANNEL, ARM_PDM_MASK_CHANNEL_4 |ARM_PDM_MASK_CHANNEL_5, NULL);
|
|
if(ret != ARM_DRIVER_OK){
|
|
printf("\r\n Error: PDM channel select control failed\n");
|
|
goto error_poweroff;
|
|
}
|
|
|
|
/* Select Standard voice PDM mode */
|
|
ret = PDMdrv->Control(ARM_PDM_MODE, ARM_PDM_MODE_AUDIOFREQ_8K_DECM_64, NULL);
|
|
if(ret != ARM_DRIVER_OK){
|
|
printf("\r\n Error: PDM Standard voice control mode failed\n");
|
|
goto error_poweroff;
|
|
}
|
|
|
|
/* Select the DC blocking IIR filter */
|
|
ret = PDMdrv->Control(ARM_PDM_BYPASS_IIR_FILTER, ENABLE, NULL);
|
|
if(ret != ARM_DRIVER_OK){
|
|
printf("\r\n Error: PDM DC blocking IIR control failed\n");
|
|
goto error_poweroff;
|
|
}
|
|
|
|
/* Set Channel 4 Phase value */
|
|
ret = PDMdrv->Control(ARM_PDM_CHANNEL_PHASE, CHANNEL_4, CH4_PHASE);
|
|
if(ret != ARM_DRIVER_OK){
|
|
printf("\r\n Error: PDM Channel_Config failed\n");
|
|
goto error_uninitialize;
|
|
}
|
|
|
|
/* Set Channel 4 Gain value */
|
|
ret = PDMdrv->Control(ARM_PDM_CHANNEL_GAIN, CHANNEL_4, CH4_GAIN);
|
|
if(ret != ARM_DRIVER_OK){
|
|
printf("\r\n Error: PDM Channel_Config failed\n");
|
|
goto error_uninitialize;
|
|
}
|
|
|
|
/* Set Channel 4 Peak detect threshold value */
|
|
ret = PDMdrv->Control(ARM_PDM_CHANNEL_PEAK_DETECT_TH, CHANNEL_4, CH4_PEAK_DETECT_TH);
|
|
if(ret != ARM_DRIVER_OK){
|
|
printf("\r\n Error: PDM Channel_Config failed\n");
|
|
goto error_uninitialize;
|
|
}
|
|
|
|
/* Set Channel 4 Peak detect ITV value */
|
|
ret = PDMdrv->Control(ARM_PDM_CHANNEL_PEAK_DETECT_ITV, CHANNEL_4, CH4_PEAK_DETECT_ITV);
|
|
if(ret != ARM_DRIVER_OK){
|
|
printf("\r\n Error: PDM Channel_Config failed\n");
|
|
goto error_uninitialize;
|
|
}
|
|
|
|
pdm_coef_reg.ch_num = 4;
|
|
memcpy(pdm_coef_reg.ch_fir_coef, ch4_fir, sizeof(pdm_coef_reg.ch_fir_coef)); /* Channel 4 fir coefficient */
|
|
pdm_coef_reg.ch_iir_coef = 0x00000004; /* Channel IIR Filter Coefficient */
|
|
|
|
ret = PDMdrv->Config(&pdm_coef_reg);
|
|
if(ret != ARM_DRIVER_OK){
|
|
printf("\r\n Error: PDM Channel_Config failed\n");
|
|
goto error_uninitialize;
|
|
}
|
|
|
|
/* Set Channel 5 Phase value */
|
|
ret = PDMdrv->Control(ARM_PDM_CHANNEL_PHASE, CHANNEL_5, CH5_PHASE);
|
|
if(ret != ARM_DRIVER_OK){
|
|
printf("\r\n Error: PDM Channel_Config failed\n");
|
|
goto error_uninitialize;
|
|
}
|
|
|
|
/* Set Channel 5 Gain value */
|
|
ret = PDMdrv->Control(ARM_PDM_CHANNEL_GAIN, CHANNEL_5, CH5_GAIN);
|
|
if(ret != ARM_DRIVER_OK){
|
|
printf("\r\n Error: PDM Channel_Config failed\n");
|
|
goto error_uninitialize;
|
|
}
|
|
|
|
/* Set Channel 5 Peak detect threshold value */
|
|
ret = PDMdrv->Control(ARM_PDM_CHANNEL_PEAK_DETECT_TH, CHANNEL_5, CH5_PEAK_DETECT_TH);
|
|
if(ret != ARM_DRIVER_OK){
|
|
printf("\r\n Error: PDM Channel_Config failed\n");
|
|
goto error_uninitialize;
|
|
}
|
|
|
|
/* Set Channel 5 Peak detect ITV value */
|
|
ret = PDMdrv->Control(ARM_PDM_CHANNEL_PEAK_DETECT_ITV, CHANNEL_5, CH5_PEAK_DETECT_ITV);
|
|
if(ret != ARM_DRIVER_OK){
|
|
printf("\r\n Error: PDM Channel_Config failed\n");
|
|
goto error_uninitialize;
|
|
}
|
|
|
|
pdm_coef_reg.ch_num = 5;
|
|
memcpy(pdm_coef_reg.ch_fir_coef, ch5_fir, sizeof(pdm_coef_reg.ch_fir_coef)); /* Channel 5 fir coefficient */
|
|
pdm_coef_reg.ch_iir_coef = 0x00000004; /* Channel IIR Filter Coefficient */
|
|
|
|
ret = PDMdrv->Config(&pdm_coef_reg);
|
|
if(ret != ARM_DRIVER_OK){
|
|
printf("\r\n Error: PDM Channel_Config failed\n");
|
|
goto error_uninitialize;
|
|
}
|
|
|
|
printf("\n------> Start Speaking or Play some Audio!------> \n");
|
|
|
|
/* Receive the audio samples */
|
|
ret = PDMdrv->Receive((uint16_t*)sample_buf, NUM_SAMPLE);
|
|
if(ret != ARM_DRIVER_OK){
|
|
printf("\r\n Error: PDM Receive failed\n");
|
|
goto error_capture;
|
|
}
|
|
|
|
/* wait for the call back event */
|
|
xTaskNotifyWait(NULL, PDM_CALLBACK_ERROR_EVENT | PDM_CALLBACK_WARNING_EVENT | \
|
|
PDM_CALLBACK_AUDIO_DETECTION_EVENT, \
|
|
&ulNotificationValue, \
|
|
portMAX_DELAY);
|
|
|
|
/* PDM channel audio detection event */
|
|
if(ulNotificationValue & PDM_CALLBACK_AUDIO_DETECTION_EVENT)
|
|
{
|
|
printf("\n PDM audio detect event: data in the audio channel");
|
|
}
|
|
|
|
/* PDM fifo alomost full warning event */
|
|
if(ulNotificationValue & PDM_CALLBACK_WARNING_EVENT)
|
|
{
|
|
printf("\n PDM warning event : Fifo almost full\n");
|
|
}
|
|
|
|
/* PDM fifo overflow error event */
|
|
if(ulNotificationValue & PDM_CALLBACK_ERROR_EVENT)
|
|
{
|
|
printf("\n PDM error event: Fifo overflow \n");
|
|
}
|
|
|
|
printf("\n------> Stop recording ------> \n");
|
|
printf("\n--> PCM samples will be stored in 0x%p address and size of buffer is %d\n", sample_buf, sizeof(sample_buf));
|
|
printf("\n ---END--- \r\n <<< wait forever >>> \n");
|
|
while(1);
|
|
|
|
error_capture:
|
|
error_poweroff:
|
|
ret = PDMdrv->PowerControl(ARM_POWER_OFF);
|
|
if(ret != ARM_DRIVER_OK)
|
|
{
|
|
printf("\n Error: PDM power off failed\n");
|
|
}
|
|
|
|
error_uninitialize:
|
|
ret = PDMdrv->Uninitialize();
|
|
if(ret != ARM_DRIVER_OK)
|
|
{
|
|
printf("\n Error: PDM Uninitialize failed\n");
|
|
}
|
|
|
|
printf("\r\n XXX PDM demo exiting XXX...\r\n");
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* Main: Initialize and start the FreeRTOS Kernel
|
|
*---------------------------------------------------------------------------*/
|
|
int main(void)
|
|
{
|
|
#if defined(RTE_Compiler_IO_STDOUT_User)
|
|
int32_t ret;
|
|
ret = stdout_init();
|
|
if(ret != ARM_DRIVER_OK)
|
|
{
|
|
while(1)
|
|
{
|
|
}
|
|
}
|
|
#endif
|
|
/* System Initialization */
|
|
SystemCoreClockUpdate();
|
|
|
|
/* Create application main thread */
|
|
BaseType_t xReturned = xTaskCreate(pdm_demo_thread_entry, "PDMFreertos", 256, NULL,configMAX_PRIORITIES-1, &pdm_xHandle);
|
|
if (xReturned != pdPASS)
|
|
{
|
|
vTaskDelete(pdm_xHandle);
|
|
return -1;
|
|
}
|
|
|
|
/* Start thread execution */
|
|
vTaskStartScheduler();
|
|
}
|