打印
[其他产品]

MicroChip SAM L21 内部温度传感器

[复制链接]
6159|27
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
ic, ip, sam
MicroChip SAM L21 内部温度传感器的应用
温度采样的ADC配置



使用特权

评论回复
沙发
发GV第几啊|  楼主 | 2022-2-10 15:51 | 只看该作者
温度采样的ADC配置大体上和普通IO口的配置是一样的,但是需要注意以下几个点:

温度采样的采样时间需要大于40us,如果没有特殊要求,请尽量的大于40us。在使用过程中,发现过采样时间为60us时,温度采样有概率失败的问题。后我进一步提高了采样时间到88us,问题不再复现;
检测ADC配置中TSEN寄存器是否开启;
在使能ADC之前,关闭VREFOE,开启TEMPSENSE,然后再使能ADC,最后再读取温度。
下图为我使用的ADC配置。

使用特权

评论回复
板凳
发GV第几啊|  楼主 | 2022-2-10 15:54 | 只看该作者

使用特权

评论回复
地板
发GV第几啊|  楼主 | 2022-2-10 16:10 | 只看该作者
配置代码如下:
void ADC_Initialize( void )
{
    /* Reset ADC */
    ADC_REGS->ADC_CTRLA = (uint8_t)ADC_CTRLA_SWRST_Msk;

    while((ADC_REGS->ADC_SYNCBUSY & ADC_SYNCBUSY_SWRST_Msk) == ADC_SYNCBUSY_SWRST_Msk)
    {
        /* Wait for Synchronization */
    }
    /* Write linearity calibration in BIASREFBUF and bias calibration in BIASCOMP */
    uint32_t calib_low_word = (uint32_t)(*(uint64_t*)OTP5_ADDR);
    ADC_REGS->ADC_CALIB = (uint16_t)((ADC_CALIB_BIASREFBUF((calib_low_word & ADC_LINEARITY_Msk) >> ADC_LINEARITY_POS)) |
                                      (ADC_CALIB_BIASCOMP((calib_low_word & ADC_BIASCAL_Msk) >> ADC_BIASCAL_POS)));

    /* Prescaler */
    ADC_REGS->ADC_CTRLB = (uint8_t)ADC_CTRLB_PRESCALER_DIV2;
    /* Sampling length */
    ADC_REGS->ADC_SAMPCTRL = (uint8_t)ADC_SAMPCTRL_SAMPLEN(31UL);

    /* Reference */
    ADC_REGS->ADC_REFCTRL = (uint8_t)ADC_REFCTRL_REFSEL_INTVCC2;

    /* Input pin */
    ADC_REGS->ADC_INPUTCTRL = (uint16_t) ADC_POSINPUT_TEMP;

    /* Resolution & Operation Mode */
    ADC_REGS->ADC_CTRLC = (uint16_t)(ADC_CTRLC_RESSEL_12BIT | ADC_CTRLC_WINMODE(0UL) );


    /* Clear all interrupt flags */
    ADC_REGS->ADC_INTFLAG = (uint8_t)ADC_INTFLAG_Msk;

    while(0U != ADC_REGS->ADC_SYNCBUSY)
    {
        /* Wait for Synchronization */
    }
}

使用特权

评论回复
5
发GV第几啊|  楼主 | 2022-2-10 16:32 | 只看该作者
温度采集的公式换算
一般的MCU采用的内部温度换算公式比较简单,只需采集到AD值,经过简单的计算即可得到温度值,但是这款SAM L21为了确保温度的精准性,在出厂时为每个芯片写入了不同的基准电压。这也导致了温度换算的公式变得比一般的MCU更为复杂,这也是这个转换的麻烦点所在。我问过原厂的技术支持,官方好像并没有标准的温度例程,所以这得需要自己来完成。

使用特权

评论回复
6
发GV第几啊|  楼主 | 2022-2-10 16:40 | 只看该作者
以下是使用的代码,我简单讲解以下。厂家在出厂前,会往“TEMP_LOG_ADDR“寄存器写入几个初始值分别为室温温度值,高温温度值,这两个温度值对应的AD值,以及温度对应的参考电压的温漂值。知道这几个值后,我们就可以通过以下的这个公式变换得到当前的温度值。

使用特权

评论回复
7
发GV第几啊|  楼主 | 2022-2-10 16:56 | 只看该作者
VADC :温度AD采样出来的电压
temp :当前的温度
VADCR:室温对应的电压
tempR:室温
VADCH:高温对应的电压
tempH:高温

使用特权

评论回复
8
发GV第几啊|  楼主 | 2022-2-10 17:16 | 只看该作者
其中6个参数,后4个都是出厂时写好的固定值,VADC也是采集出来的变量了。通过以上这些值,可以很容易的获得当前温度。但是,这都是在不考虑参考电压的温漂的情况下的。如果你的实际应用不需要太精准的温度,那么通过以上的这个公式变换,就可以得到粗略的温度值。也足够满足日常使用了。但是如果你想要获得更精准的温度,那么就要通过以下的公式,进行进一步的换算。

使用特权

评论回复
9
发GV第几啊|  楼主 | 2022-2-10 17:17 | 只看该作者

使用特权

评论回复
10
发GV第几啊|  楼主 | 2022-2-10 17:19 | 只看该作者

使用特权

评论回复
11
发GV第几啊|  楼主 | 2022-2-10 17:23 | 只看该作者
下列是实际应用的代码。
#ifndef TEMPERATURE_H
#define        TEMPERATURE_H

#include <stdint.h>

#define TEMP_LOG_ADDR                  _UL_(0x00806030)    /**< TEMP_LOG base address (type: fuses)*/

#define NVM_TEMPERATURE_LOG_ROW_BASE    (*(uint32_t*)TEMP_LOG_ADDR)
#define NVM_TEMPERATURE_LOG_ROW_HIG_BASE    (*(uint32_t*)(TEMP_LOG_ADDR + 4))


// 室温温度值
#define ROOM_TEMP_VAL_INT     (NVM_TEMPERATURE_LOG_ROW_BASE & 0xFF)   
#define ROOM_TEMP_VAL_DEC     ((NVM_TEMPERATURE_LOG_ROW_BASE>>8) & 0x0F)

// 高温温度值
#define HOT_TEMP_VAL_INT     ((NVM_TEMPERATURE_LOG_ROW_BASE>>12) & 0xFF)
#define HOT_TEMP_VAL_DEC     ((NVM_TEMPERATURE_LOG_ROW_BASE>>20) & 0x0F)

// 相应温度下,参考电压的浮动值
#define ROOM_INT1V_VAL       ((NVM_TEMPERATURE_LOG_ROW_BASE>>24) & 0xFF)
#define HOT_INT1V_VAL       ((NVM_TEMPERATURE_LOG_ROW_HIG_BASE) & 0xFF)

// 相应温度的AD值
#define ROOM_ADC_VAL        ((NVM_TEMPERATURE_LOG_ROW_HIG_BASE>>8) & 0xFFF)
#define HOT_ADC_VAL        ((NVM_TEMPERATURE_LOG_ROW_HIG_BASE>>20) & 0xFFF)

室温温度值
#define ROOM_TEMP_VAL   (ROOM_TEMP_VAL_INT + ROOM_TEMP_VAL_DEC*0.1)

// 高温温度值
#define HOT_TEMP_VAL   (HOT_TEMP_VAL_INT + HOT_TEMP_VAL_DEC*0.1)


extern void Device_Temp_Init(void);
extern int8_t Get_Device_Temp(void);

#endif        /* GRID_H */

使用特权

评论回复
12
发GV第几啊|  楼主 | 2022-2-10 17:24 | 只看该作者
#include "definitions.h" // SYS function prototypes
#include "temperature.h"
#include "sapmle_pow.h"
static float f_int1_h; // 基准电压
static float f_int1_r;

static float f_temp_h; // 高温
static float f_temp_r; // 室温

static uint16_t u16_adc_h; // 校准过的ADC值
static uint16_t u16_adc_r; // 校准过的ADC值

void Device_Temp_Init(void) {
    // 基准电压换算

    f_int1_h = ((int8_t) HOT_TEMP_VAL_INT) * 0.001 + 1;
    f_int1_r = ((int8_t) ROOM_INT1V_VAL) * 0.001 + 1;

    // 温度换算
    f_temp_h = HOT_TEMP_VAL_INT + HOT_TEMP_VAL_DEC * 0.1;
    f_temp_r = ROOM_TEMP_VAL_INT + ROOM_TEMP_VAL_DEC * 0.1;

        // 校准ADC值
    u16_adc_h = HOT_ADC_VAL * f_int1_h + 0.5;
    u16_adc_r = ROOM_ADC_VAL * f_int1_r + 0.5;

}

int8_t Get_Device_Temp(void) {

    float f_temp_c; // 粗略温度值
    float f_temp_f; // 实际温度值
    float f_int1_m; // 基准电压
    uint16_t u16_adc_m;
   
    u16_adc_m = Get_ADC_Val(ADC_TEMP_CH) * 3.3; // 基准使用 3.3 V 所以放大3.3倍

    f_temp_c = f_temp_r + ((u16_adc_m - u16_adc_r)*(f_temp_h - f_temp_r) / (u16_adc_h - u16_adc_r)); // 温度粗值计算
    f_int1_m = f_int1_r + ((f_int1_h - f_int1_r)*(f_temp_c - f_temp_r) / (f_temp_h - f_temp_r)); // 基准电压计算
    u16_adc_m = u16_adc_m * f_int1_m + 0.5;
    f_temp_f = f_temp_r + ((u16_adc_m - u16_adc_r)*(f_temp_h - f_temp_r) / (u16_adc_h - u16_adc_r));

    return (int8_t) f_temp_f;
}

使用特权

评论回复
13
tpgf| | 2022-3-2 15:05 | 只看该作者
这个需要校对吗

使用特权

评论回复
14
wowu| | 2022-3-2 15:25 | 只看该作者
算法很是复杂啊

使用特权

评论回复
15
xiaoqizi| | 2022-3-2 15:30 | 只看该作者
有必要优化一下吗

使用特权

评论回复
16
木木guainv| | 2022-3-2 15:39 | 只看该作者
可以简单一点吗

使用特权

评论回复
17
磨砂| | 2022-3-2 15:47 | 只看该作者
需要的参数好多啊

使用特权

评论回复
18
晓伍| | 2022-3-2 15:54 | 只看该作者
移位比乘除好多了

使用特权

评论回复
19
isseed| | 2022-3-5 22:02 | 只看该作者
内部温度传感器在哪?

使用特权

评论回复
20
saservice| | 2022-3-5 23:06 | 只看该作者
为什么还需外部温度传感器

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

20

主题

276

帖子

0

粉丝