佬们好,stc8a8k64d4,用的stc官方库函数,设置好了dma_adc转运,我判断dmaadcflag是否置1发送串口消息,但是一直发不出去(我很确定是dmaadcflag没有置1的问题,因为没了这个判断串口发送是正常的),然后我跟官方例程也对了很久,没看出来有啥问题。如图所示,然后我dma转运到的是u16的十六个元素的数组。查了三个小时没查出来啥问题,我就是个苦学生,求求了。
Kiểm tra kỹ phần ngắt! Tôi thấy bạn đã bật NVIC_DMA_ADC_Init, nhưng bạn có viết hàm phục vụ ngắt tương ứng không? Tên hàm ngắt của STC là cố định, không được tự ý đặt tên lung tung, nếu viết sai thì sẽ không vào được ngắt, cờ hiệu nhiên không thể được đặt thành 1. Ngoài ra, đừng nhầm lẫn số ngắt với các ngắt khác.
Vấn đề này là một lỗi rất điển hình đối với người mới khi phát triển DMA-ADC trên STC8A8K64D4. Tôi sẽ phân tích chi tiết vấn đề và giải pháp theo thứ tự ưu tiên từ cao đến thấp, bạn chỉ cần kiểm tra tuần tự theo trình tự này là có thể giải quyết được.
Một, Vấn đề cốt lõi bắt buộc phải kiểm tra (90% khả năng sai ở đây)
1. Phải bật chức năng DMA cho ADC, nếu không ADC sẽ không kích hoạt truyền DMA
Trong mã khởi tạo ADC của bạn, hoàn toàn chưa bật chức năng DMA cho ADC! Trên vi điều khiển STC8A, để ADC hoạt động cùng DMA, bạn phải thiết lập sao cho sau khi chuyển đổi xong, ADC tự động kích hoạt DMA. Nếu chưa bật công tắc này, dù ADC hoàn thành chuyển đổi thì DMA cũng không hoạt động, do đó sẽ không có ngắt hoàn thành truyền, cờ báo hiệu cũng sẽ không bao giờ được đặt thành 1.
- Cách sửa: Thêm cấu hình bật DMA trong cấu trúc khởi tạo ADC:
ADC_InitStructure.ADC_DMA_Enable = ENABLE; // Cốt lõi: Bật chức năng kích hoạt DMA của ADC
- Nếu thư viện của bạn không có thành viên này, hãy trực tiếp thao tác thanh ghi:
ADC_CONTR |= 0x40; // Đặt bit ADC_DMA_EN, vị trí cụ thể căn cứ theo sổ tay chip của bạn
2. Sai cấu hình khởi tạo DMA — chưa thiết lập địa chỉ bộ đệm đích
Trong mã của bạn, dòng DMA_ADC_InitStructure.DMA_Buffer = ADC_Result_Once; là hoàn toàn sai:
-
ADC_Result_Oncelà một macro chế độ truyền (truyền đơn lần), chứ không phải địa chỉ bộ đệm -
Bạn chưa thiết lập địa chỉ của mảng u16 mà bạn định nghĩa, do đó DMA không biết phải truyền kết quả ADC tới đâu, dẫn đến không thể hoàn tất truyền dữ liệu.
-
Cấu hình DMA đúng mẫu (phù hợp với thư viện chuẩn chính thức của STC):
u16 AdcResultBuf[16]; // Mảng u16 16 phần tử như bạn nói
DMA_ADC_InitStructure.DMA_Enable = ENABLE;
DMA_ADC_InitStructure.DMA_Channel = 0x0003; // Kênh DMA 3 dành cho ADC — cấu hình này của bạn là đúng
DMA_ADC_InitStructure.DMA_Mode = ADC_Result_Once; // Chế độ truyền đơn lần — đặt tham số chế độ ở đây
DMA_ADC_InitStructure.DMA_Times = 16; // Số lần truyền phải khớp với độ dài mảng, bạn viết 8 là sai
DMA_ADC_InitStructure.DMA_AdcResult = AdcResultBuf; // Cốt lõi: Gán địa chỉ mảng đích
3. Biến cờ phải dùng volatile, và phải được đặt trong hàm ngắt
Bạn không dán đoạn mã hàm ngắt và khai báo biến cờ — đây là điểm dễ bị bỏ sót nhất:
-
Vấn đề 1: Nếu
DmaADCFlagđược sửa trong ngắt nhưng kiểm tra trong hàm main, bắt buộc phải dùng từ khóavolatile, nếu không trình biên dịch sẽ tối ưu bỏ câu lệnh if, cho rằng biến này không thay đổi, khiến dù ngắt đã đặt thành 1 thì hàm main vẫn không nhìn thấy. -
Vấn đề 2: Phải viết đúng hàm phục vụ ngắt DMA-ADC và đặt cờ trong đó, nếu không biến cờ sẽ luôn bằng 0.
-
Cách viết đúng:
volatile u8 DmaADCFlag = 0; // BẮT BUỘC phải có volatile!!!
// Hàm phục vụ ngắt, số ngắt sau interrupt phải khớp với định nghĩa trong thư viện chính thức (thường là INT_NO_DMA_ADC, giá trị 22)
void DMA_ADC_Isr(void) interrupt INT_NO_DMA_ADC
{
DmaADCFlag = 1; // Truyền xong, đặt cờ
}
Hai, Những vấn đề phụ nhưng cũng phải sửa
1. Số lần truyền DMA không khớp với độ dài mảng
Bạn nói mảng có 16 phần tử u16, nhưng trong mã lại viết DMA_Times=ADC_8_Times (8 lần). Số lần truyền không khớp với độ dài mảng — ngay cả khi các phần khác đúng, dữ liệu cũng chỉ truyền một nửa, logic phía sau sẽ sai. Bạn phải sửa thành giá trị tương ứng với độ dài mảng.
2. Chế độ chuyển đổi ADC và sự phối hợp với kích hoạt DMA
ADC của bạn chưa cấu hình chế độ chuyển đổi, mặc định là chế độ đơn lần. Sau mỗi lần truyền DMA hoàn tất, bạn phải khởi động lại chuyển đổi ADC, nếu không ADC sẽ không chuyển đổi nữa và DMA cũng không được kích hoạt thêm lần nào.
- Gợi ý: Chuyển sang chế độ chuyển đổi liên tục, thêm vào phần khởi tạo ADC:
ADC_InitStructure.ADC_Conversion = ADC_CONVERSION_CONTINUOUS; // Bật chế độ chuyển đổi liên tục
- Hoặc sau mỗi lần kích hoạt lại DMA, thủ công khởi động lại ADC:
DMA_ADC_TRIG();
ADC_START(); // Macro khởi động ADC trong thư viện chính thức, căn cứ theo thư viện bạn đang dùng
Ba, Gợi ý trình tự kiểm tra lỗi
- Trước tiên, thêm
volatilecho biến cờ, viết đúng hàm phục vụ ngắt, đảm bảo ngắt có thể được thực thi bình thường; - Sửa lại khởi tạo ADC, thêm bật chức năng DMA cho ADC;
- Sửa lại khởi tạo DMA, thiết lập đúng chế độ truyền, số lần truyền và địa chỉ mảng đích;
- Kiểm tra tổng ngắt
EA=1và bật ngắt DMA đã được cấu hình chưa; - Trước tiên đơn giản hóa thử nghiệm: đổi số lần truyền thành 1, mảng thành 1 phần tử, trước tiên chạy thử thành công với truyền đơn lần, sau đó mới quay lại 16 lần.

