聚丰项目 > 基于CH32V103的体温检测仪

基于CH32V103的体温检测仪

本项目基于沁恒RISC-V架构的CH32V103,通过驱动ADT7320,实现了一个简单的体温检测,通过ADT7320接触人体表面,OLED屏幕显示出来。

懒得搭理世间烦扰 懒得搭理世间烦扰

分享
0 喜欢这个项目
团队介绍

懒得搭理世间烦扰 懒得搭理世间烦扰

团队成员

石文强

分享
项目简介
本项目基于沁恒RISC-V架构的CH32V103,通过驱动ADT7320,实现了一个简单的体温检测,通过ADT7320接触人体表面,OLED屏幕显示出来。
硬件说明

H32V103R8T6最高80MHz系统主频 ,片上集成 RAM 20K, Flash 64K,UART、IIC 、SPI、ADC、PWM、USB等资源。基于MounRiver Studio(MRS) 可使用RT-Thread nano进行开发。

ADT7320是一款4 mm × 4 mm LFCSP封装高精度数字温度传感器,可在较宽的工业温度范围内提供突破性的性能。它内置一个带隙温度基准源, 一个温度传感器和一个16位模数转换器(ADC),用来监控温度并进行数字转换,分辨率为0.0078°C。默认ADC分辨率设置为13位(0.0625°C)。ADC分辨率为用户可编程模式,可通过串行接口更改。

特性

高性能

温度精度:

±0.2°C(−10°C+85°C, 3 V~3.3V)

±0.25°C(−20°C+105°C, 3V~3.6V)

16位温度分辨率:0.0078°C

超低温漂:0.0073°C

NIST可溯源或相当功能

6 ms快速首次上电温度转换

易于实现

用户无需温度校准/校正

无需线性校正

低功耗

1 SPS(每秒采样率)省电模式

正常模式:700 μW(典型值,3.3 V)

关断模式:7 μW(典型值,3.3 V)

宽工作范围

温度范围:−40°C+150

电压范围:2.7 V5.5 V

可编程中断

临界过温中断

过温/欠温中断

SPI兼容型接口

16引脚、4 mm × 4 mm LFCSP封装,符合RoHS标准


下图是ADT7320的原理图

FsgBzCg1PZ98UzeprMsBENxcVXE9

软件说明

OLED的驱动代码

#include "oled.h"

#include "oledfont.h"


u8 OLED_GRAM[144][8];


//反显函数

void OLED_ColorTurn(u8 i)

{

    if(i==0)

        {

            OLED_WR_Byte(0xA6,OLED_CMD);//正常显示

        }

    if(i==1)

        {

            OLED_WR_Byte(0xA7,OLED_CMD);//反色显示

        }

}


//屏幕旋转180度

void OLED_DisplayTurn(u8 i)

{

    if(i==0)

        {

            OLED_WR_Byte(0xC8,OLED_CMD);//正常显示

            OLED_WR_Byte(0xA1,OLED_CMD);

        }

    if(i==1)

        {

            OLED_WR_Byte(0xC0,OLED_CMD);//反转显示

            OLED_WR_Byte(0xA0,OLED_CMD);

        }

}



void OLED_WR_Byte(u8 dat,u8 cmd)

{

    u8 i;

    if(cmd)

      OLED_DC_Set();

    else

      OLED_DC_Clr();

    OLED_CS_Clr();

    for(i=0;i<8;i++)

    {

        OLED_SCLK_Clr();

        if(dat&0x80)

           OLED_SDIN_Set();

        else

           OLED_SDIN_Clr();

        OLED_SCLK_Set();

        dat<<=1;

    }

    OLED_CS_Set();

    OLED_DC_Set();

}


//开启OLED显示

void OLED_DisPlay_On(void)

{

    OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能

    OLED_WR_Byte(0x14,OLED_CMD);//开启电荷泵

    OLED_WR_Byte(0xAF,OLED_CMD);//点亮屏幕

}


