资讯详情

基于STM32设计的数字电子秤

1. 项目介绍

称重计量是当今社会活动中不可缺少的一部分,随着国际交流的发展,称重计量的国际统一越来越重要。

电子称重技术是现代称重测量和控制系统工程的重要基础之一。近年来,随着现代科学技术的进步,电子称重技术取得了突飞猛进的发展,电子秤在计量领域也发挥着越来越重要的作用。特别是商用电子衡器以其精度高、反应灵敏、性能稳定、结构简单、环境适应性强、与电子计算机结合方便、称重计量和过程控制自动化等特点,广泛应用于工商贸易、能源交通、冶金矿山、轻工食品、医疗卫生、航空航天等领域。

电子秤的工作原理是通过称重传感器收集被测物体的重量,并将其转换为电压信号。输出电压信号通常很小,需要通过前端信号处理电路准确线性放大。放大后的模拟电压信号通过A/D转换电路转换成数字量,送入主控电路的单片机,然后由单片机控制OLED显示屏,从而显示出被测物体的重量,在实际应用中为提高数据采集的精度,并尽量减少外界电气干扰还需要在传感器与A/D在芯片之间添加信号调节电路。

采用当前项目STM32 称重模块 OLED实现了简单的电子秤项目,称重模块采用24位ADC芯片,精度高。实现称重、校准、去皮等功能。

MCU:STM32F103ZET6,只要是STM32F1X本系列工程代码通用。

称重模块: 淘宝购买的称重模块

OLED: SPI接口的0.96寸OLED屏,采用的是中景园电子的OLED屏。

https://download.csdn.net/download/xiaolong1126626497/63993934

https://live.csdn.net/v/182608

image-20211221160445064

2. 项目实现

2.1 称重传感器

称重传感器是一种压力传感器,也称为悬臂梁压力传感器。安装时需要一端固定,另一端受力。内部有四个应变电阻片,共同形成电桥。当受力端施加压力时,传感器外壳会变形,从而影响应变电阻片的电阻值。

采用称重传感器 CS1237 将微小的电压信号转换为转换芯片 24 位精度数字信号。模块信号输入端可接受差分信号,内部有可编程操作放大器放大输入端的弱信号。模块内置温度传感器,可粗略估计周围温度。模块可用于电子秤、血液计、智能变换器等各种工业过程控制场合。

