Bài 8: Lập trình STM32 đọc ADC một kênh

ADC một kênh

Lập trình STM32 đọc ADC một kênh sẽ giúp các bạn:

  • Hiểu ADC là gì
  • Cách cấu hình ADC trên STM32 CubeMX
  • Cách lập trình ADC chế độ Single Mode và Continuous Mode

Bài 8 trong serie Học lập trình STM32 từ A tới Z

ADC là gì?

ADC (Analog to Digital Converter) là bộ chuyển đổi tín hiệu tương tự sang số. Đại lượng tương tự là điện áp Vin được so sánh với điện áp mẫu Vref (giá trị lớn nhất), sau đó được chuyển đổi thành số lưu vào thanh ghi dữ liệu của bộ chuyển đổi đó.

Các bạn hình dung giống một chiếc cốc có chia vạch: Vref là lúc cốc đầy, cốc được chia thành 4096 vạch, ADC sẽ cho chúng ta biết mực nước (điện áp) đang ở vạch thứ bao nhiêu.

Có 2 tham số quan trọng của bộ ADC cần lưu ý:

  • Tốc độ lấy mẫu (sampling): được tính theo số chu kì chuyển đổi
  • Độ phân giải: tính theo bit, bộ ADC có độ phân giải 10 bit sẽ có 2^10 = 1024 giá trị
H1

Các chức năng chính của ADC trong STM32

Độ phân giải 12 bit

Sinh ra ngắt tại các sự kiện End of Conversion, End of Injected Conversion, Analog Watchdog

Chế độ Single hoặc Continuous

Chế độ Scan tự động quét từ kênh 0 đến kênh n (STM32F103C8 có tối đa 10 kênh đầu vào ngoài)

Có cơ chế tự hiệu chuẩn (self-calibration)

Data Alignment (căn chỉnh data) căn trái hoặc căn phải

Cài đặt thời gian lấy mẫu riêng cho từng kênh

Có thể kích hoạt bằng tín hiệu bên ngoài

Chế độ Dual mode sử dụng cùng lúc 2 bộ ADC

Hỗ trợ DMA

Xung clock của ADC được lấy từ bus APB2 thông qua ADC prescaler và phải nhỏ hơn 14MHz

H2 6

Chi tiết tham khảo mục 11.2 trong Reference Manual.

ADC một kênh chế độ Single và Continuous

Với chế độ Single, bộ ADC chỉ chuyển đổi 1 lần rồi dừng, một ngắt được sinh ra nếu bit EOCIE được set lên 1.

Với chế độ Continuous, bộ ADC sẽ chuyển đổi liên tục, một ngắt được sinh ra sau mỗi lần chuyển đổi nếu bit EOCIE được set lên 1.

Yêu cầu DMA sẽ được sinh ra nếu bit DMA được set lên 1 (DMA chỉ có trên ADC1 và ADC3).

Kết quả chuyển đổi được lưu vào thanh ghi DR.

Bắt đầu chuyển đổi bằng cách set bit ADON lên 1.

Các bước để sử dụng ADC như sau:

  1. Enable clock cho ADC, ghi hệ số chia cho ADC prescaler
  2. Chọn các kênh cần chuyển đổi
  3. Chọn chế độ chuyển đổi Single, Continuous, Scan, Discontinuous
  4. Chọn thời gian lấy mẫu (Sampling Time)
  5. Chọn ngắt hoặc DMA
  6. Start bộ chuyển đổi
  7. Kiểm tra cờ EOC hoặc đọc dữ liệu từ thanh ghi DR trong ngắt

Cấu hình ADC một kênh trong STM32 CubeMX

Trong bài này chúng ta sẽ sử dụng ADC1 channel 0 (chân PA0) ở chế độ Single hoặc Continuous để đọc giá trị từ biến trở về. Biến trở nối như sơ đồ sau: chân giữa vào PA0, hai chân còn lại vào 3.3V và GND.

Sơ đồ đấu nối STM32 ADC một kênh: chân giữa biến trở 10kΩ nối PA0 (ADC1 kênh 0), hai chân còn lại nối 3.3V và GND của Blue Pill

Lưu ý: Chân ADC của STM32 chỉ chịu được tối đa 3.3V. Đừng nối biến trở lên chân 5V của board, điện áp vào PA0 sẽ vượt ngưỡng đo và có thể làm hỏng chân.

Đầu tiên các bạn mở phần mềm CubeMX, chọn chip STM32F103C8 và Start Project.

Trong SYS chọn Debug: Serial Wire, các bước này mình đã nói kĩ trong Bài 3 rồi nhé.

Tiếp tục trong ADC1 tick chọn IN0 (Channel 0).

H3 5

Trong Configuration chọn như hình.

Giải thích:

  • Data Alignment: Right alignment, căn data về bên phải. Vì thanh ghi dữ liệu là 16 bit còn kết quả chuyển đổi là 12 bit, tham số này xác định dữ liệu được căn lề bên trái hay bên phải
  • Scan Conversion Mode: Disable. Tắt chế độ quét (chỉ dùng khi chuyển đổi nhiều kênh)
  • Continuous Conversion Mode: Disable. Tắt chế độ làm việc liên tục, nghĩa là chạy chế độ Single
  • Rank: 1. Thứ tự chuyển đổi của kênh (sử dụng khi chuyển đổi nhiều kênh)
  • Sampling Time: 239.5 Cycles. Thời gian lấy mẫu tính theo chu kì clock ADC

Lưu ý: Sampling time càng lớn thì kết quả chuyển đổi càng ổn định, nhất là khi nguồn tín hiệu có trở kháng cao như biến trở, đổi lại tốc độ đọc sẽ chậm hơn.

