• 方案介紹
    • 一、項目介紹
    • 二、設計思路
    • 三、代碼實現
  • 附件下載
  • 相關推薦
申請入駐 產業(yè)圖譜

基于STM32的重力感應售貨機系統(tǒng)設計

03/21 09:00
1675
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

更多詳細資料請聯系.docx

共1個文件

一、項目介紹

隨著智能物聯網技術的不斷發(fā)展,人們的生活方式和消費習慣也正在發(fā)生改變。如今越來越多的人習慣于在線購物、自助購物等新型消費模式,因此智能零售自助柜應運而生。

本項目設計開發(fā)一款基于STM32主控芯片的智能零售自助柜,通過重力傳感器監(jiān)測貨柜內商品重量變化,并通過WiFi通信模塊與手機端實現交互。用戶可以通過輸入賬號密碼,柜門自動打開,用戶自取商品后關閉柜門,柜門鎖定,系統(tǒng)根據重量變化判斷用戶拿取的商品并從賬戶自動扣費。同時,用戶也可以通過手機端查看消費流水、商品庫存,并進行補貨和充值等操作。

智能零售自助柜的應用場景非常廣泛,可以應用于商場、超市、酒店、機場、車站等各類場景。通過自助購物,可以提高消費者的消費體驗和購物效率,同時也降低了商家的人力成本和物流成本。

二、設計思路

【1】功能細節(jié)總結

(1)ESP8266配置成AP+TCP服務器模式與手機APP連接。

(2)手機APP可以完成用戶的注冊,充值功能,然后通過連接貨柜將數據同步到貨柜的存儲芯片上(W25Q64-FLASH保存數據)。

(3)手機APP連接貨柜之后,可以拉取數據顯示,了解貨柜現在的物品哪些已經售賣出去,哪些還沒有售賣。,每個物品是放在一個貨柜格子里,透明玻璃可以查看到物品。

【2】硬件選型

  1. 主控芯片:STM32F103RCT6是一款主流的32位ARM Cortex-M系列微控制器,具有高性能、低功耗和易于開發(fā)等特點,因此被選擇作為該系統(tǒng)的主控芯片。
  2. 重力傳感器:HX711重力傳感器模塊采用24位高精度芯片,能夠精確測量重量,適用于該系統(tǒng)中貨柜內商品的重量監(jiān)測。
  3. SG90舵機:該系統(tǒng)需要控制柜門的打開和關閉,因此使用舵機來實現柜門控制。
  4. 矩陣鍵盤:用戶需要輸入賬號密碼進行登錄,因此使用矩陣鍵盤作為輸入設備。
  5. 顯示屏OLED顯示屏具有低功耗、高對比度、快速響應等特點,適用于該系統(tǒng)中的桌面顯示界面。
  6. WiFi模塊:ESP8266-WIFI模塊是一款成本低、體積小、性能穩(wěn)定的WiFi通信模塊,適合在該系統(tǒng)中與手機APP進行無線通信

【2】程序設計思路

  1. 初始化系統(tǒng),包括各個外設的初始化,如WiFi模塊、重力傳感器HX711模塊、矩陣鍵盤等;
  2. 用戶輸入賬號密碼,判斷是否為有效用戶;
  3. 根據重力傳感器讀取貨柜內商品重量,判斷用戶拿取的商品并從賬戶自動扣費;
  4. 控制柜門打開和關閉,同時顯示屏上顯示相關提示信息;
  5. 同步數據到手機APP。

【3】設備操作流程

  1. 用戶輸入賬號密碼,系統(tǒng)進行驗證,判斷是否為有效用戶;
  2. 如果驗證通過,屏幕上顯示“登錄成功”,并顯示貨柜內商品列表和對應價格;
  3. 用戶選擇需要購買的商品,系統(tǒng)根據重力傳感器讀取貨柜內商品重量,并判斷用戶拿取的商品并從賬戶自動扣費;
  4. 系統(tǒng)控制電磁鎖或舵機將柜門打開,用戶自取商品后關閉柜門;
  5. 重力傳感器監(jiān)測到貨柜內重量變化,系統(tǒng)自動判斷用戶拿取的商品種類和數量,并在顯示屏上顯示相關提示信息,如顯示扣費金額;
  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] != '?'){
        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);  // 等待發(fā)送緩沖區(qū)空
    SPI_I2S_SendData(OLED_SPI_PORT, data);  // 通過SPI發(fā)送數據
}

void OLED_WriteCmd(uint8_t cmd)
{
    OLED_DC_Clr();  // 將DC置為0,表示發(fā)送命令
    OLED_CS_Clr();  // 將CS置為0,選中OLED芯片
    OLED_SPI_SendByte(cmd);  // 發(fā)送命令
    OLED_CS_Set();  // 將CS置為1,取消OLED芯片選中
}

void OLED_WriteData(uint8_t data)
{
    OLED_DC_Set();  // 將DC置為1,表示發(fā)送數據
    OLED_CS_Clr();  // 將CS置為0,選中OLED芯片
    OLED_SPI_SendByte(data);  // 發(fā)送數據
    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 顯示屏發(fā)送命令,而 OLED_WriteData 函數用于向 OLED 顯示屏發(fā)送數據。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 grn", 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 管腳
}

// 初始化 USART1
void 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;  // 只啟用串口發(fā)送

    USART_Init(USART1, &USART_InitStructure);  // 初始化 USART1
    USART_Cmd(USART1, ENABLE);  // 使能 USART1
}

// 通過 USART1 發(fā)送字符
void USART1_SendChar(char ch)
{
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);  // 等待發(fā)送緩沖區(qū)為空
    USART_SendData(USART1, (uint8_t)ch);  // 發(fā)送數據
}

代碼執(zhí)行流程說明:

(1)通過 init_GPIO() 函數初始化 SCK 和 DOUT 兩個 GPIO 管腳,并通過 init_USART1() 函數初始化 USART1 串口。其中,初始化 SCK 管腳為輸出模式,DOUT 管腳為輸入模式,USART1 算是串口助手,用于將數據打印輸出。

(2)read_HX711_data() 函數用于向 HX711 傳感器發(fā)出讀取數據的指令,并將返回的數據進行處理(將24位二進制重量值轉化為帶符號數)后返回。

(3)在主函數的 while 循環(huán)中,不斷調用 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是系統(tǒng)時鐘頻率,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);
    }
}
  • 更多詳細資料請聯系.docx
    下載
意法半導體

意法半導體

意法半導體(ST)集團于1987年6月成立,是由意大利的SGS微電子公司和法國Thomson半導體公司合并而成。1998年5月,SGS-THOMSON Microelectronics將公司名稱改為意法半導體有限公司。意法半導體是世界最大的半導體公司之一,公司銷售收入在半導體工業(yè)五大高速增長市場之間分布均衡(五大市場占2007年銷售收入的百分比):通信(35%),消費(17%),計算機(16%),汽車(16%),工業(yè)(16%)。 據最新的工業(yè)統(tǒng)計數據,意法半導體是全球第五大半導體廠商,在很多市場居世界領先水平。例如,意法半導體是世界第一大專用模擬芯片和電源轉換芯片制造商,世界第一大工業(yè)半導體和機頂盒芯片供應商,而且在分立器件、手機相機模塊和車用集成電路領域居世界前列.

意法半導體(ST)集團于1987年6月成立,是由意大利的SGS微電子公司和法國Thomson半導體公司合并而成。1998年5月,SGS-THOMSON Microelectronics將公司名稱改為意法半導體有限公司。意法半導體是世界最大的半導體公司之一,公司銷售收入在半導體工業(yè)五大高速增長市場之間分布均衡(五大市場占2007年銷售收入的百分比):通信(35%),消費(17%),計算機(16%),汽車(16%),工業(yè)(16%)。 據最新的工業(yè)統(tǒng)計數據,意法半導體是全球第五大半導體廠商,在很多市場居世界領先水平。例如,意法半導體是世界第一大專用模擬芯片和電源轉換芯片制造商,世界第一大工業(yè)半導體和機頂盒芯片供應商,而且在分立器件、手機相機模塊和車用集成電路領域居世界前列.收起

查看更多

相關推薦