我正在使用 I2S DMA 使用双缓冲技术播放一些 wav 文件(固定格式和采样率),
不知何故,当传输开始时,TxHalfCplt Callback仅调用一次,然后停止工作。只有 TxCplt 正常工作。这会导致每次回调时只有缓冲区的后半部分被重新填充,而缓冲区的前半部分留下剩余数据并在 DAC 中播放。
如果我使用
printf
和 UART 来调试它,则在执行 TxHalfCplt 回调时将执行 UART 发送。但其他标志则不然。
这很奇怪。该代码在 RTOS-CMSIS V2 中运行,我已将任务的优先级设置为最高。我还缺少什么?
任务中的代码
uint32_t task_stat = STOP_TASK;
uint32_t StartAddr = 0x00000; // Sample Siren Audio Track
uint32_t EndAddr = 0x4FFFF;
uint32_t currentAddr = 0x00000;
uint32_t flashOffsetHalfBuffer = (AUDIO_BUFF_SIZE / 2) * sizeof(uint16_t); // 2 bytes per Channel sample, each half buffer is 4096 bytes
uint32_t flashOffsetFullBuffer = AUDIO_BUFF_SIZE * sizeof(uint16_t); // 2 bytes per Channel sample, each full buffer is 8196 bytes
/* Infinite loop */
for(;;)
{
if(osMessageQueueGet(test_TaskQueueHandle, &task_stat, NULL, osWaitForever) == osOK){
// Process the received message
printf("TEST STARTED\r\n");
// fill up the buffer (full buffer)
initAudioData(StartAddr, dacData);
currentAddr = StartAddr + flashOffsetFullBuffer;
FLAG_I2S_DATA_READY = 1;
// Start DAC
HAL_I2S_Transmit_DMA(&hi2s1, (uint16_t *)dacData, DMA_MAX(AUDIO_BUFF_SIZE / 2));
} else {
// Handle error
printf("ERR: TASK HANDLE MSG QT\r\n");
}
while (task_stat == RUN_TASK) {
if( FLAG_I2S_DATA_READY == 0 ){
if((currentAddr + flashOffsetHalfBuffer) <= EndAddr){
// Process Audio Data
if(processAudioData(currentAddr, dacPtr) == STAT_OK){
// Increment Start Address
currentAddr += flashOffsetHalfBuffer;
FLAG_I2S_DATA_READY = 1;
// Buffer loading debug output
//if(FLAG_FIRST_HALF_RELOAD == 1){
// printf("F\r\n");
//} else if(FLAG_SECOND_HALF_RELOAD == 1){
// printf("S\r\n");
//}
}
} else {
// Redirect Current Address to Start Address, Hence the Circular Playback
currentAddr = StartAddr;
// Process Audio Data
if(processAudioData(currentAddr, dacPtr) == STAT_OK){
// Increment Start Address
currentAddr += flashOffsetHalfBuffer;
FLAG_I2S_DATA_READY = 1;
// Buffer loading debug output
//if(FLAG_FIRST_HALF_RELOAD == 1){
// printf("F\r\n");
//} else if(FLAG_SECOND_HALF_RELOAD == 1){
// printf("S\r\n");
//}
}
}
}
if(osMessageQueueGet(test_TaskQueueHandle, &task_stat, NULL, 1) == osOK){
// Process the received message
printf("TASK PAUSE\r\n");
HAL_I2S_DMAStop(&hi2s1);
// Purge DAC Buffer?
currentAddr = 0x00000;
// Stop Task
task_stat = STOP_TASK;
}
}
osDelay(1);
}
回调:
void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s){
dacPtr = &dacData[AUDIO_BUFF_SIZE/2];
FLAG_I2S_DATA_READY = 0;
FLAG_FIRST_HALF_RELOAD = 0;
FLAG_SECOND_HALF_RELOAD = 1;
/* Callback debug output*/
//printf("C\r\n");
}
void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s){
dacPtr = &dacData[0];
FLAG_I2S_DATA_READY = 0;
FLAG_FIRST_HALF_RELOAD = 1;
FLAG_SECOND_HALF_RELOAD = 0;
/* Callback debug output*/
//printf("H\r\n");
}
定义:
// DMA Flag
volatile uint8_t FLAG_I2S_DATA_READY = 1;
volatile uint8_t FLAG_FIRST_HALF_RELOAD = 0;
volatile uint8_t FLAG_SECOND_HALF_RELOAD = 0;
// DMA Buffer
uint16_t dacData[AUDIO_BUFF_SIZE];
// Buffer Pointer
static volatile uint16_t *dacPtr = &dacData[0];
看起来这是一个愚蠢的错误。
当我传输 16 位音频数据时,DMA 必须使用半字(16 位)而不是字(32 位)进行配置(即使 I2S 设置为在 32 位帧中传输 16 位数据)。否则中断似乎无法找出中点在哪里。