H4 4

Trong NVIC tick vào bật ngắt ADC1.

H5 5

Đặt tên project, chọn Toolchain và Gen code.

H6 5

Lập trình ADC một kênh với STM32

Nhấn F7 để build project. Trong stm32f1xx_it.c tìm đến hàm xử lý ngắt ADC.

Go To Definition nó. Hẳn bạn đã rất quen thuộc với bước này rồi.

H7 6

Tìm tới hàm HAL_ADC_ConvCpltCallback(hadc), Go To Definition.

H8 5

Copy và paste vào phía trên hàm main(), nhớ bỏ từ khóa __weak.

H9 6

Sau đó chúng ta khởi tạo một biến u16_ADCVal lưu giá trị ADC. Trong hàm xử lý ngắt ta kiểm tra ngắt đến từ đâu, nếu là ADC1 thì đọc giá trị ADC vào biến vừa khởi tạo.

Khởi tạo thêm một biến Voltage lưu giá trị điện áp tại chân CH0 để so sánh với thực tế: Voltage = ADC_Value / 4095 * 3.3

Trong đó 4095 là giá trị cực đại của ADC 12 bit.

3.3 là điện áp tham chiếu Vref.

Lưu ý: Khi viết công thức này trong C, nếu để nguyên u16_ADCVal/4095*3.3 thì phép chia số nguyên sẽ cho ra 0 (trừ khi ADC đạt đúng 4095). Các bạn phải ép kiểu sang float trước, ví dụ Voltage = (float)u16_ADCVal * 3.3f / 4095;

H10 4

Sau đó khởi động ADC1 bằng lệnh HAL_ADC_Start_IT(&hadc1); trong while(1), delay khoảng 50ms mỗi lần đọc.

Vì chế độ Single chỉ đọc một lần nên ta phải liên tục gọi hàm Start. Chế độ này tiết kiệm năng lượng hơn chế độ Continuous, lập trình viên cần đọc lúc nào thì mới bật lúc đó.

H11 4

Nhấn F7 để build, sau đó nạp chương trình, nhấn vào nút Debug (Ctrl + F5), add 2 biến u16_ADCVal và Voltage vào Watch.

Nhấn F5 để chạy debug, ta thấy sai số so với đồng hồ đo trong khoảng ±0.04V.

Lưu ý: ADC của STM32F1 nên được hiệu chuẩn 1 lần sau khi khởi tạo bằng hàm HAL_ADCEx_Calibration_Start(&hadc1); (gọi trước lần Start đầu tiên), sai số sẽ nhỏ hơn đáng kể.

H12 3
H15

Với chế độ Continuous, các bạn tìm đến hàm khởi tạo ADC: MX_ADC1_Init()

Sửa dòng hadc1.Init.ContinuousConvMode = DISABLE; thành ENABLE.

H13 2

Chuyển hàm HAL_ADC_Start_IT(&hadc1); lên trên while(1), chỉ cần gọi 1 lần.

H14 2

Nhấn F7 build và nạp vào chip. Vào Debug và xem kết quả nhé.

Kết

Lập trình STM32 đọc ADC một kênh là một trong những bài bắt buộc phải nắm rõ trong lập trình STM32. Bởi vì các thiết bị analog đều cần sử dụng ADC thì vi điều khiển mới có thể hiểu được.

Hãy cố gắng làm chủ ADC một kênh nhé!

4.5/5 - (4 bình chọn)

12 những suy nghĩ trên “Bài 8: Lập trình STM32 đọc ADC một kênh”

  1. Nhân Trần nói:

    Ad hướng dẫn dùm mình 1 tí, mình cần chuyển đổi 1 số thực (float) sang số uint8_t thì dùng hàm gì nhỉ? mình mới nhập môn nên còn gà lắm!

  2. Nhân Trần nói:

    Cụ thể là mình đang làm bài tập đọc ADC, đọc được giá trị điện áp Voltage ( dạng số thực), muốn gửi lên Hercules Teminal để xem giá trị (qua Uart)
    HAL_UART_Transmit(&huart1,Voltage,sizeof(u8_TxBuff),100);
    Mong bạn hướng dẫn giúp ! Cảm ơn .

  3. Van Thanh nói:

    Giả sử mình có một nút nhấn, mà mình muốn lúc nút nhấn không bật thì mới dùng adc thì làm sao ạ ? Mong anh giải đáp, em mới học lập trình nên hơi non.

  4. Tú Huỳnh nói:

    em phải xem video hay đọc bên web khác trước rồi vài đọc lại bài anh viết em mới biết là anh đang viết cái gì ^^.

  5. Thuc nói:

    Chào Thầy Khuê Nguyễn,

    Mình xem bài viết của bạn chạy ADC mô phỏng trên proteus
    Thì giá trị đọc khi vặn biến trở lên mức cao nhất 5v=> thì giá trị hiển thị ra LCD ~6.59(~7v)
    Mình chỉ set up 1 điểm duy nhất khác với bạn Set up là mình để thời gian biến đổi mặc định theo CubeMX cài đặt 1.5 cycles ==> còn các cài đặt khác giống hệt của bạn

    Mình đang thắc mắc đúng ra kéo biến trở lên Max thì cũng chỉ hiển thị tối đa 5V

    Bạn chỉ giúp mình.

  6. Thuc nói:

    Mình tìm ra rồi nguyên nhân rồi,

    Nó ở chỗ tính toán phải chia cho 4096 nó mới ra Max 3.3v
    =>Công thức đúng: Vol = (float)(3.3*ADC_Val/4096);

    Mình chia cho 4095 lên kết quả ra sai số lớn

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *