写点什么

基于 STM32 的重力感应售货机系统设计

作者:DS小龙哥
  • 2024-07-04
    重庆
  • 本文字数:5185 字

    阅读完需:约 17 分钟

一、项目介绍

随着智能物联网技术的不断发展,人们的生活方式和消费习惯也正在发生改变。如今越来越多的人习惯于在线购物、自助购物等新型消费模式,因此智能零售自助柜应运而生。


本项目设计开发一款基于 STM32 主控芯片的智能零售自助柜,通过重力传感器监测货柜内商品重量变化,并通过 WiFi 通信模块与手机端实现交互。用户可以通过输入账号密码,柜门自动打开,用户自取商品后关闭柜门,柜门锁定,系统根据重量变化判断用户拿取的商品并从账户自动扣费。同时,用户也可以通过手机端查看消费流水、商品库存,并进行补货和充值等操作。


智能零售自助柜的应用场景非常广泛,可以应用于商场、超市、酒店、机场、车站等各类场景。通过自助购物,可以提高消费者的消费体验和购物效率,同时也降低了商家的人力成本和物流成本。


二、设计思路

【1】功能细节总结

(1)ESP8266 配置成 AP+TCP 服务器模式与手机 APP 连接。


(2)手机 APP 可以完成用户的注册,充值功能,然后通过连接货柜将数据同步到货柜的存储芯片上(W25Q64-FLASH 保存数据)。


(3)手机 APP 连接货柜之后,可以拉取数据显示,了解货柜现在的物品哪些已经售卖出去,哪些还没有售卖。,每个物品是放在一个货柜格子里,透明玻璃可以查看到物品。

【2】硬件选型

  1. 主控芯片:STM32F103RCT6 是一款主流的 32 位 ARM Cortex-M 系列微控制器,具有高性能、低功耗和易于开发等特点,因此被选择作为该系统的主控芯片。

  2. 重力传感器:HX711 重力传感器模块采用 24 位高精度芯片,能够精确测量重量,适用于该系统中货柜内商品的重量监测。

  3. SG90 舵机:该系统需要控制柜门的打开和关闭,因此使用舵机来实现柜门控制。

  4. 矩阵键盘:用户需要输入账号密码进行登录,因此使用矩阵键盘作为输入设备。

  5. 显示屏:OLED 显示屏具有低功耗、高对比度、快速响应等特点,适用于该系统中的桌面显示界面。

  6. WiFi 模块:ESP8266-WIFI 模块是一款成本低、体积小、性能稳定的 WiFi 通信模块,适合在该系统中与手机 APP 进行无线通信。

【2】程序设计思路

  1. 初始化系统,包括各个外设的初始化,如 WiFi 模块、重力传感器 HX711 模块、矩阵键盘等;

  2. 用户输入账号密码,判断是否为有效用户;

  3. 根据重力传感器读取货柜内商品重量,判断用户拿取的商品并从账户自动扣费;

  4. 控制柜门打开和关闭,同时显示屏上显示相关提示信息;

  5. 同步数据到手机 APP。

【3】设备操作流程

  1. 用户输入账号密码,系统进行验证,判断是否为有效用户;

  2. 如果验证通过,屏幕上显示“登录成功”,并显示货柜内商品列表和对应价格;

  3. 用户选择需要购买的商品,系统根据重力传感器读取货柜内商品重量,并判断用户拿取的商品并从账户自动扣费;

  4. 系统控制电磁锁或舵机将柜门打开,用户自取商品后关闭柜门;

  5. 重力传感器监测到货柜内重量变化,系统自动判断用户拿取的商品种类和数量,并在显示屏上显示相关提示信息,如显示扣费金额;

  6. 控制柜门锁定,确保商品安全,同时在显示屏上显示“门已锁定”等相关提示信息;

  7. 同步扣费记录和商品库存信息到手机 APP,以便用户查看消费流水和进行补货等操作。

  8. 如需要充值,用户可以在手机 APP 上进行余额充值操作。

三、代码实现

【1】OLED 显示屏驱动代码

下面是 OLED 显示屏的测试代码。使用的 SPI 接口的 OLED 显示屏。