//关闭OLED显示

void OLED_DisPlay_Off(void)

{

    OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能

    OLED_WR_Byte(0x10,OLED_CMD);//关闭电荷泵

    OLED_WR_Byte(0xAF,OLED_CMD);//关闭屏幕

}


//更新显存到OLED

void OLED_Refresh(void)

{

    u8 i,n;

    for(i=0;i<8;i++)

    {

       OLED_WR_Byte(0xb0+i,OLED_CMD); //设置行起始地址

       OLED_WR_Byte(0x00,OLED_CMD);   //设置低列起始地址

       OLED_WR_Byte(0x10,OLED_CMD);   //设置高列起始地址

       for(n=0;n<128;n++)

         OLED_WR_Byte(OLED_GRAM[n][i],OLED_DATA);

  }

}

//清屏函数

void OLED_Clear(void)

{

    u8 i,n;

    for(i=0;i<8;i++)

    {

       for(n=0;n<128;n++)

            {

             OLED_GRAM[n][i]=0;//清除所有数据

            }

  }

    OLED_Refresh();//更新显示

}


//画点

//x:0~127

//y:0~63

void OLED_DrawPoint(u8 x,u8 y)

{

    u8 i,m,n;

    i=y/8;

    m=y%8;

    n=1<<m;

    OLED_GRAM[x][i]|=n;

}


//清除一个点

//x:0~127

//y:0~63

void OLED_ClearPoint(u8 x,u8 y)

{

    u8 i,m,n;

    i=y/8;

    m=y%8;

    n=1<<m;

    OLED_GRAM[x][i]=~OLED_GRAM[x][i];

    OLED_GRAM[x][i]|=n;

    OLED_GRAM[x][i]=~OLED_GRAM[x][i];

}



//画线

//x:0~128

//y:0~64

void OLED_DrawLine(u8 x1,u8 y1,u8 x2,u8 y2)

{

    u8 i,k,k1,k2;

    if((x1<0)||(x2>128)||(y1<0)||(y2>64)||(x1>x2)||(y1>y2))return;

    if(x1==x2)    //画竖线

    {

            for(i=0;i<(y2-y1);i++)

            {

                OLED_DrawPoint(x1,y1+i);

            }

  }

    else if(y1==y2)   //画横线

    {

            for(i=0;i<(x2-x1);i++)

            {

                OLED_DrawPoint(x1+i,y1);

            }

  }

    else      //画斜线

    {

        k1=y2-y1;

        k2=x2-x1;

        k=k1*10/k2;

        for(i=0;i<(x2-x1);i++)

            {

              OLED_DrawPoint(x1+i,y1+i*k/10);

            }

    }

}

//x,y:圆心坐标

//r:圆的半径

void OLED_DrawCircle(u8 x,u8 y,u8 r)

{

    int a, b,num;

    a = 0;

    b = r;

    while(2 * b * b >= r * r)

    {

        OLED_DrawPoint(x + a, y - b);

        OLED_DrawPoint(x - a, y - b);

        OLED_DrawPoint(x - a, y + b);

        OLED_DrawPoint(x + a, y + b);


        OLED_DrawPoint(x + b, y + a);

        OLED_DrawPoint(x + b, y - a);

        OLED_DrawPoint(x - b, y - a);

        OLED_DrawPoint(x - b, y + a);


        a++;

        num = (a * a + b * b) - r*r;//计算画的点离圆心的距离

        if(num > 0)

        {

            b--;

            a--;

        }

    }

}




//在指定位置显示一个字符,包括部分字符

//x:0~127

//y:0~63

//size:选择字体 12/16/24

//取模方式 逐列式

void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size1)

{

    u8 i,m,temp,size2,chr1;

    u8 y0=y;

    size2=(size1/8+((size1%8)?1:0))*(size1/2);  //得到字体一个字符对应点阵集所占的字节数

    chr1=chr-' ';  //计算偏移后的值

    for(i=0;i<size2;i++)

    {

        if(size1==12)

        {temp=asc2_1206[chr1][i];} //调用1206字体

        else if(size1==16)

        {temp=asc2_1608[chr1][i];} //调用1608字体

        else if(size1==24)

        {temp=asc2_2412[chr1][i];} //调用2412字体

        else return;

                for(m=0;m<8;m++)           //写入数据

                {

                    if(temp&0x80)OLED_DrawPoint(x,y);

                    else OLED_ClearPoint(x,y);

                    temp<<=1;

                    y++;

                    if((y-y0)==size1)

                    {

                        y=y0;

                        x++;

                        break;

          }

                }

  }

}



//显示字符串

//x,y:起点坐标

//size1:字体大小

//*chr:字符串起始地址

void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 size1)

{

    while((*chr>=' ')&&(*chr<='~'))//判断是不是非法字符!

    {

        OLED_ShowChar(x,y,*chr,size1);

        x+=size1/2;

        if(x>128-size1)  //换行

        {

            x=0;

            y+=2;

    }

        chr++;

  }

}


//m^n

u32 OLED_Pow(u8 m,u8 n)

{

    u32 result=1;

    while(n--)

    {

      result*=m;

    }

    return result;

}


////显示2个数字

////x,y :起点坐标

////len :数字的位数

////size:字体大小

void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size1)

{

    u8 t,temp;

    for(t=0;t<len;t++)

    {

        temp=(num/OLED_Pow(10,len-t-1))%10;

            if(temp==0)

            {

                OLED_ShowChar(x+(size1/2)*t,y,'0',size1);

      }

            else

            {

              OLED_ShowChar(x+(size1/2)*t,y,temp+'0',size1);

            }

  }

}


//显示汉字

//x,y:起点坐标

//num:汉字对应的序号

//取模方式 列行式

void OLED_ShowChinese(u8 x,u8 y,u8 num,u8 size1)

{

    u8 i,m,n=0,temp,chr1;

    u8 x0=x,y0=y;

    u8 size3=size1/8;

    while(size3--)

    {

        chr1=num*size1/8+n;

        n++;

            for(i=0;i<size1;i++)

            {

                if(size1==16)

                        {temp=Hzk1[chr1][i];}//调用16*16字体

                else if(size1==24)

                        {temp=Hzk2[chr1][i];}//调用24*24字体

                else if(size1==32)

                        {temp=Hzk3[chr1][i];}//调用32*32字体

                else if(size1==64)

                        {temp=Hzk4[chr1][i];}//调用64*64字体

                else return;


                        for(m=0;m<8;m++)

                            {

                                if(temp&0x01)OLED_DrawPoint(x,y);

                                else OLED_ClearPoint(x,y);

                                temp>>=1;

                                y++;

                            }

                            x++;

                            if((x-x0)==size1)

                            {x=x0;y0=y0+8;}

                            y=y0;

             }

    }

}


//num 显示汉字的个数

//space 每一遍显示的间隔

void OLED_ScrollDisplay(u8 num,u8 space)

{

    u8 i,n,t=0,m=0,r;

    while(1)

    {

        if(m==0)

        {

        OLED_ShowChinese(128,24,t,16); //写入一个汉字保存在OLED_GRAM[][]数组中

            t++;

        }

        if(t==num)

            {

                for(r=0;r<16*space;r++)      //显示间隔

                 {

                    for(i=0;i<144;i++)

                        {

                            for(n=0;n<8;n++)

                            {

                                OLED_GRAM[i-1][n]=OLED_GRAM[i][n];

                            }

                        }

           OLED_Refresh();

                 }

        t=0;

      }

        m++;

        if(m==16){m=0;}

        for(i=0;i<144;i++)   //实现左移

        {

            for(n=0;n<8;n++)

            {

                OLED_GRAM[i-1][n]=OLED_GRAM[i][n];

            }

        }

        OLED_Refresh();

    }

}


