Kiến thức âm thanh cơ bản

Buffer DMA trong I2S: vì sao âm thanh bị giật, rè và cách khắc phục

Cimo 8 phút đọc

Âm thanh ESP32 qua I2S bị giật, rè thường do buffer DMA bị cạn đúng lúc cần phát. Bài viết hướng dẫn tính thời gian đệm, khoanh vùng underrun theo triệu chứng và khắc phục từng nguyên nhân.

Buffer DMA trong I2S: vì sao âm thanh bị giật, rè và cách khắc phục
Mục lục
  1. Triệu chứng và phạm vi của bài viết
  2. Buffer DMA trong I2S hoạt động như thế nào?
  3. Cách tính thời gian đệm từ cấu hình DMA
  4. Tên tham số khác nhau giữa hai thế hệ driver
  5. Quy trình khoanh vùng theo thứ tự dễ làm trước
  6. Bảng xử lý lỗi theo triệu chứng
  7. Chọn kích thước buffer: đánh đổi độ trễ và độ ổn định
  8. Cách xác nhận lỗi đã được giải quyết
  9. Câu hỏi thường gặp
  10. Tăng buffer DMA lên thật lớn có hết giật không?
  11. Buffer DMA I2S bao nhiêu mili giây là đủ?
  12. Vì sao tắt Wi-Fi thì hết rè nhưng bật Wi-Fi lại rè?
  13. Nâng cấp từ ESP-IDF v4.4 lên v5 thì chỉnh buffer DMA ở đâu?

Âm thanh phát qua I2S bị giật, rè hoặc lặp đoạn thường không bắt đầu từ loa hay DAC, mà từ việc buffer DMA I2S bị cạn đúng lúc phần cứng cần dữ liệu. DMA (Direct Memory Access, truy cập bộ nhớ trực tiếp) là cơ chế phần cứng tự chuyển dữ liệu giữa RAM và ngoại vi I2S mà CPU không phải chép từng mẫu; khi task nạp dữ liệu đến chậm, bộ đệm rỗng — tình trạng gọi là underrun (thiếu dữ liệu ở phía phát) — ngoại vi buộc phải phát lại dữ liệu cũ hoặc im lặng tùy cấu hình, và tai nghe thành giật, lặp, rè kiểu robot. Bài này giúp bạn tính thời gian đệm, khoanh vùng nguyên nhân theo triệu chứng và sửa đúng chỗ, thay vì tăng buffer mù quáng.

Triệu chứng và phạm vi của bài viết

Bài viết tập trung vào luồng phát (phía TX) trên ESP32 và ESP32-S3 dùng ESP-IDF: giật theo chu kỳ, rè khi bật Wi-Fi, lặp một đoạn ngắn, im lặng ngắt quãng và giật khi đọc từ thẻ nhớ, flash hoặc PSRAM. Rè do nhiễu nguồn, vòng lặp mass hoặc clipping số là nhóm khác, không xử lý ở đây. Nếu chưa nắm đường đi tổng thể của âm thanh số trên ESP32, hãy đọc trước bài ESP32 xử lý âm thanh số: kiến trúc và giới hạn thực tế rồi quay lại đây.

Buffer DMA trong I2S hoạt động như thế nào?

I2S phát dữ liệu liên tục theo xung clock, không chờ CPU rảnh. Để CPU không phải canh từng mẫu, DMA tự đọc dữ liệu từ một vùng đệm vòng trong RAM và đưa ra ngoại vi đúng nhịp clock. Vùng đệm này chia thành nhiều descriptor (mô tả DMA: khối quản lý địa chỉ, độ dài và trạng thái của một đoạn đệm) nối tiếp nhau thành vòng tròn. Dữ liệu trong mỗi descriptor được tính bằng frame (khung mẫu, gồm một mẫu của mỗi kênh tại cùng một thời điểm; với stereo, một frame gồm mẫu trái và mẫu phải).

Task ứng dụng ghi dữ liệu mới vào descriptor đang trống, DMA lần lượt phát descriptor đã đầy. Khi tới lượt phát mà descriptor chưa có dữ liệu mới, underrun xảy ra. Đây là lỗi về nhịp và thời gian, không phải chất lượng mẫu, nên cùng một file có thể mượt ở cấu hình này nhưng giật ở cấu hình khác.

Người đang kiểm tra và gỡ lỗi bo mạch âm thanh ESP32 cùng laptop trên bàn làm việc

Cách tính thời gian đệm từ cấu hình DMA

Thời gian đệm suy ra từ cấu hình DMA bằng công thức sau (driver I2S mới của ESP-IDF):

