查看: 2176|回复: 2

[原创] 基于lpc55s69的ntc温度传感器和adc驱动开发

[复制链接]
  • TA的每日心情
    开心
    2023-2-20 03:47
  • 签到天数: 2 天

    [LV.1]初来乍到

    10

    主题

    30

    帖子

    0

    中级会员

    Rank: 3Rank: 3

    积分
    297
    最后登录
    2024-1-25
    发表于 2023-2-20 03:08:35 | 显示全部楼层 |阅读模式
    本帖最后由 supenghou 于 2023-2-20 13:04 编辑

    讲解驱动之前,先说说系统框架
    面向对象实际上有 容器,类,对象,句柄的概念,面向对象实际上是面向具体事件,从事件来说把共同属性提炼成类,而多个对象最终用容器来承接,而调用对象用指针就是句柄来找寻。但是从系统性质来说,容器存放了多种类,而对象是类的实现,如果对象有更多的属性以后其他对象能用到,就对类进行继承,然后再用对象实现。
    这和跟目录(容器),分类文件夹(类),同属性的文件(对象),快捷方式(句柄)。
    找寻也是根据路径,实际上这里叫链表,进行挂链,实际上为了链表得对象地址(句柄)就有一个函数,得链表地址转换为对象句柄,(从linux继承的)。(如果有错误欢迎讨论共同完善这句话)。
    程序:
    1. /**
    2. * rt_container_of - return the member address of ptr, if the type of ptr is the
    3. * struct type.        ---------rt_container_of - 如果 ptr 的类型是结构类型,则返回 ptr 的成员地址。
    4. *        函数作用是以结构体类型type形式返回链表所在结构体的首地址
    5. */
    6. #define rt_container_of(ptr, type, member) \
    7.     ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))
    8. //第一个地址为链表的地址,然后用空函数计算方法计算链表在所在的结构体下的偏移量,
    复制代码
    linux 现在的应该是这个样子
    1. #define container_of(ptr, type, member) ({ \
    2. const typeof( ((type *)0)->member ) *__mptr = (ptr); \
    3. (type *)( (char *)__mptr - offsetof(type,member) );})
    复制代码
    Adc从文件来说也是分四大类:
    1、 官方驱动或者说支持包这里是fsl_lpadc.c
    2、 RTT系统的支持包adc.c
    以上两个文件基本不应该动
    3、 针对官方驱动和RTT系统支持包给定结构体进行整合,drv_adc.c
    三大类,第一,对驱动寄存器的调配,比如配置adc模块,和io引脚映射(可以不在这里,整合进IO控制模块中);第二对驱动操作,利用寄存器和驱动文件对系统规定的操作项目进行填充,第三,实现支持包adc.h给定的结构体(类)然后把操作函数映射到结构体的函数指针中完成承上启下
    result = rt_hw_adc_register(&adc0_device,"adc0", &lpc_adc_ops, ADC0);
    4、 具体应用的初级调用。
    通过驱动名字比如这里的adc0,find到驱动的句柄,句柄指向drv_adc.c设计的结构体

           这里实际上是操作系统隔绝,用户无需考虑底层应用,只要找到驱动句柄,就可以用系统api进行操作,比如这里三个个操作
    1. rt_adc_enable(adc_dev, ADC_DEV_CHANNEL);                                //使能通道
    2. value = rt_adc_read(adc_dev, ADC_DEV_CHANNEL);                //读取这个通道采样值
    3. ret = rt_adc_disable(adc_dev, ADC_DEV_CHANNEL);                 //关闭这个通道
    复制代码
    下面就用一个实例进行讲解,NTC测温

    实物图
    1.png
    材料:开发板,串口板,ntc带电路的传感器
    因为需要测试所有的adc通道,所以用了个核心板,jlinkv9烧写器

    Ntc电路图
    2.png
    设计要点:两个电容和电阻形成Π型滤波,adc的供电使用3.3v可以增强ntc抗干扰性。

    第一步形成工程
    下载到rtt5.0代码:https://gitee.com/rtthread/rt-thread
    安装env工具


    命令menuconfig打开图形化配置工具
    3.png
    选取第五项
    4.png
    找到如下的adc配置
    5.png
    按空格选取adc总使能和第一个通道(Channel0)

    正常情况下退出保存即可,但是这里有个不正常的驱动
    6.png
    7.png
    找到这里把MMA8562干掉,如果这里没有干掉,用我这个连接干掉即可

    退出保存,选yes即可
    8.png
    输入命令:scons –dist,进行搭建项目工程框架
    9.png
    然后再输入命令:scons --target=mdk5

    就可以生成工程文件了,用mdk5打开它
    10.png
    出现两个关键文件就是之前提到的adc.c和drv_adc.c
    这时候你就可以编译了,通过下载到开发板可以在命令行里看到驱动了。

    这证明了从系统到底层驱动都打通了。如果用p0.23,接传感器实际上可以用,因为系统原装本身只开通了p0.23的adc0_CN0通道。
    下面要进行驱动整改了
    其中不能动的是fsl_lpadc.c和adc.c文件
    drv_adc.c

    替换程序
    1. /*
    2. * Copyright (c) 2006-2023, RT-Thread Development Team
    3. *
    4. * SPDX-License-Identifier: Apache-2.0
    5. *
    6. * Change Logs:
    7. * Date           Author       Notes
    8. * 2019-04-20     tyustli      the first version.
    9. * 2019-07-15     Magicoe      The first version for LPC55S6x
    10. *
    11. */
    12. #include <rtthread.h>

    13. #ifdef BSP_USING_ADC                //adc总开关

    14. #define LOG_TAG             "drv.adc"
    15. #include <drv_log.h>
    16. #include "drv_adc.h"
    17. #include "fsl_power.h"
    18. #include "fsl_lpadc.h"
    19. #include <rtdevice.h>

    20. //adc使能
    21. static rt_err_t lpc_lpadc_enabled(struct rt_adc_device *device, rt_uint32_t channel, rt_bool_t enabled)
    22. {
    23.     return RT_EOK;
    24. }
    25. //adc转换
    26. //驱动、通道、变量
    27. static rt_err_t lpc_lpadc_convert(struct rt_adc_device *device, rt_uint32_t channel, rt_uint32_t *value)
    28. {
    29.     lpadc_conv_trigger_config_t mLpadcTriggerConfigStruct;
    30.     lpadc_conv_command_config_t mLpadcCommandConfigStruct;        //通道选择
    31.     lpadc_conv_result_t mLpadcResultConfigStruct;

    32.     ADC_Type *base;
    33.     base = (ADC_Type *)(device->parent.user_data);

    34.     /* Set conversion CMD configuration. */
    35.     LPADC_GetDefaultConvCommandConfig(&mLpadcCommandConfigStruct);//初始化命令结构体,默认12位分辨率,默认通道模式为A
    36.     mLpadcCommandConfigStruct.sampleChannelMode = (lpadc_sample_channel_mode_t)(channel>>3),      /* 通道模式 */
    37.     mLpadcCommandConfigStruct.channelNumber = channel&0x07;                                                           /* 使用通道 */
    38.     mLpadcCommandConfigStruct.chainedNextCommandNumber = 0;
    39.     mLpadcCommandConfigStruct.enableAutoChannelIncrement = false;
    40.     mLpadcCommandConfigStruct.loopCount = 0;
    41.     mLpadcCommandConfigStruct.hardwareAverageMode = kLPADC_HardwareAverageCount8;             /* ADC硬件平均 */
    42.     mLpadcCommandConfigStruct.sampleTimeMode = kLPADC_SampleTimeADCK19;                           /* ADC采样周期 */
    43.     mLpadcCommandConfigStruct.hardwareCompareMode = kLPADC_HardwareCompareDisabled;        /* 默认使用12位*/
    44.     //mLpadcCommandConfigStruct.conversionResolutionMode = (lpadc_conversion_resolution_mode_t)bit; /* ADC分辨率 */
    45.     mLpadcCommandConfigStruct.enableWaitTrigger = false;                                                           
    复制代码
    注意io相关是为了测试方便而加的,后期归类到板级io
    drv_adc.h
    替换程序
    1. /*
    2. * Copyright (c) 2006-2023, RT-Thread Development Team
    3. *
    4. * SPDX-License-Identifier: Apache-2.0
    5. *
    6. * Change Logs:
    7. * Date           Author       Notes
    8. * 2019-04-20     Lee          the first version.
    9. */

    10. #ifndef DRV_ADC_H__
    11. #define DRV_ADC_H__
    12. #include <rtdevice.h>
    13. #include "fsl_gpio.h"
    14. typedef enum  //ADC模块-枚举型
    15. {
    16.     //ADC通道 管脚关系        LPC55S69      
    17.     ADC0CH0A_P0_23=0,     /*!< P0_23    可用 AD0(20)*/
    18.     ADC0CH1A_P0_10=1,     /*!< P0_10    可用 AD1(21)*/
    19.     ADC0CH2A_P0_15=2,     /*!< P0_15    可用 AD2(22)*/
    20.     ADC0CH3A_P0_31=3,     /*!< P0_31    可用 AD3(23)*/
    21.     ADC0CH4A_P1_8 =4,     /*!< P1_8     可用 AD4(24)*/
    22.         
    23.     ADC0CH0B_P0_16=0+0X08,/*!< P0_16    可用 AD8(14)*/
    24.     ADC0CH1B_P0_11=1+0X08,/*!< P0_11    可用(SWD下载器口)(13)  */
    25.     ADC0CH2B_P0_12=2+0X08,/*!< P0_12    可用(SWD下载器口)(12) */
    26.     ADC0CH3B_P1_0=3+0X08, /*!< P1_0     可用 AD11 (11)*/                 
    复制代码
    注意io相关是为了测试方便而加的,后期归类到板级io
    rt_config.h

    整改项目:
    1. #define RT_TICK_PER_SECOND 1000                //1ms
    复制代码
    给系统提提速原来系统时钟是10ms,方便以后的操作
    在Applications下增加adc应用文件

    adc_tem.c
    1. /*********************************************************
    2. *Copyright (C), 2022, coyote
    3. *文件名:  adc_tem.c
    4. *作  者:  LYTX
    5. *版  本:  V1.00
    6. *日  期:  2022/09/1
    7. *描  述:  ADC读取函数,把数据放入堆栈中,其他函数需要时候读取最新数据
    8. *备  注:  适用于eaco-ES8P508x-海鲜机- V1.0
    9. **********************************************************/
    10. #include <rtthread.h>
    11. #include <rtdevice.h>
    12. #include "adc_tem.h"

    13. //暂时存放
    14. typedef enum  //ADC模块-枚举型
    15. {
    16.     //ADC通道 管脚关系        LPC55S69      
    17.     ADC0CH0A_P0_23=0,     /*!< P0_23    可用 AD0(20)*/
    18.     ADC0CH1A_P0_10=1,     /*!< P0_10    可用 AD1(21)*/
    19.     ADC0CH2A_P0_15=2,     /*!< P0_15    可用 AD2(22)*/
    20.     ADC0CH3A_P0_31=3,     /*!< P0_31    可用 AD3(23)*/
    21.     ADC0CH4A_P1_8 =4,     /*!< P1_8     可用 AD4(24)*/
    22.         
    23.     ADC0CH0B_P0_16=0+0X08,/*!< P0_16    可用 AD8(14)*/
    24.     ADC0CH1B_P0_11=1+0X08,/*!< P0_11    可用(SWD下载器口)(13)  */
    25.     ADC0CH2B_P0_12=2+0X08,/*!< P0_12    可用(SWD下载器口)(12) */
    26.     ADC0CH3B_P1_0=3+0X08, /*!< P1_0      可用 AD11(11)*/                 
    复制代码
    相关头文件
    adc_tem.h
    1. #ifndef __ADC_TEM_H__
    2. #define __ADC_TEM_H__

    3. #include <rtthread.h>
    4. #include <rtdevice.h>

    5. typedef enum
    6. {
    7.        ADC_NONE = 0x3000,   /* -1 表示无温度事件 */

    8. }ADC_ENUM;


    9. /* 存储ad的堆栈 */
    10. #define ADC_FILO_SIZE        10
    11. typedef struct
    12. {
    13.         rt_uint32_t Buf[ADC_FILO_SIZE];                /* 键值缓冲区 */
    14.         uint8_t Read;                                        /* 缓冲区读指针1 */
    15.         uint8_t Write;                                        /* 缓冲区写指针 */
    16. }ADC_FILO_T;//所有按键只需要一个,指针的消息队列


    17. //extern uint8_t xtkjxssjbzw;
    18. extern uint8_t adcjjsq;//AD采集计数器
    19. extern uint32_t adsumer;//AD采集数据暂存单元
    20. static void ADC0_UserInit(void);
    21. static void ADCACPConfig(void);
    22. static void ADCRead(void *parameter);
    23. static void adhandle(void);

    24. static void ADC_FILO_Put(rt_uint32_t _KeyCode);
    25. void ADC_FILO_Clear(void);                //堆栈清除函数
    26. //rt_uint32_t ADC_FILO_Get(void);                //得到堆栈最新数据函数
    27. rt_uint32_t ADC_FILO_Get_new(void);
    28. rt_uint32_t valuetodbc_entry(void);//外函数引用得到温度返回,数值为正,实际温度+50
    29. #endif
    复制代码
    main文件替换程序
    1. /*
    2. * Copyright (c) 2006-2023, RT-Thread Development Team
    3. * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
    4. *
    5. * SPDX-License-Identifier: Apache-2.0
    6. *
    7. * Change Logs:
    8. * Date           Author       Notes
    9. * 2019-10-24     Magicoe      first version
    10. * 2020-01-10     Kevin/Karl   Add PS demo
    11. * 2020-09-21     supperthomas fix the main.c
    12. *
    13. */

    14. #include <rtdevice.h>
    15. #include "dfs_fs.h"
    16. #include "drv_pin.h"
    17. #include "adc_tem.h"

    18. /* defined the LED pin: GPIO1_IO4 */
    19. /* GPIO1_4 is Blue LED */
    20. #define LEDB_PIN      GET_PINS(1, 4)


    21. //温度采集线程和基本温度比较控制(执行周期为10*34*3=1020ms一次)
    22. /*================================================================================
    23. *Function        Name         :GetADCValue
    24. *Description                  :获取NTC的ADC离散中心值
    25. *parameter                        :无
    26. *Return                                :ADCFilterValue
    27. ================================================================================*/
    28. rt_uint32_t GetADCValue(void)
    29. {
    30.         /*== 变量定义 ==*/
    31.         rt_uint32_t    ADCFilterValue = 0;
    32.         uint32_t *ADCArray;//数组元素的地址
    33.         uint32_t i,j,m=0;
    34.         uint32_t times = 3; //样本大小
    35.         /*== 获得样本数据 ==*/
    36.         ADCArray = (uint32_t *)malloc(times);
    37.         for(m=0;m<times;m++)
    38.         {
    39.                 ADCArray[m] = valuetodbc_entry();
    40.                 if(ADCArray[m]==0x2000||ADCArray[m]==ADC_NONE)
    41.                 return ADCArray[m];//传感器错误,直接返回
    42.         }
    43.         /*== 样本数据从小到大排列 ==*/
    44.         for (j=0;j<times-1;j++)
    45.         {
    46.                 for (i=0;i<times-1-j;i++)
    47.                 {
    48.                         if (ADCArray[i] > ADCArray[i+1])
    49.                         {
    50.                                 ADCArray[i]          ^= ADCArray[i+1];
    51.                                 ADCArray[i+1] ^= ADCArray[i];
    52.                                 ADCArray[i]          ^= ADCArray[i+1];
    53.                         }
    54.                 }
    55.         }
    56.         /*== 过滤远离目标值的无效值 ==*/
    57.         //这里只取了排序之后的中间的值作为有效值,也就是中间值
    58.         ADCFilterValue = ADCArray[(times-1)/2];
    59.         free(ADCArray);

    60.         return ADCFilterValue;
    61. }




    62. int main(void)
    63. {
    64.                 rt_uint32_t value1;
    65. #if defined(__CC_ARM)
    66.     rt_kprintf("using armcc, version: %d\n", __ARMCC_VERSION);
    67. #elif defined(__clang__)
    68.     rt_kprintf("using armclang, version: %d\n", __ARMCC_VERSION);
    69. #elif defined(__ICCARM__)
    70.     rt_kprintf("using iccarm, version: %d\n", __VER__);
    71. #elif defined(__GNUC__)
    72.     rt_kprintf("using gcc, version: %d.%d\n", __GNUC__, __GNUC_MINOR__);
    73. #endif

    74.     rt_pin_mode(LEDB_PIN, PIN_MODE_OUTPUT);  /* Set GPIO as Output */

    75. #ifdef RT_USING_SDIO
    76.     rt_thread_mdelay(2000);
    77.     if (dfs_mount("sd", "/", "elm", 0, NULL) == 0)
    78.     {
    79.         rt_kprintf("sd mounted to /\n");
    80.     }
    81.     else
    82.     {
    83.         rt_kprintf("sd mount to / failed\n");
    84.     }
    85. #endif
    86.     while (1)
    87.     {
    88.         rt_pin_write(LEDB_PIN, PIN_HIGH);    /* Set GPIO output 1 */
    89.         //rt_thread_mdelay(500);               /* Delay 500mS */
    90.         value1=GetADCValue();//获取温度传感器数据,由于信号量的限制,所以10ms*34执行一次
    91.         if(value1==0x2000)         rt_kprintf("tem is error\r\n");
    92.         else        rt_kprintf("tem is %d\r\n",value1-500);
    93.         rt_pin_write(LEDB_PIN, PIN_LOW);     /* Set GPIO output 0 */
    94.         //rt_thread_mdelay(500);               /* Delay 500mS */
    95.        value1=GetADCValue();//获取温度传感器数据,由于信号量的限制,所以10ms*34执行一次
    96.        if(value1==0x2000)         rt_kprintf("tem is error\r\n");
    97.        else        rt_kprintf("tem is %d\r\n",value1-500);
    98.     }
    99. }

    100. // end file
    复制代码
    到此如果变异成功恭喜了,如果电路无误,会在串口调试窗口输出温度,灯闪烁
    11.png
    PS:如果发现突然mdk5找不到内核没法下载程序,按isp按键再按重启就可以找到内核了。如果核心板,就短接P0.5,就是88脚位到地。

    12.png 13.png
    到此,工程完毕。

    解说:
    对于工程
    drv_adc.c的整改,从前后台来看,需要初始化adc,初始化就需要设置时钟,模块状态,io映射,其实和系统都一样的,组后系统需要进行一个注册机制
    1. rt_hw_adc_register(&adc0_device, "adc0", &lpc_adc_ops, ADC0);
    复制代码
    然后模块和对外引脚都工作了就可以读取了,比如切换一个通道进行读取一下
    如果前后台,就整一个定时读取,或者循环读取,读取需要的次数然后计算,
    如果系统,需要首先找寻驱动句柄
    1. adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME);
    复制代码
    得到句柄了就可以用句柄进行通道开关,读取了
    1. rt_adc_enable(adc_dev, ADC_DEV_CHANNEL);//使能这个通道
    复制代码
    多次
    1. value = rt_adc_read(adc_dev, ADC_DEV_CHANNEL);             //读取这个通道采样值
    复制代码
    执行,到数目后
    1. ret = rt_adc_disable(adc_dev, ADC_DEV_CHANNEL); //关闭这个通道
    复制代码
    然后进行数据处理
    从这可以看到
    drv_adc.c
    需要实现两个操作
    1. static struct rt_adc_ops lpc_adc_ops =

    2. {

    3. .enabled = lpc_lpadc_enabled,           //使能adc通道

    4. .convert = lpc_lpadc_convert,             //读取adc对应通道数值

    5. };
    复制代码
    这里整改了,驱动后可以多通道使用,驱动统一化。方便一个程序进行多通道读取然后共同使用数据。
    而体现在adc_tem.c的应用上,就是多次读取传感器的数值,进行平均数提取,然后比较分度表得到温度数值,具体流程有注释
    这里特别说明的是队列使用,按键可以,但是温度传感器容易在超出范围或者断开时候报错不能即使传给下一个程序的操作,比如通过温度进行比较断开继电器停止价格或者制冷等等。所以用了个对外的函数,用信号进行实时数据提供。
    Main函数具有最终温度使用函数,这里进行了输出操作,只为了演示,而有个离散框中方法为了解决小数点跳变问题,这一切是因为当初海尔m0单片机资源不够而阉割的。离散方法样本数据越多越准确。
    如果有其他问题可以共同讨论和具体询问。

    回复

    使用道具 举报

    该用户从未签到

    37

    主题

    327

    帖子

    1

    高级会员

    Rank: 4

    积分
    886
    最后登录
    2023-7-19
    发表于 2023-2-20 10:11:44 | 显示全部楼层
    厉害厉害
    回复

    使用道具 举报

    该用户从未签到

    0

    主题

    2

    帖子

    0

    新手上路

    Rank: 1

    积分
    18
    最后登录
    2024-1-12
    发表于 2023-12-30 08:16:57 | 显示全部楼层
    不错不错!
    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-4-20 10:57 , Processed in 0.135015 second(s), 22 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表