[产品应用] 基于CW32L052电子秤设计

[复制链接]
 楼主| lulugl 发表于 2023-8-4 19:28 | 显示全部楼层 |阅读模式
本帖最后由 lulugl 于 2023-8-4 19:33 编辑

#申请原创# #有奖活动#[url=home.php?mod=space&uid=760190]@21小跑堂 [/url]
电子秤作为一种计量手段,广泛应用于工农业、科研、交通、内外贸易等各个领域,与人民的生活紧密相连。电子秤是电子衡器中的一种,衡器是国家法定计量器具,是国计民生、国防建设、科学研究、内外贸易不可缺少的计量设备,衡器产品技术水平的高低,将直接影响各行各业的现代化水平和社会经济效益的提高。称重装置不仅是提供重量数据的单体仪表,而且作为工业控制系统和商业管理系统的一个组成部分,推进了工业生产的自动化和管理的现代化,它起到了缩短作业时间、改善操作条件、降低能源和材料的消耗、提高产品质量以及加强企业管理、改善经营管理等多方面的作用。称重装置的应用已遍及到国民经济各领域,取得了显著的经济效益。
电子秤是称重技术中的一种新型仪表,广泛应用于各种场合。电子秤与机械秤比较有体积小、重量轻、结构简单、价格低、实用价值强、维护方便等特点,可在各种环境工作,重量信号可远传,易于实现重量显示数字化,易于与计算机联网,实现生产过程自动化,提高劳动生产率。从世界水平看,衡器技术已经经历了四个阶段,从传统的全部由机械元器件组成的机械称到用电子线路代替部分机械元器件的机电结合秤,再从集成电路式到目前的单片机系统设计的电子计价秤。我国电子衡器从最初的机电结合型发展到现在的全电子型和数字智能型。现今电子衡器制造技术及应用得到了新发展:电子称重技术从静态称重向动态称重发展;计量方法从模拟测量向数字测量发展;测量特点从单参数测量向多参数测量发展。常规的测试仪器仪表和控制装置被更先进的智能仪器所取代,使得传统的电子测量仪器在远离、功能、精度及自动化水平定方面发生了巨大变化,并相应的出现了各种各样的智能仪器控制系统,使得科学实验和应用工程的自动化程度得以显著提高。
电子称重的实现首先是通过压力传感器采集到被测物体的重量并将其转换成电压信号。输出电压信号通常很小,需要通过前端信号处理电路进行准确的线性放大。放大后的模拟电压信号经A/D转换电路转换成数字量被送入到主控电路的单片机中,再经过单片机控制译码显示器,从而显示出被测物体的重量。按照设计的基本要求,系统可分为三大模块,数据采集模块、控制器模块、人机交互液晶显示界面模块。其中数据采集模块由压力传感器、信号的前级处理和A/D转换部分组成。转换后的数字信号送给控制器处理,由控制器完成对该数字量的处理,驱动显示模块完成人机间的信息交换。此部分对软件的设计要求比较高,系统的大部分功能都需要软件来控制。
本次电子称的实现流程如下:
5804d830c70e5f786c4b2cc53a32c91a
系统采用压电传感器是一种典型的有源传感器,又称自发电式传感器。其工作原理是基于某些材料受力后在其相应的特定表面产生电荷的压电效应。
压电传感器体积小、重量轻、结构简单、工作可靠,适用于动态力学量的测量,不适合测频率太低的被测量,更不能测静态量。目前多用于加速度和动态力或压力的测量。压电器件的弱点:高内阻、小功率。功率小,输出的能量微弱,电缆的分布电容及噪声干扰影响输出特性,这对外接电路要求很高。
电阻应变式传感器是一种利用电阻应变效应,将各种力学量转换为电信号的结构型传感器。电阻应变片式电阻应变式传感器的核心元件,其工作原理是基于材料的电阻应变效应,电阻应变片即可单独作为传感器使用,又能作为敏感元件结合弹性元件构成力学量传感器。
导体的电阻随着机械变形而发生变化的现象叫做电阻应变效应。电阻应变片把机械应变信号转换为△R/R后,由于应变量及相应电阻变化一般都很微小,难以直接精确测量,且不便处理。因此,要采用转换电路把应变片的△R/R变化转换成电压或电流变化。其转换电路常用测量电桥。
直流电桥的特点是信号不会受各元件和导线的分布电感及电容的影响,抗干扰能力强,但因机械应变的输出信号小,要求用高增益和高稳定性的放大器放大。
本次采用的如称重转感器如下图所示:
77e4b5a73010533734556ad5cf3cf56c
应变片式传感器有如下特点:
(1)应用和测量范围广,应变片可制成各种机械量传感器。
(2)分辨力和灵敏度高,精度较高。
(3)结构轻小,对试件影响小, 对复杂环境适应性强,可在高温、高压、强磁场等特殊环境中使用,频率响应好。
(4)商品化,使用方便,便于实现远距离、自动化测量[5]。
通过对压力传感器与电阻应变式传感器比较分析,最终选择了第二种方案。题目要求称重范围0~5Kg,满量程量误差不大于0.005Kg,考虑到秤台自重、振动和冲击分量,还要避免超重损坏传感器,所以传感器量程必须大于额定称重5Kg。我们选择的是电阻应变片压力传感器,量程为10Kg,精度为0.01% ,满足本系统的精度要求。
系统AD转换芯片选择
HX711是一款专为高精度电子秤而设计的24位A/D转换器芯片。与同类型其它芯片相比,该芯片集成了包括稳压电源、片内时钟振荡器等其它同类型芯片所需要的外围电路,具有集成度高、响应速度快、抗干扰性强等优点。降低了电子秤的整机成本,提高了整机的性能和可靠性。该芯片与后端MCU 芯片的接口和编程非常简单,所有控制信号由管脚驱动,无需对芯片内部的寄存器编程。输入选择开关可任意选取通道A 或通道B,与其内部的低噪声可编程放大器相连。通道A 的可编程增益为128 或64,对应的满额度差分输入信号幅值分别为±20mV或±40mV。通道B 则为固定的64 增益[[]9,用于系统参数检测。芯片内提供的稳压电源可以直接向外部传感器和芯片内的A/D 转换器提供电源,系统板上无需另外的模拟电源。芯片内的时钟振荡器不需要任何外接器件。上电自动复位功能简化了开机的初始化过程。芯片原理图如下所示
e9c452139460db4e2952fadd1d0ba6e3
系统显示器选择
CW32L052开发板板载了8位段码LCD屏。
代码设计:
HX711.h
  1. #ifndef __HX711_H
  2. #define __HX711_H

  3. #include "main.h"
  4. #include "cw32l052_gpio.h"
  5. #include "cw32L052_rcc.h"

  6. //PF04 SCK
  7. //PF05 DOUT
  8. #define HX711_SCK  CW_GPIOF->ODR_f.PIN4
  9. #define HX711_DOUT GPIO_ReadPin(CW_GPIOF, GPIO_PIN_5)


  10. extern void Init_HX711pin(void);
  11. extern uint32_t HX711_Read(void);
  12. extern void Get_Maopi(void);
  13. extern void Get_Weight(void);

  14. extern uint32_t HX711_Buffer;
  15. extern uint32_t Weight_Maopi;
  16. extern int32_t Weight_Shiwu;
  17. extern uint8_t Flag_Error;

  18. #endif

HX711.c
  1. /************************************************************************************
  2.                                                 
  3. *************************************************************************************/
  4. #include "HX711.h"


  5. uint32_t HX711_Buffer;
  6. uint32_t Weight_Maopi;
  7. int32_t Weight_Shiwu;
  8. uint8_t Flag_Error = 0;

  9. //校准参数
  10. //因为不同的传感器特性曲线不是很一致,因此,每一个传感器需要矫正这里这个参数才能使测量值很准确。
  11. //当发现测试出来的重量偏大时,增加该数值。
  12. //如果测试出来的重量偏小时,减小改数值。
  13. //该值可以为小数
  14. #define GapValue 212.5
  15. void delay_1us(void)
  16. {
  17.         __NOP();
  18.         __NOP();
  19. }

  20. void Init_HX711pin(void)
  21. {
  22.         __RCC_GPIOF_CLK_ENABLE();
  23.         GPIO_InitTypeDef GPIO_InitStructure;


  24.         //HX711_SCK
  25.         GPIO_InitStructure.Pins = GPIO_PIN_4;
  26.         GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
  27.         GPIO_Init(CW_GPIOF, &GPIO_InitStructure);

  28.         GPIO_InitStructure.Pins = GPIO_PIN_5;
  29.         GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
  30.         GPIO_Init(CW_GPIOF, &GPIO_InitStructure);
  31.         
  32.         GPIO_SetBits(CW_GPIOF,GPIO_PIN_4);                                        //初始化设置为0
  33. }



  34. //****************************************************
  35. //读取HX711
  36. //****************************************************
  37. uint32_t HX711_Read(void)        //增益128
  38. {
  39.         
  40.         unsigned long count;
  41.         unsigned char i;
  42.           //HX711_DOUT=1;
  43.                 CW_GPIOF->ODR_f.PIN5 = 1;
  44.         
  45.           HX711_SCK=0;
  46.           count=0;
  47.           while(HX711_DOUT);
  48.           for(i=0;i<24;i++)
  49.         {
  50.                   HX711_SCK=1;
  51.                   count=count<<1;
  52.                 delay_1us();
  53.                 HX711_SCK=0;
  54.                   if(HX711_DOUT)
  55.                         count++;
  56.                 delay_1us();
  57.         }
  58.          HX711_SCK=1;
  59.   count=count^0x800000;//第25个脉冲下降沿来时,转换数据
  60.         delay_1us();
  61.         HX711_SCK=0;  
  62.         return(count);
  63. }

  64. //****************************************************
  65. //获取毛皮重量
  66. //****************************************************
  67. void Get_Maopi(void)
  68. {
  69.         Weight_Maopi = HX711_Read();        
  70. }

  71. //****************************************************
  72. //称重
  73. //****************************************************
  74. void Get_Weight(void)
  75. {
  76.         HX711_Buffer = HX711_Read();
  77.         if(HX711_Buffer > Weight_Maopi)                        
  78.         {
  79.                 Weight_Shiwu = HX711_Buffer;
  80.                 Weight_Shiwu = Weight_Shiwu - Weight_Maopi;                                //获取实物的AD采样数值。
  81.         
  82.                 Weight_Shiwu = (int32_t)((float)Weight_Shiwu/GapValue);         //计算实物的实际重量
  83.                                                                                                                                                 //因为不同的传感器特性曲线不一样,因此,每一个传感器需要矫正这里的GapValue这个除数。
  84.                                                                                                                                                 //当发现测试出来的重量偏大时,增加该数值。
  85.                                                                                                                                                 //如果测试出来的重量偏小时,减小改数值。
  86.         }

  87.         
  88. }

LCD

  1. #include "lcd.h"

  2. /* 段码低8(左) */
  3. static const uint8_t num_L[19] = {
  4.     0x0d, //0
  5.     0x00, //1
  6.     0x0e, //2
  7.     0x0a, //3
  8.     0x03, //4
  9.     0x0b, //5
  10.     0x0f, //6
  11.     0x00, //7
  12.     0x0f, //8
  13.     0x0b, //9
  14.     0x07, //A
  15.     0x0f, //b
  16.     0x0d, //C
  17.     0x0e, //d
  18.     0x0f, //E
  19.     0x07, //F
  20.     0x07, //H
  21.     0x0d, //L
  22.     0x02, //-
  23. };
  24. /* 段码高8(右) */
  25. static const uint8_t num_H[19] = {
  26.     0x07,
  27.     0x06,
  28.     0x03,
  29.     0x07,//3
  30.     0x06,//4
  31.     0x05, //5
  32.     0x05, //
  33.     0x07, //7
  34.     0x07, //8
  35.     0x07, //9
  36.     0x07, //A
  37.     0x04, //B
  38.     0x01, //C
  39.     0x06, //d
  40.     0x01, //e
  41.     0x01, //f
  42.     0x06, //H/*
  43.     0x00, //L
  44.     0x00, //-
  45. };

  46. /* 函数功能:在指定的段码屏位置上显示指定序号的字符
  47. *参数wei:
  48.    指定需要显示的位
  49. *参数num:
  50.    需要显示的序号。
  51.    */
  52. void lcd_show_string(uint8_t wei, uint8_t num)
  53. {
  54.     if(wei>7 || num > sizeof(num_H))
  55.     {
  56.         return;
  57.     }
  58.     switch(wei)
  59.     {
  60.     case 7:
  61.     {
  62. //显示第7个数码管
  63.         CW_LCD->RAM[0] |= num_H[num]<<8 | num_L[num];

  64.         break;
  65.     }
  66.     case 6:
  67.     {
  68. //显示第6个数码管
  69.         CW_LCD->RAM[0] |= (num_H[num]<<8 | num_L[num]) <<16;
  70.         break;
  71.     }
  72.     case 5:
  73.     {
  74. //显示第5个数码管
  75.         CW_LCD->RAM[1] |= num_H[num]<<8 | num_L[num];
  76.         break;
  77.     }
  78.     case 4:
  79.     {
  80. //显示第4个数码管
  81.         CW_LCD->RAM[1] |= (num_H[num]<<8 | num_L[num]) <<16;
  82.         break;
  83.     }
  84.     case 3:
  85.     {
  86. //显示第3个数码管
  87.         CW_LCD->RAM[2] |= num_H[num]<<8 | num_L[num];
  88.         break;
  89.     }
  90.     case 2:
  91.     {
  92. //显示第2个数码管
  93.         CW_LCD->RAM[3] |= (num_H[num]<<8);
  94.         CW_LCD->RAM[2] |= (num_L[num]<<16);
  95.         break;
  96.     }
  97.     case 1:
  98.     {
  99. //显示第1个数码管
  100.         CW_LCD->RAM[3] |= num_H[num]<<24 | num_L[num]<<16;
  101.         break;
  102.     }
  103.     case 0:
  104.     {
  105. //显示第0个数码管
  106.         CW_LCD->RAM[4] |= (num_H[num]<<8 | num_L[num]);
  107.         break;
  108.     }
  109.     }

  110. };

  111. void lcd_init(void)
  112. {
  113.         
  114.         LCD_InitTypeDef LCD_InitStruct = {0};
  115.         RCC_LSI_Enable();    // 启动LSI为LCD提供时钟
  116.         __RCC_LCD_CLK_ENABLE();
  117.         
  118.         LCD_InitStruct.LCD_Bias = LCD_Bias_1_3;
  119.         LCD_InitStruct.LCD_ClockSource = LCD_CLOCK_SOURCE_LSI;
  120.         LCD_InitStruct.LCD_Duty = LCD_Duty_1_4;
  121.         LCD_InitStruct.LCD_ScanFreq = LCD_SCAN_FREQ_256HZ;
  122.         LCD_InitStruct.LCD_VoltageSource = LCD_VoltageSource_Internal;
  123.         
  124.         LCD_Init(&LCD_InitStruct);
  125.         LCD_COMConfig(LCD_COM0 | LCD_COM1 | LCD_COM2 | LCD_COM3 , ENABLE);
  126.         LCD_SEG0to23Config(LCD_SEG0|LCD_SEG1|LCD_SEG2|LCD_SEG3|LCD_SEG4|LCD_SEG5|LCD_SEG6|LCD_SEG7|LCD_SEG8|LCD_SEG9|LCD_SEG10|LCD_SEG13|LCD_SEG14|LCD_SEG15|LCD_SEG16|LCD_SEG17, ENABLE);
  127.    
  128.         LCD_Cmd(ENABLE);
  129.         LCD_ContrastConfig(LCD_Contrast_Level_6);
  130.         LCD_DriveVoltageConfig(LCD_INRS_LEVEL_0);
  131.         
  132. }

  133. void lcd_clear(void)
  134. {
  135.         CW_LCD->RAM[0] =0;
  136.         CW_LCD->RAM[1] =0;
  137.         CW_LCD->RAM[2] =0;
  138.         CW_LCD->RAM[3] =0;
  139.         CW_LCD->RAM[4] =0;
  140. }

主程序代码:
  1. /******************************************************************************/

  2. #include "main.h"
  3. #include "log.h"
  4. void show_weight(uint32_t weight);

  5. int32_t main(void)
  6. {
  7.         lcd_init();
  8.         Init_HX711pin();
  9.         LogInit();
  10.         InitTick(SystemCoreClock);
  11.         Get_Maopi();
  12.         SysTickDelay(2000);
  13.         Get_Maopi();                                //重新获取毛皮重量
  14.     CW_SYSCTRL->AHBEN_f.GPIOA = 1U;    //Open GPIOA Clk

  15.     CW_GPIOA->ANALOG_f.PIN0 = 0U;      //Digital
  16.     CW_GPIOA->BRR_f.BRR0 = 1U;         //Reset PA00
  17.     CW_GPIOA->DIR_f.PIN0 = 0U;         //Output

  18.                 printf("start...\r\n");
  19.     while(1)
  20.     {
  21.                         Get_Weight();

  22.                         printf("净重量 = %d g\r\n",Weight_Shiwu); //打印
  23.                         show_weight(Weight_Shiwu);
  24.                         SysTickDelay(1000);
  25.       CW_GPIOA->TOG = bv0;

  26.     }

  27. }

  28. void show_weight(uint32_t weight)
  29. {
  30.         lcd_clear();
  31.         if(weight>=10000)
  32.         {
  33.                 lcd_show_string(4,weight/10000);
  34.                 lcd_show_string(3,weight%10000/1000);
  35.                 lcd_show_string(2,weight%1000/100);
  36.                 lcd_show_string(1,weight%100/10);
  37.                 lcd_show_string(0,weight%10);
  38.         }
  39.         else if(weight>=1000)
  40.         {
  41.                 lcd_show_string(3,weight/1000);
  42.                 lcd_show_string(2,weight%1000/100);
  43.                 lcd_show_string(1,weight%100/10);
  44.                 lcd_show_string(0,weight%10);
  45.         }
  46.         else if(weight>=100)
  47.         {
  48.                 lcd_show_string(2,weight/100);
  49.                 lcd_show_string(1,weight%100/10);
  50.                 lcd_show_string(0,weight%10);               
  51.         }
  52.         else if(weight>=10)
  53.         {
  54.                 lcd_show_string(1,weight/10);
  55.                 lcd_show_string(0,weight%10);               
  56.         }
  57.         else{
  58.                 lcd_show_string(0,weight%10);
  59.         }
  60.         
  61. }
  62. /******************************************************************************
  63. * EOF (not truncated)
  64. ******************************************************************************/
  65. #ifdef  USE_FULL_ASSERT
  66. /**
  67.   * [url=home.php?mod=space&uid=247401]@brief[/url]  Reports the name of the source file and the source line number
  68.   *         where the assert_param error has occurred.
  69.   * @param  file: pointer to the source file name
  70.   * @param  line: assert_param error line source number
  71.   * @retval None
  72.   */
  73. void assert_failed(uint8_t *file, uint32_t line)
  74. {
  75.     /* USER CODE BEGIN 6 */
  76.     /* User can add his own implementation to report the file name and line number,
  77.        tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  78.     /* USER CODE END 6 */
  79. }
  80. #endif /* USE_FULL_ASSERT */

【调试】
由于不同的传感器,不同的温度,传感器的AD转换需要较调,我这里采集了100克的法码进行了设调,即修改GapValue的值。
【工程效果】:
61d0f3557c16f0b8e466fbcc8387a89e


chenjun89 发表于 2023-8-5 20:05 来自手机 | 显示全部楼层
不错,设计简洁,朴实无华,哈哈。
weifeng90 发表于 2023-8-6 09:02 来自手机 | 显示全部楼层
0.01%的精度,怎么校准?
 楼主| lulugl 发表于 2023-8-6 11:19 | 显示全部楼层
weifeng90 发表于 2023-8-6 09:02
0.01%的精度,怎么校准?

用法码较了一下,要想非常精准,还需要测量温度来较准。
sdlls 发表于 2023-8-7 16:18 | 显示全部楼层
这个电子秤时怎么校验的?              
macpherson 发表于 2023-8-7 16:32 | 显示全部楼层
注意电源管理,使得电子秤具备低功耗和长续航能力。
nomomy 发表于 2023-8-7 16:56 | 显示全部楼层
使用CW32L052微控制器,它具有高性能和低功耗的特点,非常适合用于电子秤设计。
kmzuaz 发表于 2023-8-7 17:18 | 显示全部楼层
校准过程可能涉及使用已知重量进行比较和调整。
 楼主| lulugl 发表于 2023-8-7 17:30 | 显示全部楼层
macpherson 发表于 2023-8-7 16:32
注意电源管理,使得电子秤具备低功耗和长续航能力。

就是做个示例,如果做成产品,还有许多因素要考虑的。L052带了LCD控制器,做称还是非常合适的。
 楼主| lulugl 发表于 2023-8-7 17:31 | 显示全部楼层
kmzuaz 发表于 2023-8-7 17:18
校准过程可能涉及使用已知重量进行比较和调整。

真正的较准,要在指定的温度、湿度下面进行较准。没有这个专业设备。
sanfuzi 发表于 2023-8-7 17:40 | 显示全部楼层
单片机电子秤设计如何标定?               
 楼主| lulugl 发表于 2023-8-7 17:44 | 显示全部楼层
sanfuzi 发表于 2023-8-7 17:40
单片机电子秤设计如何标定?

用法码标定呀。要想准确,还得标定每个温度值的标定植,处理起来比较麻烦。因为压力传感是金属,所以不同的温度,要给出不同的较准值。
ccook11 发表于 2023-8-7 18:02 | 显示全部楼层
CW32L052是一款低功耗的ARM Cortex-M0+微控制器,适合用于电子秤等嵌入式系统的设计
phoenixwhite 发表于 2023-8-7 18:27 | 显示全部楼层
选择合适的传感器来测量重量 。然后需要使用单片机或微控制器来控制整个系统。
uytyu 发表于 2023-8-7 18:50 | 显示全部楼层
电子秤在设计 中为了减少误差,具体有哪些方**
fengm 发表于 2023-8-7 19:12 | 显示全部楼层
扭矩传感器设计一个电子秤               
mnynt121 发表于 2023-8-7 19:33 | 显示全部楼层
传感器如何选择?A/D如何选择?
loutin 发表于 2023-8-7 19:55 | 显示全部楼层
网上有资料可以参考的吗?              
biechedan 发表于 2023-8-7 20:18 | 显示全部楼层
使用ADC来将模拟信号转换为数字信号
saservice 发表于 2023-8-7 20:41 | 显示全部楼层
常见的选择包括压力传感器、应变片传感器
您需要登录后才可以回帖 登录 | 注册

本版积分规则

180

主题

830

帖子

12

粉丝
快速回复 在线客服 返回列表 返回顶部