Lập trình STM32 đọc ADC nhiều kênh kết hợp DMA sẽ giúp bạn hiểu được:
- Cách sử dụng chế độ Scan Mode
- Cách sử dụng DMA cho ADC
- Cách lập trình ADC nhiều kênh trên STM32
Bài 9 trong serie Học lập trình STM32 từ A tới Z
ADC nhiều kênh sử dụng chế độ Scan Mode
Chế độ Scan chỉ được bật khi sử dụng từ 2 kênh ADC trở lên.
Nếu bit CONT được set lên 1, sau khi scan hết các kênh, ADC tự động lặp lại thao tác scan.
Nếu bit CONT không được bật, sau khi ADC scan hết các kênh sẽ dừng lại.
Sau khi chuyển đổi xong tại mỗi kênh, giá trị thu được sẽ lưu vào thanh ghi DR và yêu cầu DMA được sinh ra (cờ EOC chỉ bật khi đã scan xong cả nhóm kênh).
Khi chuyển đổi xong kênh tiếp theo, giá trị mới sẽ ghi đè lên thanh ghi DR. Vì chỉ có 1 thanh ghi DR dùng chung cho mọi kênh, nên chúng ta cần DMA để kịp chuyển từng giá trị đi trước khi bị ghi đè.
Khi sử dụng DMA, mỗi giá trị chuyển đổi sẽ được bộ DMA tự động chép từ thanh ghi DR sang một mảng trong SRAM mà CPU không phải làm gì. DMA giống như một anh shipper: cứ có hàng ở cửa (DR) là tự mang vào kho (mảng trong RAM), chủ nhà (CPU) chỉ việc mở kho ra lấy.

Cấu hình ADC nhiều kênh trên STM32 CubeMX
Trong bài này chúng ta sẽ sử dụng ADC Scan mode 2 kênh CH0 và CH1, không sử dụng mode CONT và dùng DMA để lưu giá trị.
Tạo project, chọn chip STM32F103C8, trong SYS chọn Debug: Serial Wire. Chi tiết trong Bài 3.
Phần Mode tick chọn IN0 và IN1.

Phần Configuration, tab Parameter Settings:
Number Of Conversion: 2, chọn số kênh cần chuyển đổi
Khi đó Scan Conversion Mode sẽ tự động Enable
Continuous Conversion Mode: Disable, tắt chế độ liên tục
Tại Rank 2: chọn thêm Channel 1, chọn chu kì lấy mẫu cho mỗi Channel tại Sampling Time
Rank là thứ tự chuyển đổi, từ 1 đến tối đa 16. Bộ ADC sẽ scan từ kênh có Rank là 1 đến n, với n là số kênh các bạn cần chuyển đổi.

Trong DMA Settings:
Mục DMA Request thêm kênh ADC1, các mục còn lại sẽ tự động được cài đặt theo.
Mode: Circular, với mode này mỗi khi ghi hết mảng, DMA sẽ tự quay về đầu mảng và ghi tiếp, không cần khởi động lại.
Data Width: Half Word (16 bit)

Lưu ý: Data Width Half Word phải khớp với kiểu của mảng nhận dữ liệu là uint16_t. Nếu khai báo mảng uint32_t mà DMA để Half Word (hoặc ngược lại), giá trị 2 kênh sẽ bị ghép lẫn vào nhau và đọc ra số rất vô lý.
Ok, xong phần cấu hình, các bạn đặt tên, chọn Toolchain là MDK-ARM V5 rồi Gen code ra thôi.

Lập trình ADC nhiều kênh chế độ Scan Mode
Mở project, các bạn thêm biến u16_ADCScanVal[2];

Rồi khởi động ADC với DMA và trỏ DMA tới biến vừa tạo để chứa giá trị ADC. Vì là chế độ Single nên bạn phải gọi hàm Start trong while(1), mỗi lần chuyển đổi cách nhau 100ms.

Phần cứng mình sẽ kết nối với module joystick để đọc 2 giá trị X và Y nhé. Các bạn nối như sau:
- Chân +5V (VCC) của joystick nối vào 3.3V của Blue Pill (không nối 5V vì chân ADC chỉ chịu tối đa 3.3V)
- GND nối GND
- VRX nối PA0 (ADC kênh 0)
- VRY nối PA1 (ADC kênh 1)

Bật Debug lên, tìm tới biến vừa khởi tạo và Add to Watch 1.

Nhấn F5 để Run và xem kết quả, gạt joystick sẽ thấy 2 giá trị thay đổi theo.

Với chế độ Scan Continuous ta chỉ cần bật bit CONT bằng câu lệnh:
hadc1.Init.ContinuousConvMode = ENABLE;

Sau đó chuyển lệnh Start lên trên while(1).

Như vậy ADC sẽ hoạt động liên tục, mảng u16_ADCScanVal luôn chứa giá trị mới nhất của 2 kênh.
Khi cần dừng bộ ADC đang chạy cùng DMA, chúng ta sử dụng lệnh:
HAL_ADC_Stop_DMA(&hadc1);
Lưu ý: ADC đã start bằng HAL_ADC_Start_DMA() thì phải dừng bằng HAL_ADC_Stop_DMA(). Nếu chỉ gọi HAL_ADC_Stop() thì kênh DMA vẫn còn bật, lần Start_DMA sau sẽ trả về lỗi HAL_BUSY.
Kết
ADC nhiều kênh được sử dụng rất nhiều trong việc đọc tín hiệu từ các loại cảm biến analog. Bộ ADC là một ngoại vi cơ bản, bắt buộc phải nắm rõ khi học lập trình vi điều khiển, cụ thể hơn là lập trình STM32. Hẹn gặp lại các bạn trong bài tiếp theo nhé!


Bạn có thể giúp mình scan nhiều channels sử dụng Timer không ? Mình đang muốn sử dụng ADC2 (STM32F103VBT6) đọc 3 tín hiệu ADC trên 3 chân liên tục. Sử dụng STM32CubeMX thì thấy ADC2 không support DMA. Thanks in advance !
Bạn có thể tìm hiểu Timer Trigger, mình ko có time viết về chủ đề này
hi anh, em đang làm bài tương tự như trên nhưng mà em k dùng stm32f1 mà em dùng kit stm32f429i nhưng mà k biết vì sao vẫn không đọc được giá trị từ joystick ạ. trong cubemx em thấy chân PA0 và PA1 kit đã dùng, nên em dùng chanel 5 và chanel 13 của ADC 1 để thay thế, 2 chân này em nối vào VRX và VRY của joystick. Các cấu hình khác em làm giống anh, nhưng mà vào debug thì giá trị đều bằng 0 ạ. Không biết là cách cấu hình trên stm32f429i của em có phù hợp với code trên bài anh hướng dẫn không ạ?