CS1237中有1路ADC,集成1路差分输入,信号输入可以是差分输入信号AINP、AINN,它也可以是温度传感器的输出信号,由寄存器切换输入信号(ch_sel控制。

CS1237是采用2线SPI串行通信,通过SCLK和DRDY/DOUT可实现数据接收和功能配置。

#include "ADC-CS1237.h"   static long AD_Res_Last=0;                  //上一轮ADC数值保存 /* 定义CS1237使用的GPIO口 CLK PB14 时钟线 OUT PB15 数据输出线 */ void CS1237_GPIO_INIT(void) { 
          RCC->APB2ENR |= 0x01 << 3;               //打开PB口    GPIOB->CRH &= 0xF0FFFFFF;              ///清零寄存器  GPIOB->CRH |= 0x03000000;              //通用推挽输出 50MHz  GPIOB->ODR |= 1<< 14;                //拉高CLK电平 }  void CS1237_DRDY(void)                //配置PB15为输入 { 
          GPIOB->CRH &= 0x0FFFFFFF;              ///清零寄存器  GPIOB->CRH |= 0x80000000;              //上下拉输入模式 }   void CS1237_DOUT
       
        (
        void
        ) 
        //配置PB15为输出 
        { 
          GPIOB
        ->CRH 
        &= 
        0x0FFFFFFF
        ; 
        //寄存器清零 GPIOB
        ->CRH 
        |= 
        0x30000000
        ; 
        //通用推挽输出 50MHz 
        } 
        //CS1237进入低功耗模式 
        void 
        CS1237_Power_Down
        (
        void
        ) 
        { 
          CLK_HIGH 
        delay_us
        (
        200
        )
        ; 
        //CLK上拉时间应超过100us,恢复后下拉时间至少10us 
        } 
        //配置CS1237芯片 
        void 
        Con_CS1237
        (
        void
        ) 
        { 
          u8 i
        ; u8 dat
        ; u8 count_i
        =
        0
        ; 
        //溢出计时器 dat 
        = CS_CON
        ; 
        // 0100 1000 
        CS1237_DOUT
        (
        )
        ; OUT_HIGH 
        delay_ms
        (
        310
        )
        ; 
        //上电建立时间 
        CS1237_DRDY
        (
        )
        ; 
        //配置PB15为输入  CLK_LOW 
        //时钟拉低 
        while
        (INT
        ) 
        //芯片准备好数据输出 时钟已经为0,数据也需要等CS1237全部拉低为0才算都准备好 
        { 
          
        printf
        (
        "123\r\n"
        )
        ; 
        delay_ms
        (
        100
        )
        ; 
        //10HZ下转换时间是100ms count_i
        ++
        ; 
        if
        (count_i 
        > 
        150
        ) 
        { 
          CLK_HIGH
        ; 
        CS1237_DOUT
        (
        )
        ; OUT_HIGH 
        return
        ; 
        //超时,则直接退出程序 
        } 
        } 
        for
        (i
        =
        0
        ;i
        <
        29
        ;i
        ++
        ) 
        // 1 - 29 
        { 
          One_CLK
        ; 
        } 
        CS1237_DOUT
        (
        )
        ; CLK_HIGH
        ;
        delay_us
        (
        6
        )
        ;OUT_HIGH
        ;CLK_LOW
        ;
        delay_us
        (
        6
        )
        ; 
        //30 CLK_HIGH
        ;
        delay_us
        (
        6
        )
        ;OUT_HIGH
        ;CLK_LOW
        ;
        delay_us
        (
        6
        )
        ; 
        //31 CLK_HIGH
        ;
        delay_us
        (
        6
        )
        ;OUT_LOW
        ;CLK_LOW
        ;
        delay_us
        (
        6
        )
        ; 
        //32 CLK_HIGH
        ;
        delay_us
        (
        6
        )
        ;OUT_LOW
        ;CLK_LOW
        ;
        delay_us
        (
        6
        )
        ; 
        //33 CLK_HIGH
        ;
        delay_us
        (
        6
        )
        ;OUT_HIGH
        ;CLK_LOW
        ;
        delay_us
        (
        6
        )
        ; 
        //34 CLK_HIGH
        ;
        delay_us
        (
        6
        )
        ;OUT_LOW
        ;CLK_LOW
        ;
        delay_us
        (
        6
        )
        ; 
        //35 CLK_HIGH
        ;
        delay_us
        (
        6
        )
        ;OUT_HIGH
        ;CLK_LOW
        ;
        delay_us
        (
        6
        )
        ; 
        //36 One_CLK
        ; 
        //37 写入了0x65 
        for
        (i
        =
        0
        ;i
        <
        8
        ;i
        ++
        ) 
        // 38 - 45个脉冲了,写8位数据 
        { 
          CLK_HIGH
        ; 
        delay_us
        (
        6
        )
        ; 
        if
        (dat
        &
        0x80
        ) OUT_HIGH 
        else OUT_LOW dat 
        <<= 
        1
        ; CLK_LOW
        ; 
        delay_us
        (
        6
        )
        ; 
        } 
        CS1237_DRDY
        (
        )
        ; One_CLK
        ; 
        //46个脉冲拉高数据引脚 
        } 
        //读取芯片的配置数据 u8 
        Read_CON
        (
        void
        ) 
        { 
          u8 i
        ; u8 dat
        =
        0
        ; 
        //读取到的数据 u8 count_i
        =
        0
        ; 
        //溢出计时器 
        CS1237_DOUT
        (
        )
        ; OUT_HIGH 
        CS1237_DRDY
        (
        )
        ; 
        //配置PB15为输入  CLK_LOW 
        //时钟拉低 
        while
        (INT
        ) 
        //芯片准备好数据输出 时钟已经为0,数据也需要等CS1237全部拉低为0才算都准备好 
        { 
          
        delay_ms
        (
        100
        )
        ; count_i
        ++
        ; 
        if
        (count_i 
        > 
        150
        ) 
        { 
          CLK_HIGH
        ; 
        CS1237_DOUT
        (
        )
        ; OUT_HIGH
        ; 
        return 
        1
        ; 
        //超时,则直接退出程序 
        } 
        } 
        for
        (i
        =
        0
        ;i
        <
        29
        ;i
        ++
        ) 
        // 1 - 29 
        { 
          One_CLK
        ; 
        } 
        CS1237_DOUT
        (
        )
        ; CLK_HIGH
        ;
        delay_us
        (
        6
        )
        ;OUT_HIGH
        ;CLK_LOW
        ;
        delay_us
        (
        6
        )
        ;
        //30 CLK_HIGH
        ;
        delay_us
        (
        6
        )
        ;OUT_LOW
        ;CLK_LOW
        ;
        delay_us
        (
        6
        )
        ;
        //31 CLK_HIGH
        ;
        delay_us
        (
        6
        )
        ;OUT_HIGH
        ;CLK_LOW
        ;
        delay_us
        (
        6
        )
        ;
        //32 CLK_HIGH
        ;
        delay_us
        (
        6
        )
        ;OUT_LOW
        ;CLK_LOW
        ;
        delay_us
        (
        6
        )
        ;
        //33 CLK_HIGH
        ;
        delay_us
        (
        6
        )
        ;OUT_HIGH
        ;CLK_LOW
        ;
        delay_us
        (
        6
        )
        ;
        //34 CLK_HIGH
        ;
        delay_us
        (
        6
        )
        ;OUT_HIGH
        ;CLK_LOW
        ;
        delay_us
        (
        6
        )
        ;
        //35 CLK_HIGH
        ;
        delay_us
        (
        6
        )
        ;OUT_LOW
        ;CLK_LOW
        ;
        delay_us
        (
        6
        )
        ;
        //36 One_CLK
        ;
        //37 写入了0x56 
        CS1237_DRDY
        (
        )
        ; dat
        =
        0
        ; 
        for
        (i
        =
        0
        ;i
        <
        8
        ;i
        ++
        ) 
        // 38 - 45个脉冲了,读取数据 
        { 
          One_CLK
        ; dat 
        <<= 
        1
        ; 
        if
        (INT
        ) dat
        ++
        ; 
        } One_CLK
        ; 
        //46个脉冲拉高数据引脚 
        return dat
        ; 
        } 
        //读取ADC数据,返回的是一个有符号数据 
        long 
        Read_CS1237
        (
        void
        ) 
        { 
          u8 i
        ; 
        long dat
        =
        0
        ; 
        //读取到的数据 u16 count_i
        =
        0
        ; 
        //溢出计时器 
        CS1237_DOUT
        (
        )
        ; OUT_HIGH 
        //等待模拟输入信号建立 CLK_LOW
        ; 
        //时钟拉低 
        CS1237_DRDY
        (
        )
        ; 
        while
        (INT
        ) 
        //芯片准备好数据输出 时钟已经为0,数据也需要等CS1237拉低为0才算都准备好 
        { 
          
        // printf("等待1\r\n"); 
        delay_ms
        (
        10
        )
        ; count_i
        ++
        ; 
        if
        (count_i 
        > 
        300
        ) 
        { 
          CLK_HIGH
        ; 
        CS1237_DOUT
        (
        )
        ; OUT_HIGH
        ; 
        return 
        0
        ; 
        //超时,则直接退出程序 
        } 
        } 
        CS1237_DOUT
        (
        )
        ; OUT_HIGH 
        //端口存1, 
        CS1237_DRDY
        (
        )
        ; dat
        =
        0
        ; 
        for
        (i
        =
        0
        ;i
        <
        24
        ;i
        ++
        ) 
        //获取24位有效转换 
        { 
          CLK_HIGH
        ; 
        delay_us
        (
        6
        )
        ; dat 
        <<= 
        1
        ; 
        if
        (INT
        ) dat 
        ++
        ; CLK_LOW
        ; 
        delay_us
        (
        6
        )
        ; 
        } 
        for
        (i
        =
        0
        ;i
        <
        3
        ;i
        ++
        ) 
        //一共输入27个脉冲 
        { 
          One_CLK
        ; 
        } 
        CS1237_DOUT
        (
        )
        ; OUT_HIGH
        ; 
        //先根据宏定义里面的有效位,丢弃一些数据 i 
        = 
        24 
        - ADC_Bit
        ;
        //i表示将要丢弃的位数 dat 
        >>= i
        ;
        //丢弃多余的位数 
        return dat
        ; 
        } 
        //初始化ADC相关参数 
        int 
        Init_CS1237
        (
        void
        ) 
        { 
          
        Con_CS1237
        (
        )
        ;
        //配置CS1237 
        if
        (
        Read_CON
        (
        ) 
        != CS_CON
        )
        //如果读取的ADC配置出错,则重启 
        { 
          
        printf
        (
        "读取错误\r\n"
        )
        ; 
        return 
        0
        ; 
        } 
        delay_us
        (
        10000
        )
        ; AD_Res_Last 
        = 
        Read_CS1237
        (
        )
        ; AD_Res_Last 
        = 
        Read_CS1237
        (
        )
        ; AD_Res_Last 
        = 
        Read_CS1237
        (
        )
        ; 
        return 
        0
        ; 
        } 
        //数字一阶滤波器 滤波系数A,小于1。上一次数值B,本次数值C out = b*A + C*(1-A) 
        //下面这个程序负责读取出最终ADC数据 
        long 
        Read_18Bit_AD
        (
        void
        ) 
        //18位的 
        { 
          
        float out
        ,c
        ; out 
        = AD_Res_Last
        ; c 
        = 
        Read_CS1237
        (
        )
        ; 
        if
        (c
        !=
        0
        ) 
        // 读到正确数据 
        { 
          out 
        = out
        *Lv_Bo 
        + c
        *
        (
        1
        -Lv_Bo
        )
        ; AD_Res_Last 
        = out
        ;
        //把这次的计算结果放到全局变量里面保护 
        } 
        return AD_Res_Last
        ; 
        } 
       

2.2 OLED显示屏

OLED显示屏是0.96寸 SPI接口显示屏,采用SSD1306驱动,兼容3.3V或5V电源输入,非常常见,淘宝一搜一大堆,当前选择的是中景园电子的OLED显示屏。

在调试设备或者测试数据时,有时候需要实时观察数据的变化,加入显示屏可以把观察设备的运行情况,数据变化等。在成本和难易程度上,OLED显示屏是非常适合初学者去学习与应用的。

#include "OLED.H"
#include "oled_font.h"

/* 定义OLED使用的GPIO口 D0 PA5 时钟线 D1 PA1 数据输出线 RES PA2 复位线 DC PA3 数据/命令选择线 CS PA4 片选线 */

void OLED_GPIO_INIT(void)
{ 
        
	RCC->APB2ENR |= 1<<2;       //打开PA口
	GPIOA->CRL &= 0xFF00000F;	//寄存器清零
	GPIOA->CRL |= 0x00333330;  //通用推挽输出 50MHz
	GPIOA->ODR |=0x003E;
}

void OLED_Init(void)
{ 
        
	OLED_GPIO_INIT();	//GPIO口初始化
 
	OLED_RES_HIGH;
	delay_ms(100);
	OLED_RES_LOW;
	delay_ms(200);								//延迟,由于单片机上电初始化比OLED快,所以必须加上延迟,等待OLED上电初始化完成
	OLED_RES_HIGH;
	delay_ms(200);

	OLED_WR_Byte(0xAE,OLED_CMD);	//关闭显示
	OLED_WR_Byte(0x2e,OLED_CMD);	//关闭滚动

	OLED_WR_Byte(0x00,OLED_CMD);	//设置低列地址
	OLED_WR_Byte(0x10,OLED_CMD);	//设置高列地址
	OLED_WR_Byte(0x40,OLED_CMD);	//设置起始行地址
	OLED_WR_Byte(0xB0,OLED_CMD);	//设置页地址

	OLED_WR_Byte(0x81,OLED_CMD);	// 对比度设置,可设置亮度
	OLED_WR_Byte(0xFF,OLED_CMD);	// 265 

	OLED_WR_Byte(0xA1,OLED_CMD);	//设置段(SEG)的起始映射地址;column的127地址是SEG0的地址
	OLED_WR_Byte(0xA6,OLED_CMD);	//正常显示;0xa7逆显示

	OLED_WR_Byte(0xA8,OLED_CMD);	//设置驱动路数
	OLED_WR_Byte(0x3F,OLED_CMD);	//1/64duty
	
	OLED_WR_Byte(0xC8,OLED_CMD);	//重映射模式,COM[N-1]~COM0扫描

	OLED_WR_Byte(0xD3,OLED_CMD);	//设置显示偏移
	OLED_WR_Byte(0x00,OLED_CMD);	//无偏移
	
	OLED_WR_Byte(0xD5,OLED_CMD);	//设置震荡器分频(默认)
	OLED_WR_Byte(0x80,OLED_CMD);	
	
	OLED_WR_Byte(0xD8,OLED_CMD);	//设置 area color mode off(没有)
	OLED_WR_Byte(0x05,OLED_CMD);
	
	OLED_WR_Byte(0xD6,OLED_CMD);	//放大显示
	OLED_WR_Byte(0x00,OLED_CMD);
	
	OLED_WR_Byte(0xD9,OLED_CMD);	//设置 Pre-Charge Period(默认)
	OLED_WR_Byte(0xF1,OLED_CMD);
	
	OLED_WR_Byte(0xDA,OLED_CMD);	//设置 com pin configuartion(默认)
	OLED_WR_Byte(0x12,OLED_CMD);
	
	OLED_WR_Byte(0xDB,OLED_CMD);	//设置 Vcomh,可调节亮度(默认)
	OLED_WR_Byte(0x30,OLED_CMD);
	
	OLED_WR_Byte(0x8D,OLED_CMD);	//设置OLED电荷泵
	OLED_WR_Byte(0x14,OLED_CMD);	//开显示
	
	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面板显示
	OLED_Clear();	//清屏
	OLED_Set_Pos(0,0); 	 //画点
}

void OLED_Write_Byte(u8 data)
{ 
        
	u8 i;	//定义变量
	for(i = 0; i < 8; i++)	//循环8次
	{ 
        
		OLED_D0_LOW		//将时钟线拉低
		delay_us(1);	//延迟 
		if(data & 0x80)	//数据从高位-->低位依次发送
			OLED_D1_HIGH	//数据为为1
		else
			OLED_D1_LOW	//数据位为0 
		data <<= 1;	//数据左移1位
		OLED_D0_HIGH	//时钟线拉高,把数据发送出去
		delay_us(1);	//延迟
	}
	
}


/* @brief 对OLED写入一个字节 @param dat:数据 cmd:1,写诶数据;0,写入命令 @retval 无 */
void OLED_WR_Byte(u8 dat,u8 cmd)
{ 
        
	if(cmd)  //如果cmd为高,则发送的是数据
		OLED_DC_HIGH	//将DC拉高
	else    //如果cmd为低,则发送的是命令
		OLED_DC_LOW	//将DC拉低
		
	OLED_CS_LOW; //片选拉低,选通器件
		
	OLED_Write_Byte(dat); //发送数据
		
	OLED_CS_HIGH //片选拉高,关闭器件 
	OLED_DC_HIGH //DC拉高,空闲时为高电平
}


/* @brief 设置数据写入的起始行、列 @param x: 列的起始低地址与起始高地址;0x00~0x0f:设置起始列低地址(在页寻址模式); 0x10~0x1f:设置起始列高地址(在页寻址模式) y:起始页地址 0~7 @retval 无 */
void OLED_Set_Pos(u8 x, u8 y) 
{ 
         
	OLED_WR_Byte(0xb0+y,OLED_CMD);//写入页地址
	OLED_WR_Byte((x&0x0f),OLED_CMD);  //写入列的地址 低半字节
	OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);//写入列的地址 高半字节
}   	     	  


/* @brief 开显示 @param 无 @retval 无 */ 
void OLED_Display_On(void)
{ 
        
	OLED_WR_Byte(0X8D,OLED_CMD);  //设置OLED电荷泵
	OLED_WR_Byte(0X14,OLED_CMD);  //使能,开
	OLED_WR_Byte(0XAF,OLED_CMD);  //开显示
}

/* @br

标签: 集成电路x0cs1206低阻值电阻

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台