//配置写入数据的起始位置

void OLED_WR_BP(u8 x,u8 y)

{

    OLED_WR_Byte(0xb0+y,OLED_CMD);//设置行起始地址

    OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);

    OLED_WR_Byte((x&0x0f),OLED_CMD);

}


//x0,y0:起点坐标

//x1,y1:终点坐标

//BMP[]:要写入的图片数组

void OLED_ShowPicture(u8 x0,u8 y0,u8 x1,u8 y1,u8 BMP[])

{

    u32 j=0;

    u8 x=0,y=0;

    if(y%8==0)y=0;

    else y+=1;

    for(y=y0;y<y1;y++)

     {

         OLED_WR_BP(x0,y);

         for(x=x0;x<x1;x++)

         {

             OLED_WR_Byte(BMP[j],OLED_DATA);

             j++;

     }

     }

}

//OLED的初始化

void OLED_Init(void)

{

    GPIO_InitTypeDef  GPIO_InitStructure;


    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);    //使能A端口时钟

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;         //推挽输出

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz

    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_SetBits(GPIOA,GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5);


    OLED_RST_Clr();//复位

    Delay_Ms(200);

    OLED_RST_Set();


    OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel

    OLED_WR_Byte(0x00,OLED_CMD);//---set low column address

    OLED_WR_Byte(0x10,OLED_CMD);//---set high column address

    OLED_WR_Byte(0x40,OLED_CMD);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)

    OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register

    OLED_WR_Byte(0xCF,OLED_CMD);// Set SEG Output Current Brightness

    OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常

    OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常

    OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display

    OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)

    OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty

    OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset   Shift Mapping RAM Counter (0x00~0x3F)

    OLED_WR_Byte(0x00,OLED_CMD);//-not offset

    OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency

    OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec

    OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period

    OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock

    OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration

    OLED_WR_Byte(0x12,OLED_CMD);

    OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh

    OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level

    OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)

    OLED_WR_Byte(0x02,OLED_CMD);//

    OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable

    OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable

    OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)

    OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7)

    OLED_WR_Byte(0xAF,OLED_CMD);

    OLED_Clear();

}

ADT7320的驱动代码(该代码是基于在官方的代码上修改的)

/********************************************************************************

 Function:   ReadFromADT7320ViaSPI

 Parameter:  none

 Return value :  unsigned int

 Description :  Configure the regisiters of ADT7320, and read the convention data.

********************************************************************************/

#include "adt7320.h"


void ADT7320_Init(void){

    GPIO_InitTypeDef  GPIO_InitStructure;


    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);    //使能A端口时钟

    GPIO_InitStructure.GPIO_Pin = GPIO_ADT7320_DIN | GPOI_ADT7320_SCL | GPOI_ADT7320_CS;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;         //推挽输出

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz

    GPIO_Init(GPIOA, &GPIO_InitStructure);


    GPIO_InitStructure.GPIO_Pin = GPOI_ADT7320_DOUT;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;         //推挽输出

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz

    GPIO_Init(GPIOA, &GPIO_InitStructure);


    GPIO_SetBits(GPIOA,GPIO_ADT7320_DIN | GPOI_ADT7320_SCL | GPOI_ADT7320_CS);


}


unsigned int ReadFromADT7320ViaSPI(unsigned int reg_address, unsigned int Bit)