#include "stm32f10x.h"#include "OLED.h"   // OLED驱动库头文件
void OLED_ShowString(uint8_t x, uint8_t y, uint8_t *str){ uint8_t i = 0; while(str[i] != '\0'){ if(x > OLED_WIDTH - 8){ // 满行自动换行 x = 0; y++; } OLED_ShowChar(x, y, str[i]); // 显示单个字符 x += 8; // 水平方向上的下一个字符 i++; }}
void OLED_SPI_SendByte(uint8_t data){ while(SPI_I2S_GetFlagStatus(OLED_SPI_PORT, SPI_I2S_FLAG_TXE) == RESET); // 等待发送缓冲区空 SPI_I2S_SendData(OLED_SPI_PORT, data); // 通过SPI发送数据}
void OLED_WriteCmd(uint8_t cmd){ OLED_DC_Clr(); // 将DC置为0,表示发送命令 OLED_CS_Clr(); // 将CS置为0,选中OLED芯片 OLED_SPI_SendByte(cmd); // 发送命令 OLED_CS_Set(); // 将CS置为1,取消OLED芯片选中}
void OLED_WriteData(uint8_t data){ OLED_DC_Set(); // 将DC置为1,表示发送数据 OLED_CS_Clr(); // 将CS置为0,选中OLED芯片 OLED_SPI_SendByte(data); // 发送数据 OLED_CS_Set(); // 将CS置为1,取消OLED芯片选中}

int main(void){ uint32_t i;
// 初始化SPI接口 SPI_InitTypeDef SPI_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); // 打开SPI1时钟 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // 设置SPI工作模式 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 数据位宽8bit SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; // 时钟极性为低电平 SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; // 时钟第一个边沿采样 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // 软件控制CS信号 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; // 预分频系数为256 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; // MSB先行 SPI_InitStructure.SPI_CRCPolynomial = 7; // CRC校验值 SPI_Init(SPI1, &SPI_InitStructure); SPI_Cmd(SPI1, ENABLE); // 使能SPI1
// 初始化OLED显示屏 OLED_Init(); // OLED初始化
// 显示数字 char str[] = "1234567890"; OLED_ShowString(0, 0, (uint8_t *)str); // 在(0,0)坐标处显示字符串
while(1){ for(i = 0; i < 10000000; i++); // 延时等待 }}
复制代码


OLED_WriteCmd 函数用于向 OLED 显示屏发送命令,而 OLED_WriteData 函数用于向 OLED 显示屏发送数据。OLED_SPI_SendByte 函数是底层 SPI 数据传输的关键代码部分。

【2】HX711 称重传感器代码

#include "stm32f10x.h"#include <stdio.h>#include "usart.h"
#define HX711_SCK_GPIO_RCC RCC_APB2Periph_GPIOB#define HX711_SCK_GPIO_PORT GPIOB#define HX711_SCK_GPIO_PIN GPIO_Pin_13
#define HX711_DOUT_GPIO_RCC RCC_APB2Periph_GPIOB#define HX711_DOUT_GPIO_PORT GPIOB#define HX711_DOUT_GPIO_PIN GPIO_Pin_15
uint32_t read_HX711_data(void);void init_GPIO(void);void init_USART1(void);void USART1_SendChar(char ch);
int main(void){ uint32_t hx711_value;
init_GPIO(); init_USART1();
while(1){ hx711_value = read_HX711_data(); // 读取 HX711 传感器数据 printf("The weight is: %d g\r\n", hx711_value); // 通过串口打印 HX711 传感器读取的数据 }}
// 从 HX711 传感器读取数据uint32_t read_HX711_data(void){ uint32_t weight = 0; uint8_t i;
GPIO_SetBits(HX711_SCK_GPIO_PORT, HX711_SCK_GPIO_PIN); // 拉高 SCK 管脚 GPIO_ResetBits(HX711_DOUT_GPIO_PORT, HX711_DOUT_GPIO_PIN); // 拉低 DOUT 管脚 for(i = 0; i < 24; i++){ GPIO_ResetBits(HX711_SCK_GPIO_PORT, HX711_SCK_GPIO_PIN); // 拉低 SCK 管脚,使得 HX711 将数据推入 DOUT 管脚 weight <<= 1; // 左移一位,为下一次读取做准备 if(GPIO_ReadInputDataBit(HX711_DOUT_GPIO_PORT, HX711_DOUT_GPIO_PIN)) weight++; // 如果 DOUT 管脚为高电平,那么就在 weight 中保存 "1" GPIO_SetBits(HX711_SCK_GPIO_PORT, HX711_SCK_GPIO_PIN); // 拉高 SCK 管脚,为下一次读取做准备 } GPIO_ResetBits(HX711_SCK_GPIO_PORT, HX711_SCK_GPIO_PIN); // 最后时刻需要拉低 SCK 管脚一次
weight = (weight ^ 0x800000) - 0x800000; // 将读出的24位二进制重量值转化为带符号数,这里我们只考虑单通道读取的情况(如有多个物理传感器需进行一定的计算处理)
return weight;}
// 初始化 GPIO 管脚void init_GPIO(void){ GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(HX711_SCK_GPIO_RCC | HX711_DOUT_GPIO_RCC, ENABLE); // 打开 SCK 和 DOUT 管脚时钟
GPIO_InitStructure.GPIO_Pin = HX711_SCK_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(HX711_SCK_GPIO_PORT, &GPIO_InitStructure); // 初始化 SCK 管脚
GPIO_InitStructure.GPIO_Pin = HX711_DOUT_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(HX711_DOUT_GPIO_PORT, &GPIO_InitStructure); // 初始化 DOUT 管脚}
// 初始化 USART1void init_USART1(void){ USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 打开 USART1 时钟
USART_InitStructure.USART_BaudRate = 115200; // 波特率 115200 USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 数据位 8 位 USART_InitStructure.USART_StopBits = USART_StopBits_1; // 停止位 1 位 USART_InitStructure.USART_Parity = USART_Parity_No; // 无奇偶校验 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件流控制 USART_InitStructure.USART_Mode = USART_Mode_Tx; // 只启用串口发送
USART_Init(USART1, &USART_InitStructure); // 初始化 USART1 USART_Cmd(USART1, ENABLE); // 使能 USART1}
// 通过 USART1 发送字符void USART1_SendChar(char ch){ while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); // 等待发送缓冲区为空 USART_SendData(USART1, (uint8_t)ch); // 发送数据}
复制代码


代码执行流程说明:


(1)通过 init_GPIO() 函数初始化 SCK 和 DOUT 两个 GPIO 管脚,并通过 init_USART1() 函数初始化 USART1 串口。其中,初始化 SCK 管脚为输出模式,DOUT 管脚为输入模式,USART1 算是串口助手,用于将数据打印输出。


(2)read_HX711_data() 函数用于向 HX711 传感器发出读取数据的指令,并将返回的数据进行处理(将 24 位二进制重量值转化为带符号数)后返回。


(3)在主函数的 while 循环中,不断调用 read_HX711_data() 函数读取 HX711 传感器的数据,并通过串口打印出来。

【3】SG90 舵机控制代码

下面是 SG90 舵机的控制代码,可以按照指定的角度旋转。


#include "stm32f10x.h"#include "delay.h"
#define GPIO_PORT GPIOA#define GPIO_PIN GPIO_Pin_1#define RCC_APB2Periph_GPIO RCC_APB2Periph_GPIOA#define PWM_FREQ 50
void servoInit(void){ GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIO_PORT, &GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period = 9999; //计数器最大值 TIM_TimeBaseStructure.TIM_Prescaler = (72 * 2) - 1; //时钟分频,72是系统时钟频率,2是倍频 TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 0; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM2, &TIM_OCInitStructure);
TIM_Cmd(TIM2, ENABLE);}
void servoSetAngle(uint8_t angle){ uint16_t pwmVal = (uint16_t)(500 + angle * 10.0 / 9.0);
TIM_SetCompare1(TIM2, pwmVal); delay_ms(100);}
int main(void){ SystemInit(); delay_init();
servoInit();
while(1) { servoSetAngle(0); delay_ms(1000);
servoSetAngle(90); delay_ms(1000);
servoSetAngle(180); delay_ms(1000); }}
复制代码


发布于: 刚刚阅读数: 3
用户头像

DS小龙哥

关注

微信公众号:DS小龙哥嵌入式技术资讯 2022-01-06 加入

所有项目文章对应的工程源码,都可以在我的微信公众号:《DS小龙哥嵌入式技术资讯》 里下载。

评论

发布
暂无评论
基于STM32的重力感应售货机系统设计_7月月更_DS小龙哥_InfoQ写作社区