RP2040 C SDK ADC功能使用
RP2040 C SDK ADC功能使用
- 🌿RP2040 ADC功能说明文档:
https://www.raspberrypi.com/documentation/pico-sdk/hardware.html#hardware_adc
📗RP2040 ADC介绍
- SAR ADC
- 500 kS/s (Using an independent 48MHz clock)
- 12 bit (RP2040 8.7 ENOB, RP2350 9.2 ENOB)
- RP2040 5 input mux:
- 4 inputs that are available on package pins shared with GPIO[29:26]
- 1 input is dedicated to the internal temperature sensor
- 4 element receive sample FIFO。
- One input dedicated to the internal temperature sensor (see Section 12.4.6)
- Interrupt generation
- DMA interface
- 🍁相关电路:
📑RP2040 ADC API相关函数介绍
- 🌿
static inline void adc_select_input(uint input)
:配置输入通道:0 - 3分别对应GPIO26 - GPIO29。 - 🌿
static inline uint16_t adc_read(void)
:读取对应通道ADC转换结果。 - 🌿
static inline void adc_set_temp_sensor_enabled(bool enable)
:内部温度传感器使能位 - 🌿
static inline void adc_gpio_init(uint gpio)
:配置gpio模式作为ADC模拟输入模式。 - 🌿
static inline void adc_set_round_robin(uint input_mask)
:ADC通道选择位:0 - 4bit,值:0 - 1f,分别对应通道0-3,4:内部温度
如果需要配置adc下一个转换通道为3(GPIO29),那么
adc_set_round_robin(0x08)
;等同于adc_select_input(3)
效果。
📜ADC通道和输入引脚
- 用户ADC输入在0-3(GPIO 26-29)上,共用一个ADC模数转换器,在多通道读取轮流读取。内部温度传感器在输入4通道上。
- 在
CMakeLists.txt
配置文件中需要引入adc外设:
# Add the standard library to the build
target_link_libraries(RP2040_ADCpico_stdlibhardware_adc)
📘内部温度读取
/*CMSIS-DAP烧录命令:openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000"-c "program RP2040_ADC.elf verify reset exit"jlink命令: openocd -f interface/jlink.cfg -f target/rp2040.cfg -c "adapter speed 2000" -c "program RP2040_ADC.elf verify reset exit"*/
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/divider.h"
#include "hardware/adc.h"
#include "hardware/clocks.h"#define BUILTIN_LED PICO_DEFAULT_LED_PIN // LED is on the same pin as the default LED 25/* Choose 'C' for Celsius or 'F' for Fahrenheit. */
#define TEMPERATURE_UNITS 'C'static void measure_freqs(void);
/* References for this implementation:* raspberry-pi-pico-c-sdk.pdf, Section '4.1.1. hardware_adc'* pico-examples/adc/adc_console/adc_console.c */
float read_onboard_temperature(const char unit)
{/* 12-bit conversion, assume max value == ADC_VREF == 3.3 V */const float conversionFactor = 3.3f / (1 << 12);float adc = (float)adc_read() * conversionFactor;float tempC = 27.0f - (adc - 0.706f) / 0.001721f;if (unit == 'C'){return tempC;}else if (unit == 'F'){return tempC * 9 / 5 + 32;}return -1.0f;
}int main()
{stdio_init_all();sleep_ms(2500);printf("adc test!\n");set_sys_clock_khz(133000, true); // 325us// GPIO initialisation.gpio_init(BUILTIN_LED);gpio_set_dir(BUILTIN_LED, 1);gpio_pull_up(BUILTIN_LED);adc_init();// 使能温度传感器adc_set_temp_sensor_enabled(true);adc_select_input(4);while (true){// Read the temperature from the onboard temperature sensor.float temperature = read_onboard_temperature(TEMPERATURE_UNITS);printf("Onboard temperature = %.02f %c\n", temperature, TEMPERATURE_UNITS);sleep_ms(1000);gpio_xor_mask(1ul << BUILTIN_LED); // Toggle the LED// tight_loop_contents();measure_freqs();}return 0;
}static void measure_freqs(void) {uint f_pll_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_SYS_CLKSRC_PRIMARY);uint f_pll_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_USB_CLKSRC_PRIMARY);uint f_rosc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC);uint f_clk_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_SYS);uint f_clk_peri = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_PERI);uint f_clk_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_USB);uint f_clk_adc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_ADC);uint f_clk_rtc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_RTC);printf("pll_sys = %dkHz\n", f_pll_sys);printf("pll_usb = %dkHz\n", f_pll_usb);printf("rosc = %dkHz\n", f_rosc);printf("clk_sys = %dkHz\n", f_clk_sys);printf("clk_peri = %dkHz\n", f_clk_peri);printf("clk_usb = %dkHz\n", f_clk_usb);printf("clk_adc = %dkHz\n", f_clk_adc);printf("clk_rtc = %dkHz\n", f_clk_rtc);// Can't measure clk_ref / xosc as it is the ref
}
📗ADC单通道读取
/*CMSIS-DAP烧录命令:openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000"-c "program RP2040_ADC.elf verify reset exit"jlink命令: openocd -f interface/jlink.cfg -f target/rp2040.cfg -c "adapter speed 2000" -c "program RP2040_ADC.elf verify reset exit"*/
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/divider.h"
#include "hardware/adc.h"
#include "hardware/clocks.h"#define BUILTIN_LED PICO_DEFAULT_LED_PIN // LED is on the same pin as the default LED 25static void measure_freqs(void);void ADC_Reading(void)
{// 12-bit conversion, assume max value == ADC_VREF == 3.3 Vconst float conversion_factor = 3.3f / (1 << 12);uint16_t result = adc_read();printf("Raw value: 0x%03x, voltage: %f V\n", result, 3 * result * conversion_factor);
}int main()
{stdio_init_all();sleep_ms(2500);printf("adc test!\n");set_sys_clock_khz(133000, true); // 325us// GPIO initialisation.gpio_init(BUILTIN_LED);gpio_set_dir(BUILTIN_LED, 1);gpio_pull_up(BUILTIN_LED);adc_init();// Make sure GPIO is high-impedance, no pullups etcadc_gpio_init(29);// Select ADC input 3 (GPIO29)adc_select_input(3);while (true){
、、sleep_ms(1000);gpio_xor_mask(1ul << BUILTIN_LED); // Toggle the LED// tight_loop_contents();measure_freqs();ADC_Reading();}return 0;
}static void measure_freqs(void) {uint f_pll_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_SYS_CLKSRC_PRIMARY);uint f_pll_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_USB_CLKSRC_PRIMARY);uint f_rosc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC);uint f_clk_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_SYS);uint f_clk_peri = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_PERI);uint f_clk_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_USB);uint f_clk_adc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_ADC);uint f_clk_rtc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_RTC);printf("pll_sys = %dkHz\n", f_pll_sys);printf("pll_usb = %dkHz\n", f_pll_usb);printf("rosc = %dkHz\n", f_rosc);printf("clk_sys = %dkHz\n", f_clk_sys);printf("clk_peri = %dkHz\n", f_clk_peri);printf("clk_usb = %dkHz\n", f_clk_usb);printf("clk_adc = %dkHz\n", f_clk_adc);printf("clk_rtc = %dkHz\n", f_clk_rtc);
}
📒多通道读取方式
由于ADC共用一个ADC模数转换器,在多通道读取时,,需要采用轮流配置通道读取方式。
void ADC_Reading(void)
{// 12-bit conversion, assume max value == ADC_VREF == 3.3 Vconst float conversion_factor = 3.3f / (1 << 12);adc_select_input(2);//设置通道2uint16_t adc_2_raw = adc_read();//读取转换通道转换结果adc_select_input(3);//设置通道3uint16_t adc_3_raw = adc_read();//读取转换通道转换结果printf("Raw1 value: 0x%03x, voltage: %f V\n", adc_2_raw, 3 * adc_2_raw * conversion_factor);printf("Raw2 value: 0x%03x, voltage: %f V\n", adc_3_raw, 3 * adc_3_raw * conversion_factor);
}
📓ADC 通过DMA方式读取
- 🐛 目前程序有个bug问题:不管设置的dma传输字节大小为
DMA_SIZE_8
还是DMA_SIZE_16
,DMA采样的ADC数据结果都一样。
/*CMSIS-DAP烧录命令:openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000"-c "program RP2040_ADC_DMA.elf verify reset exit"jlink命令: openocd -f interface/jlink.cfg -f target/rp2040.cfg -c "adapter speed 2000" -c "program RP2040_ADC_DMA.elf verify reset exit"*/
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/divider.h"
#include "hardware/adc.h"
#include "hardware/dma.h"
#include "hardware/clocks.h"
//#include "hardware/irq.h"
#define BUILTIN_LED PICO_DEFAULT_LED_PIN // LED is on the same pin as the default LED 25// Channel 0 is GPIO26
#define CAPTURE_CHANNEL 0
#define SAMPLES 10
// Ideally the signal should be bandwidth limited to sample_frequency/2
#define SAMPLING_FREQUENCY 14000 // Sampling frequency in Hzuint16_t sampleBuffer[SAMPLES];
uint16_t streamBuffer[SAMPLES]; // Scaled ADC sample working bufferdma_channel_config cfg;
int dma_chan;// const char src[] = "Hello, world! (from DMA)";
// char dst[count_of(src)];static void measure_freqs(void);void ADC_Reading(void)
{// 12-bit conversion, assume max value == ADC_VREF == 3.3 Vconst float conversion_factor = 3.3f / (1 << 12);adc_select_input(0);//设置通道2uint16_t adc_0_raw = adc_read();//读取转换通道转换结果printf("Raw1 value:%d, voltage: %f V\n", adc_0_raw,adc_0_raw * conversion_factor);}int main()
{stdio_init_all();sleep_ms(2500);printf("adc DMA test!\n");// set_sys_clock_khz(133000, true); // 325us// GPIO initialisation.gpio_init(BUILTIN_LED);gpio_set_dir(BUILTIN_LED, 1);gpio_pull_up(BUILTIN_LED);adc_init();// Init GPIO for analogue use: hi-Z, no pulls, disable digital input buffer.adc_gpio_init(26 + CAPTURE_CHANNEL);//ADC_Reading();//1983//adc_set_round_robin(ADCopen==1 ? 1 : ADCopen==2 ? 3 : ADCopen==3 ? 7 : 15);adc_select_input(CAPTURE_CHANNEL);adc_fifo_setup(true, // Write each completed conversion to the sample FIFOtrue, // Enable DMA data request (DREQ)1, // DREQ (and IRQ) asserted when at least 1 sample presentfalse, // We won't see the ERR bit because of 8 bit reads; disable.true // Shift each sample to 8 bits when pushing to FIFO);// Divisor of 0 -> full speed. Free-running capture with the divider is// equivalent to pressing the ADC_CS_START_ONCE button once per `div + 1`// cycles (div not necessarily an integer). Each conversion takes 96// cycles, so// in general you want a divider of 0 (hold down the button// continuously) or > 95 (take samples less frequently than 96 cycle// intervals). This is all timed by the 48 MHz ADC clock.//adc_set_clkdiv((48000000/SAMPLING_FREQUENCY) - 1);adc_set_clkdiv(0);// Set up the DMA to start transferring data as soon as it appears in FIFO设置DMA,一旦数据出现在FIFO中就开始传输数据dma_chan = dma_claim_unused_channel(true);cfg = dma_channel_get_default_config(dma_chan);//获取给定通道的默认通道配置// Reading from constant address, writing to incrementing byte addresseschannel_config_set_transfer_data_size(&cfg, DMA_SIZE_16);channel_config_set_read_increment(&cfg, false);//因为就一个地址永远地写到同一个位置,目的是循环触发同一个DMA.channel_config_set_write_increment(&cfg, true);//FIFO的地址自增
//新增channel_config_set_irq_quiet(&cfg, true);//在QUIET模式下,通道不会在每个传输块结束时产生irq。channel_config_set_dreq(&cfg, DREQ_ADC); // pace data according to ADCchannel_config_set_chain_to(&cfg, dma_chan);//外设作为传输源,即ADC->DMAchannel_config_set_enable(&cfg, true);//设置DMA通道的配置,包括源地址、目的地址、传输字节数、传输方向、中断等。
//这里设置了源地址为ADC的FIFO,目的地址为streamBuffer,传输字节数为SAMPLES,传输方向为从ADC到streamBuffer,中断为DREQ_ADC。// Pace transfers based on availability of ADC samples// channel_config_set_dreq(&cfg, DREQ_ADC);dma_channel_configure(dma_chan, &cfg,(uint16_t*)sampleBuffer, // dst 数据存储到目标缓冲区&adc_hw->fifo, // srcSAMPLES, // transfer count 传输数量,即采样点数true // start immediately);
// Everything is ready to go. Tell the control channel to load the first// control block. Everything is automatic from here.dma_start_channel_mask(1u << dma_chan);// 开始传输printf("Starting capture\n");adc_run(true);// Start capturewhile (true){// Read the temperature from the onboard temperature sensor.// float temperature = read_onboard_temperature(TEMPERATURE_UNITS);// printf("Onboard temperature = %.02f %c\n", temperature, TEMPERATURE_UNITS);sleep_ms(1000);gpio_xor_mask(1ul << BUILTIN_LED); // Toggle the LED// tight_loop_contents();measure_freqs();//ADC_Reading();// Wait for DMA to finish (may have already)dma_channel_wait_for_finish_blocking(dma_chan);// Stop and clean out FIFOadc_run(false);adc_fifo_drain();// Copy samples into buffer for approxFFT SAMPLESfor (int i = 0; i < SAMPLES; i++) {streamBuffer[i] = sampleBuffer[i];// streamBuffer[i] = (uint16_t)((sampleBuffer[i]&0x0f)<<8)+(uint16_t)sampleBuffer[i+1] ;printf("%d ",streamBuffer[i]);if (i % 10 == 9)printf("\n");// sleep_ms(100);}sleep_ms(1000);dma_channel_set_trans_count(DREQ_ADC, 10, true);//设置DMA传输的字节数,这里是10个字节,即10个采样点。// Now we have a copy of the samples we can start capture again// dma_channel_configure(dma_chan, &cfg,// (uint16_t*)sampleBuffer, // dst// &adc_hw->fifo, // src// SAMPLES, // transfer count// true // start immediately// );// Restart the ADC captureadc_run(true);}return 0;
}static void measure_freqs(void) {uint f_pll_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_SYS_CLKSRC_PRIMARY);uint f_pll_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_USB_CLKSRC_PRIMARY);uint f_rosc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC);uint f_clk_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_SYS);uint f_clk_peri = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_PERI);uint f_clk_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_USB);uint f_clk_adc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_ADC);uint f_clk_rtc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_RTC);printf("pll_sys = %dkHz\n", f_pll_sys);printf("pll_usb = %dkHz\n", f_pll_usb);printf("rosc = %dkHz\n", f_rosc);printf("clk_sys = %dkHz\n", f_clk_sys);printf("clk_peri = %dkHz\n", f_clk_peri);printf("clk_usb = %dkHz\n", f_clk_usb);printf("clk_adc = %dkHz\n", f_clk_adc);printf("clk_rtc = %dkHz\n", f_clk_rtc);// Can't measure clk_ref / xosc as it is the ref
}