Tổng thời gian đệm (giây) ≈ dma_desc_num × dma_frame_num / sample_rate

Trong đó dma_desc_num là số descriptor, dma_frame_num là số frame mỗi descriptor, sample_rate tính bằng Hz. Giả định: phát liên tục ở sample rate cố định, mọi descriptor đều tham gia đệm; frame đã gồm mọi kênh nên số kênh không đổi kết quả. Đây là tính toán lý thuyết để so sánh cấu hình, không phải kết quả đo thực tế.

Cấu hình ví dụTổng số frameThời gian đệm ở 48 kHzNhận xét
6 descriptor × 240 frame1440 frame30 msĐệm dày, chịu trễ nạp biến động tốt hơn
3 descriptor × 128 frame384 frame8 msĐệm mỏng, trễ thấp nhưng dễ underrun hơn

Tức là 6 × 240 = 1440 frame tương ứng 30 ms đệm, còn 3 × 128 = 384 frame chỉ tương ứng 8 ms. Chỉ đổi vài con số khi khởi tạo I2S cũng có thể khiến dự án đang giật thành ổn định, hoặc ngược lại.

Tên tham số khác nhau giữa hai thế hệ driver

Trên ESP-IDF từ v5.0, driver mới ở driver/i2s_std.h đặt cấu hình DMA trong i2s_chan_config_t với hai trường dma_desc_num và dma_frame_num. Trên ESP-IDF v4.4 trở về trước, driver cũ ở driver/i2s.h dùng cặp tương đương là dma_buf_count và dma_buf_len. Nguyên tắc không đổi: tổng đệm là tích hai giá trị, quy đổi sang frame rồi chia cho sample rate. Hãy xác định đúng thế hệ driver trước khi sao chép con số từ code mẫu.

Quy trình khoanh vùng theo thứ tự dễ làm trước

  • Ghi lại triệu chứng kèm điều kiện: giật đều theo chu kỳ, chỉ giật khi bật Wi-Fi, hay chỉ khi đọc file từ thẻ nhớ. Điều kiện đi kèm thường chỉ thẳng tới nguyên nhân.
  • Tính lại thời gian đệm. Nếu chỉ vài mili giây mà nguồn dữ liệu trễ không đều, underrun do đệm mỏng là khả năng cao nhất.
  • Tách nguồn dữ liệu để đối chứng: phát thử đoạn âm thanh nạp sẵn trong RAM. Nếu từ RAM mượt, từ thẻ hoặc mạng lại giật thì lỗi ở khâu nạp, không phải cấu hình I2S.
  • Kiểm tra task nạp: ưu tiên quá thấp, bị chặn lâu chờ khóa hoặc hàng đợi, hay xử lý DSP nặng cùng task.
  • Kiểm tra cách ghi I2S: ghi với thời gian chờ bằng 0 rồi bỏ qua dữ liệu sẽ làm mất âm thầm một phần âm thanh.
  • Kiểm tra tranh chấp tài nguyên: tắt thử Wi-Fi và Bluetooth, vì hoạt động vô tuyến tranh CPU và gây trễ ngắt.
  • Sửa từng thay đổi một rồi nghe lại cùng đoạn kiểm tra đủ dài.

Bảng xử lý lỗi theo triệu chứng

Triệu chứngNguyên nhân có thểCách kiểm traCách khắc phục
Giật, vấp đều theo chu kỳ ngắnTổng buffer quá nhỏ so với độ biến động của khâu nạp; task nạp bị chặn định kỳTính thời gian đệm; tăng thử số descriptor hoặc số frame từng bước rồi nghe lạiTăng tổng đệm vừa đủ; nâng độ ưu tiên của task nạp
Rè, vỡ tiếng kiểu robot khi bật Wi-Fi hoặc BluetoothVô tuyến tranh CPU, gây trễ ngắt khiến nạp chậm theo đợtTắt thử Wi-Fi, Bluetooth, phát lại cùng đoạn và so với phát từ RAMTăng thời gian đệm; tách việc nạp khỏi tác vụ mạng; giảm xử lý trong lúc phát
Lặp lại một đoạn âm thanh ngắnUnderrun phía phát: DMA không có dữ liệu mới nên phát lại dữ liệu cũ trong descriptorĐối chiếu thời điểm lặp với lúc task nạp bị chặnSửa nguyên nhân nạp chậm trước; tăng đệm nếu trễ nạp vẫn biến động
Im lặng ngắt quãng rồi có tiếng trở lạiBuffer cạn hẳn một khoảng; hoặc ghi I2S chờ bằng 0 rồi bỏ qua dữ liệuXem thời gian chờ và việc kiểm tra giá trị trả về trong code ghi I2SĐặt thời gian chờ hợp lý khi ghi và xử lý trường hợp ghi không thành công
Giật khi đọc file từ thẻ nhớ, flash hoặc PSRAMTrễ đọc của nguồn không đều, có lúc vượt quá thời gian đệmPhát thử cùng nội dung từ RAM; ghi log thời gian từng lần đọcĐọc trước vào bộ đệm trung gian lớn hơn; tách task đọc và task phát riêng
Buffer lúc đầy lúc cạn sau khi đổi sample rateNạp và phát lệch tốc độ, ví dụ nguồn 44,1 kHz, phát 48 kHz, không chuyển đổi đúngSo sample rate của nguồn với cấu hình I2SThống nhất một sample rate hoặc chuyển đổi tốc độ mẫu đúng trước khi nạp

