DAY63 IMX6ULL ADC Driver Development

IMX6ULL ADC Driver Development

1. Sorting Out Core Concepts of ADC

1.1 What is ADC?

ADC (Analog-to-Digital Converter) is a core module that converts continuously varying analog voltage signals into discrete digital signals, serving as a bridge for digital systems (MCU/processor) to interact with analog signals from the physical world.

1.2 Core Terminology

Terminology Core Explanation
Analog Signal A continuously varying physical quantity (e.g., voltage, temperature) in the physical world, acting as the input source of ADC
Digital Signal A discrete, discontinuous signal that can be directly parsed by MCU (e.g., values from 0 to 4095)
Sensor A device that converts physical quantities (light, temperature, pressure, etc.) into analog electrical signals (voltage/current)

2. Hardware Analysis of IMX6ULL ADC

2.1 Schematic Diagram Analysis

The ADC module of IMX6ULL is integrated inside the SOC, with the following core hardware associations:

  • Core Board (IMX6ULL_CORE_V2.0): The ADC reference voltage pin ADC_VREFH provides a voltage reference for conversion;
  • Bottom Board (IMX6ULL_MINI_V2.2): GPIO_1 (pin 7) of the P4 module is associated with the ADC channel, acting as the hardware carrier for actual signal acquisition.

2.2 Key Information from Reference Manual

(1) ADC External Signal Mapping

ADC1 of IMX6ULL includes 10 input channels, with the core channel-pin mapping as follows (only key ones listed):

Signal Description Corresponding Pin Direction
ADC_VREFH ADC high reference voltage ADC_VREFH Input
ADC1_IN0 ADC1 channel 0 input GPIO1_IO00 Input
ADC1_IN1 ADC1 channel 1 input GPIO1_IO01 Input
... ... ... ...
ADC1_IN7 ADC1 channel 7 input GPIO1_IO07 Input
(2) Clock Configuration

The ADC input clock (ADICLK) supports 3 options, which determine the conversion rate:

ADICLK Value Clock Source
00 IPG clock
01 IPG clock divided by 2
11 Asynchronous clock (ADACK)
(3) Core Registers (Key for Development)
Register Core Bit Fields Function Description
ADCx_HC0 ADCH (b0-b4) Selects the acquisition channel; switching the channel triggers one conversion
AIEN (b7) Conversion complete interrupt enable (disabled by default)
ADCx_HS COCO0 (b0) Conversion complete flag (1 = completed, 0 = not completed)
ADCx_R0 CDATA (b0-b11) Stores the conversion result (lower 12 bits valid, range 0~4095)
ADCx_GC CAL (b7) Starts automatic calibration (write 1 to start, read 0 when completed)
ADCx_GS CALF (b1) Calibration failure flag (0 = success, 1 = failure; needs to be cleared by writing 1)

3. Practical ADC Driver Development

3.1 ADC Calibration (Mandatory Step)

The ADC of IMX6ULL must undergo automatic calibration before use to eliminate hardware offset errors. The core code is as follows:

c 复制代码
#include "imx6ull.h"

/**
 * @brief ADC automatic calibration
 * @param adc_base: ADC base address (e.g., ADC1_BASE)
 * @return 0: Calibration successful, -1: Calibration failed
 */
int adc_calibrate(ADC_Type *adc_base)
{
    // 1. Start calibration: Write 1 to the CAL bit
    adc_base->GC |= (1 << 7);
    
    // 2. Wait for calibration completion: CAL bit reads 0 when completed
    while((adc_base->GC & (1 << 7)) != 0);
    
    // 3. Check calibration failure flag
    if(adc_base->GS & (1 << 1))
    {
        adc_base->GS |= (1 << 1); // Clear the failure flag
        return -1;
    }
    return 0;
}

Principle Explanation: Calibration is a core step for ADC to ensure conversion accuracy, and the hardware automatically completes offset compensation. It is necessary to wait for the CAL bit to return to 0. If CALF is set to 1, it indicates calibration failure, and the flag needs to be cleared and calibration re-performed.

3.2 ADC Single Sampling

After calibration, perform single sampling on the specified channel. The core code is as follows:

c 复制代码
/**
 * @brief ADC single sampling (12-bit resolution)
 * @param adc_base: ADC base address
 * @param channel: Sampling channel (0-9)
 * @return Sampled value (0~4095, corresponding to 0~ADC_VREFH voltage)
 */
unsigned short adc_single_sample(ADC_Type *adc_base, unsigned char channel)
{
    unsigned short adc_value = 0;
    
    // 1. Configure the sampling channel: Disable interrupt, set target channel
    adc_base->HC0 = (0 << 7) | (channel & 0x1F);
    
    // 2. Wait for conversion completion: Poll the COCO0 bit
    while((adc_base->HS & 0x01) == 0);
    
    // 3. Read the conversion result: Retain only valid lower 12 bits of data
    adc_value = adc_base->R0 & 0xFFF;
    
    return adc_value;
}

Principle Explanation : Writing the channel number to ADCx_HC0 triggers one conversion automatically by the ADC. Poll the COCO0 bit until it is set to 1, indicating conversion completion. The lower 12 bits of ADCx_R0 are the final digital quantity, corresponding to the proportional value of the analog voltage.

3.3 Mean Filter Optimization (Dedicated to Photosensitive Sensors)

The signal collected by the photosensitive sensor is susceptible to environmental interference, and mean filtering is used to reduce noise. The core code is as follows:

c 复制代码
#define SAMPLE_COUNT 10  // Number of sampling times, adjustable as needed

/**
 * @brief ADC mean filter sampling (improves photosensitive signal stability)
 * @param adc_base: ADC base address
 * @param channel: Sampling channel
 * @return Filtered sampled value
 */
