• RT1176 LPADC


    步骤一 在MCU CONFIG TOOLS配置对应引脚

    在这里插入图片描述

    步骤二

    从上层往下,依次将
    a. RTT层: rt-thread/components/drivers/misc/adc.c
    b. 中间层:libraries/drv_lpadc.c
    c. 硬件层:libraries/MIMXRT1176/drivers/fsl_lpadc.c
    添加构建,

    其中中间层的 drv_lpadc.c编译时报错,参照SDK做了些修改(注释掉的是修改前的代码)

    static rt_err_t imxrt_hp_adc_convert(struct rt_adc_device *device, rt_uint32_t channel, rt_uint32_t *value)
    {
        //LPADC1 *base; //Modified by Hao 22/08/01
        ADC_Type *base;
        lpadc_conv_command_config_t mLpadcCommandConfigStruct;
        lpadc_conv_trigger_config_t mLpadcTriggerConfigStruct;
        lpadc_conv_result_t mLpadcResultConfigStruct;
        //base = (LPADC1_BASE *)(device->parent.user_data); //Modified by Hao 22/08/01
        base = (ADC_Type *)(device->parent.user_data);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    步骤三 编写自己的应用 之ADC设置

    RTT官方链接
    RTT对ADC的操作流程如下,但一次只能读取单个通道的数据,效率不高,所以根据应用编写自己的代码

    #include 
    #include 
    
    #define ADC_DEV_NAME        "adc1"      /* ADC 设备名称 */
    #define ADC_DEV_CHANNEL     5           /* ADC 通道 */
    #define REFER_VOLTAGE       330         /* 参考电压 3.3V,数据精度乘以100保留2位小数*/
    #define CONVERT_BITS        (1 << 12)   /* 转换位数为12位 */
    
    static int adc_vol_sample(int argc, char *argv[])
    {
        rt_adc_device_t adc_dev;
        rt_uint32_t value, vol;
        rt_err_t ret = RT_EOK;
    
        /* 查找设备 */
        adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME);
        if (adc_dev == RT_NULL)
        {
            rt_kprintf("adc sample run failed! can't find %s device!\n", ADC_DEV_NAME);
            return RT_ERROR;
        }
    
        /* 使能设备 */
        ret = rt_adc_enable(adc_dev, ADC_DEV_CHANNEL);
    
        /* 读取采样值 */
        value = rt_adc_read(adc_dev, ADC_DEV_CHANNEL);
        rt_kprintf("the value is :%d \n", value);
    
        /* 转换为对应电压值 */
        vol = value * REFER_VOLTAGE / CONVERT_BITS;
        rt_kprintf("the voltage is :%d.%02d \n", vol / 100, vol % 100);
    
        /* 关闭通道 */
        ret = rt_adc_disable(adc_dev, ADC_DEV_CHANNEL);
    
        return ret;
    }
    /* 导出到 msh 命令列表中 */
    MSH_CMD_EXPORT(adc_vol_sample, adc voltage convert sample);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    我的需求是,一次性读取11个电位器的数据,
    RT176的ADC分成两部分,ADC1和ADC2,

    在这里插入图片描述
    特别的是ADC1 的 channel1,还分成了 sideA 和 sideB

    所以正常调用 RTT的接口,无法读取sideB的数据。

    那么如何操作,能读取到sideB的数据?
    lpadc_conv_command_config_t mLpadcCommandConfigStruct1;
    mLpadcCommandConfigStruct1.sampleChannelMode = kLPADC_SampleChannelSingleEndSideB;
    
    • 1
    • 2
    接下来问题又来了,怎么连续读取几次数据呢?
    mLpadcCommandConfigStruct1.enableAutoChannelIncrement = true;
    mLpadcCommandConfigStruct1.loopCount = 4; 
    //假如读取的第一个通道是 channel0,那么loopCount=4,就会读取channel0-4,一共5个通道的数据
    
    • 1
    • 2
    • 3
    那么怎么一次触发就读完 sideA 和 sideB的数据?
    mLpadcCommandConfigStruct1.chainedNextCommandNumber = 2;
    //比如读取sideA五个通道的指令为1号,读取sideB四个通道的指令是2号,这句话的意思是,执行完1号指令就会执行2号指令
    
    • 1
    • 2

    步骤四 编写自己的应用之 读取数据

    以下是NXP RT1176的代码,可以看出每次只会返回一个32位的数据,
    但我们有11个通道怎么办?

    bool LPADC_GetConvResult(ADC_Type *base, lpadc_conv_result_t *result)
    {
        assert(result != NULL); /* Check if the input pointer is available. */
    
        uint32_t tmp32;
    
        tmp32 = base->RESFIFO;
    
        if (0U == (ADC_RESFIFO_VALID_MASK & tmp32))
        {
            return false; /* FIFO is empty. Discard any read from RESFIFO. */
        }
    
        result->commandIdSource = (tmp32 & ADC_RESFIFO_CMDSRC_MASK) >> ADC_RESFIFO_CMDSRC_SHIFT;
        result->loopCountIndex = (tmp32 & ADC_RESFIFO_LOOPCNT_MASK) >> ADC_RESFIFO_LOOPCNT_SHIFT;
        result->triggerIdSource = (tmp32 & ADC_RESFIFO_TSRC_MASK) >> ADC_RESFIFO_TSRC_SHIFT;
        result->convValue = (uint16_t)(tmp32 & ADC_RESFIFO_D_MASK);
    
        return true;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    NXP都帮我们想好了,每次都读回来的数据,
    都会带着 commandIdSource 和 loopCountIndex
    也就是前面说到的几号指令,和循环读取的次数。
    这样我们就能知道是sideA或B,第几个通道的数据了

    uint32_t = g_LpadcmyResultShift = 3
    static rt_err_t my_adc_convert()
    {
        lpadc_conv_result_t mLpadcResultConfigStruct;
        while(1)
        {
           LPADC_DoSoftwareTrigger(base, 1U);
           for(uint8_t i=0;i<9;i++)
           {
               while (!LPADC_GetConvResult(base, &mLpadcResultConfigStruct))
               {
    				//等待读取结束
               }
               ReadVal[i]  = (mLpadcResultConfigStruct.convValue) >> g_LpadcmyResultShift;
               rt_kprintf("Channel%d = %d\n",i,ReadVal[i] );
           }
           rt_thread_mdelay(2000);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    碰到的坑

    1. 关于VREFH
    读取AD值时发现,电位器的中间值不是 2048(12bit的最大值,4096的一半),而是3500左右
    后来发现2V之后,读取的AD值就已经满4096了,原因在引脚 ADC_VREFH

    在这里插入图片描述
    如果输入电压是 1.8V,那么量程就是 0 - 1.8V。
    但是电位器接的是 3V3,是不是就没办法了呢??

    RT1176还提供了缩放量程的功能,在ADC设置时打开就可以了

    mLpadcCommandConfigStruct1.sampleScaleMode = kLPADC_SamplePartScale;
    //    kLPADC_SamplePartScale = 0U, /*!< Use divided input voltage signal. (Factor of 30/64). */
    
    • 1
    • 2

    比如现在接入的是 1.8V, 选择这个模式之后,上限变成 1.8 * 64 / 30 = 3.84V,完全可以覆盖3.3V。
    所以1.6V读取到的AD值约为 1650 左右,这样问题就解决了

    1. 10月24日更新
      连续采样的command需要是同一个ADC下的,比如同样是ADC1的指令,否则数据采集不到
      所以采集两个ADC的数据,可以按照下面这样写
    lpadc_conv_result_t mLpadcResultConfigStruct;
    
    	LPADC_DoSoftwareTrigger(LPADC2, 1U);
    	for(uint8_t i=0;i<4;i++)
    	 {
    		 while (!LPADC_GetConvResult(LPADC2, &mLpadcResultConfigStruct))
    		 {
    				
    		 }
    		 ReadVal[i]  = (mLpadcResultConfigStruct.convValue) >> g_LpadcResultShift;
    	 }
    	 LPADC_DoSoftwareTrigger(LPADC1, 2U);
    	 while (!LPADC_GetConvResult(LPADC1, &mLpadcResultConfigStruct))
    	{
    			
    	 }
    	 ReadVal[4]  = (mLpadcResultConfigStruct.convValue) >> g_LpadcResultShift;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  • 相关阅读:
    企业微信机器人对接GPT
    「Python实用秘技07」在pandas中实现自然顺序排序
    别用==比较包装类
    【数据结构】欧拉回路(优化最短路-图论)
    蚁群优化算法解决TSP问题(Matlab代码实现)
    flutter 命令
    PIE-engine 教程 ——map()映射函数和for循环函数的综合应用NDVI和NDWI计算北京市各区面积
    【每日一题】657. 机器人能否返回原点
    Acwing 829. 模拟队列
    性能测试 Linux 环境下模拟延时和丢包实现
  • 原文地址:https://blog.csdn.net/Hao_Zhen/article/details/126746441