Riêng lệch tốc độ mẫu, buffer không tự bù mãi được: mức đệm trôi dần về một phía cho tới khi đầy hoặc cạn, nên tăng buffer chỉ trì hoãn thời điểm giật. Cách xử lý gốc rễ có trong bài xử lý âm thanh 44,1 kHz và 48 kHz trên ESP32.

Chọn kích thước buffer: đánh đổi độ trễ và độ ổn định

Buffer lớn ít giật hơn nhưng độ trễ tổng thể tăng, khó chịu với ứng dụng tương tác, đồng bộ thời gian thực như micro hát trực tiếp. Buffer nhỏ trễ thấp nhưng nhạy: một lần đọc thẻ chậm hay một đợt tranh CPU cũng đủ gây underrun. Thứ tự đúng: sửa nguyên nhân nạp chậm trước, rồi tăng đệm tới mức vừa đủ. Phát nhạc từ thẻ nhớ thường chấp nhận đệm vài chục mili giây; ứng dụng cần phản hồi nhanh nên giữ đệm mỏng và làm đường nạp đều đặn.

Cách xác nhận lỗi đã được giải quyết

Kiểm tra lại bằng đúng kịch bản từng gây lỗi: phát đoạn dài, bật Wi-Fi và Bluetooth như khi dùng thật, đọc từ đúng nguồn thật, nghe đủ lâu để trễ định kỳ có cơ hội xuất hiện, và xem log để chắc chắn không còn lần ghi I2S nào bị bỏ qua. Nếu vẫn giật kèm mất đồng bộ sau các bước trên, vấn đề có thể nằm ở clock và định dạng I2S — hãy kiểm tra các thông số nền tảng thay vì tăng buffer tiếp.

Câu hỏi thường gặp

Tăng buffer DMA lên thật lớn có hết giật không?

Không hẳn. Nếu task nạp bị chặn lâu hơn cả thời gian đệm, hoặc nạp và phát lệch tốc độ do sai sample rate, underrun vẫn xảy ra, chỉ muộn hơn. Sửa nguyên nhân nạp chậm trước, tăng đệm sau.

Buffer DMA I2S bao nhiêu mili giây là đủ?

Không có con số chung: mức đủ phụ thuộc độ biến động trễ của nguồn dữ liệu và yêu cầu độ trễ của ứng dụng. Hãy tính thời gian đệm theo công thức trong bài rồi tăng từng bước nhỏ tới khi ổn định.

Vì sao tắt Wi-Fi thì hết rè nhưng bật Wi-Fi lại rè?

Vì vô tuyến tranh CPU và gây trễ ngắt, khiến task nạp vào buffer DMA chậm theo đợt. Đệm mỏng không đủ dự trữ nên underrun xảy ra, nghe thành rè, vỡ tiếng.

Nâng cấp từ ESP-IDF v4.4 lên v5 thì chỉnh buffer DMA ở đâu?

Ở v5, thông số nằm trong i2s_chan_config_t: dma_desc_num và dma_frame_num. Ở v4.4, cặp tương đương là dma_buf_count và dma_buf_len.

Nếu đang làm dự án âm thanh ESP32 và cần chọn bo, module DAC I2S phù hợp, hãy tham khảo danh mục linh kiện tại cimo.vn để đối chiếu thông số trước khi lắp.

#buffer DMA I2S
Chia sẻ bài viết
Facebook
Cimo
Cimo
Cimo - Mọi thứ bạn cần với mức giá tốt nhất

Bài viết liên quan

Tất cả danh mục
Flash Sale
Ưu đãi hôm nay
Đấu giá