unsigned short adc_average_sample(ADC_Type *adc_base, unsigned char channel)
{
    unsigned int sum = 0;
    unsigned char i = 0;
    
    // 1. Accumulate multiple sampling values
    for(i = 0; i < SAMPLE_COUNT; i++)
    {
        sum += adc_single_sample(adc_base, channel);
        // Short delay to avoid excessively high sampling frequency
        for(volatile int j=0; j<1000; j++);
    }
    
    // 2. Calculate the average value to filter out random noise
    return (unsigned short)(sum / SAMPLE_COUNT);
}

Principle Explanation: Collect N samples (10 in the example) continuously from the same channel, accumulate them and take the average value, which can effectively offset random interference in a single sampling and improve the stability of the photosensitive signal.

4. Extended Practice: I2C Temperature Reading and Code Optimization

4.1 LM75 Temperature Sensor Reading (I2C Method)

LM75 is a temperature sensor with an I2C interface. The core code for reading temperature is as follows:

c 复制代码
unsigned char rcv_buffer[2] = {0}; // I2C receive buffer

/**
 * @brief Read LM75 temperature value (0.5℃ precision)
 * @return Temperature value (unit: ℃)
 */
float get_temp_value(void)
{
    unsigned short t = 0;
    struct I2C_Msg msg = {
        .dev_addr = 0x48,  // LM75 device address
        .reg_addr = 0x00,  // Temperature register address
        .reg_len = 1,      // Register address length (1 byte)
        .data = rcv_buffer,// Receive data buffer
        .len = 2,          // Number of bytes to read (temperature register is 16-bit)
        .dir = I2C_read    // I2C read direction
    };
    
    // Execute I2C data transfer
    i2c_transfer(I2C1, &msg);
    
    // Splice data and convert to actual temperature
    t |= (rcv_buffer[0] << 8) | (rcv_buffer[1] << 0);
    t = t >> 7;  // Retain valid temperature bits (upper 9 bits)
    return t * 0.5;  // 0.5℃ precision conversion
}

Principle Explanation: The temperature register of LM75 is 16-bit, with the upper 9 bits as valid temperature bits (including sign). After shifting right by 7 bits and multiplying by 0.5, the digital quantity can be converted into the actual temperature value (e.g., a value of 10 corresponds to 5℃).

4.2 I2C Multi-Byte Register Address Compatibility Optimization

The register address of an I2C device may be multi-byte (e.g., 16-bit/32-bit), so it is necessary to be compatible with the transmission of addresses of different lengths. The optimized code is as follows:

c 复制代码
/**
 * @brief Send multi-byte register address (compatible with different I2C devices)
 * @param base: I2C base address
 * @param reg_addr: Register address
 * @param reg_len: Address length (number of bytes)
 * @return 0: Success, non-0: Failure
 */
int i2c_send_reg_addr(I2C_Type *base, unsigned int reg_addr, unsigned int reg_len)
{
    int status = 0;
    int i = reg_len - 1;
    
    // Send address byte by byte from high to low
    for (; i >= 0; i--)
    {
        base->I2DR = (reg_addr >> (8 * i)) & 0XFF; // Extract current byte
        status = i2c_wait_iif(base); // Wait for transmission completion
        if (status != 0) goto stop;  // Jump to stop process if failed
    }
    return 0;
    
stop:
    base->I2CR &= ~(1 << 5); // Stop I2C transmission
    return status;
}

Principle Explanation: According to the register address length (reg_len), extract the address from high to low byte by byte through shift and AND operations, and write them into the I2C data register (I2DR) one by one to ensure compatibility with register addresses of different byte lengths.

4.3 FPU Enable (Improve Floating-Point Operation Efficiency)

Temperature calculation involves floating-point operations (e.g., *0.5), so it is necessary to enable the FPU (Floating-Point Unit) of IMX6ULL. The core assembly code is as follows:

armasm 复制代码
enable_fpu:
    // 1. Read CPACR register, enable CP10/CP11 (FPU access permission)
    mrc     p15, 0, r0, c1, c0, 2   
    orr     r0, r0, #(0xF << 20)    
    mcr     p15, 0, r0, c1, c0, 2   
    
    // 2. Set the EN bit of FPEXC to enable FPU
    mov     r0, #0x40000000         
    vmsr    fpexc, r0               
    
    // 3. Clear FPSCR flags to initialize floating-point status
    mov     r0, #0x00000000         
    vmsr    fpscr, r0               
    
    bx      lr                      // Function return

Principle Explanation: The CP10/CP11 bits of the CPACR register control FPU access permission; setting them to 1 allows floating-point operations. The EN bit of FPEXC is the main FPU enable bit, and enabling it improves floating-point operation efficiency by several times.

相关推荐
扑火的小飞蛾2 小时前
RHEL 7 安装 Docker 过程总结
运维·docker·容器
帅得不敢出门2 小时前
Android Framework在mk中新增类似PRODUCT_MODEL的变量并传递给buildinfo.sh及prop属性中
android·linux·前端
程序员_大白2 小时前
区块链部署与运维,零基础入门到精通,收藏这篇就够了
运维·c语言·开发语言·区块链
德迅云安全—珍珍2 小时前
2核2G的云服务器可以架设游戏吗?
运维·服务器·游戏
Fᴏʀ ʏ꯭ᴏ꯭ᴜ꯭.3 小时前
HAProxy状态页:运维监控实战指南
运维
陈聪.3 小时前
HRCE实验
运维
阿拉伯柠檬3 小时前
网络层协议IP(三)
linux·网络·网络协议·tcp/ip·面试
Miracle&4 小时前
在Linux VirtualBox中安装系统失败
linux·运维·服务器
ShoreKiten4 小时前
ctfshow-web316
运维·服务器·前端