{

    unsigned int   i;

    unsigned int misoValue = 0;

    unsigned int spi_Value;

    //unsigned int flag;

    spi_Value = (0x40 | ( 0x78 & (reg_address << 3)));


    //flag = spi_Value;

    CS_1;//ADuC7026OutputBit(CS,1);

    SCL_1;//ADuC7026OutputBit(SCL,1);

    CS_0;//ADuC7026OutputBit(CS,0);

    Delay_Ms(5);//HAL_Delay(5);


    for(i=0;i<8;i++)

    {

    SCL_0   ;//ADuC7026OutputBit(SCL,0);

        if(spi_Value & 0x80)

            DIN_1;//ADuC7026OutputBit(DIN,1);

        else

            DIN_0;//ADuC7026OutputBit(DIN,0);

        Delay_Ms(5);//HAL_Delay(5);

        SCL_1;//ADuC7026OutputBit(SCL,1);

        spi_Value = (spi_Value << 1);

        Delay_Ms(5);//HAL_Delay(5);

    }

    DIN_1;

  //ADuC7026Delay(50);

    for(i=0;i<Bit;i++)

    {

    SCL_0;//ADuC7026OutputBit(SCL,0);

    misoValue = (misoValue << 1);

    Delay_Ms(10);//HAL_Delay(10);

        if((DOUT) == 1)

            misoValue |= 0x0001;

    else  {

        misoValue &= 0xfffe;

    }


        Delay_Ms(2);//HAL_Delay(2);

        SCL_1;//ADuC7026OutputBit(SCL,1);


        Delay_Ms(8);//HAL_Delay(8);

    }

    SCL_1;//ADuC7026OutputBit(SCL,1);

    CS_1;//ADuC7026OutputBit(CS,1);

    return  misoValue;

}


/********************************************************************************

 Function:   WriteToADT7320ViaSPI

 Parameter:  unsigned char spi_mosiValue

 Return value :  none

 Description :  Configure the regisiters of ADT7320, and read the convention data.

********************************************************************************/

void WriteToADT7320ViaSPI(unsigned int reg_address, unsigned int reg_data, unsigned int Bit)

{

    unsigned int   i;

    unsigned int spi_Value;

    unsigned int spi_Value_data;

    spi_Value = ( 0x78 & (reg_address << 3));

    spi_Value_data = reg_data;


    CS_1;//ADuC7026OutputBit(CS,1);

    SCL_1;//ADuC7026OutputBit(SCL,1);

    CS_0;//ADuC7026OutputBit(CS,0);


    for(i=0;i<8;i++)

    {

    SCL_0;//ADuC7026OutputBit(SCL,0);

        if(spi_Value & 0x80)

            DIN_1;//ADuC7026OutputBit(DIN,1);

        else

            DIN_0;//ADuC7026OutputBit(DIN,0);

        Delay_Ms(2);//          HAL_Delay(2);

        SCL_1;//ADuC7026OutputBit(SCL,1);

    spi_Value = (spi_Value << 1);

    Delay_Ms(5);//HAL_Delay(5);

    }


    if(Bit == 8)

    {

        for(i=0;i<Bit;i++)

        {

                SCL_0;//ADuC7026OutputBit(SCL,0);

                if((spi_Value_data & 0x80)==0x80)

                    DIN_1;//ADuC7026OutputBit(DIN,1);

                else

                    DIN_0;//ADuC7026OutputBit(DIN,0);

                Delay_Ms(2);//HAL_Delay(2);

            SCL_1;//ADuC7026OutputBit(SCL,1);

            spi_Value_data = (spi_Value_data << 1);

            Delay_Ms(5);//HAL_Delay(5);

        }

    }else{

        for(i=0;i<Bit;i++)

        {

                SCL_0;//ADuC7026OutputBit(SCL,0);

                if(spi_Value_data & 0x8000)

                    DIN_1;//ADuC7026OutputBit(DIN,1);

                else

                    DIN_0;//ADuC7026OutputBit(DIN,0);

                Delay_Ms(2);//HAL_Delay(2);

            SCL_1;//ADuC7026OutputBit(SCL,1);

            spi_Value_data = (spi_Value_data << 1);

            Delay_Ms(5);//HAL_Delay(5);

        }

    }

    SCL_1;//ADuC7026OutputBit(SCL,1);

    CS_1;//ADuC7026OutputBit(CS,1);

}


附件

(0.53 MB)下载

评论区(0 )