基于GD32实现CS5530电子秤称重方案及驱动

1. 概述

  CS5530是24 位带有超低噪声放大器的模拟数字转换器,配合压力传感器可以实现高精度的称重(电子秤)方案,本博文将从称重传感器实现原理到如何次采用CS5530实现电子秤称重方案进行详细描述。

2. 电子秤实现原理

  实现电子秤的传感器有很多,此处采用电阻式应变片传感器实现信号的采集
在这里插入图片描述

原理如下图所示:在这里插入图片描述

  将应变片粘贴到受力的力敏型弹性元件上, 当弹性元件受力产生变形时,应变片产生相应的应变, 转化成电阻变化,将应变片接成如
  下图所示的电桥,力引起的电阻变化将转换为测量电路的电压变化,通过测量输出电压的数值, 再通过换算即可得到所测量物体的重量
在这里插入图片描述
Uad作为输入电桥输入电源,Ubc作为电桥输出
当空载时,电桥输出应为0v,电桥平衡,此时
在这里插入图片描述
  因此:对角电阻阻值乘积相等,这是平衡电桥的基本条件
  根据传感器的不同,电桥上有的只有一个电阻发生改变,有的有两个,有的有四个;其中,有四个的精度最高;
  电桥的四个臂上接工作应变片,都参与机械变形,同处一个温度场,温度影响相互抵消,电压输出灵敏度高
  根据电阻分压,可以计算出以下关系:
在这里插入图片描述
  传感器的输出Ubc之间的输出范围与传感器有关,我选用的传感器铭牌上有标注以下信息:

类型 数据
型号 AT8502
量程 5kg
灵敏度 2.0mV/V

  注意重量务必不能超过量程,不要超载。如果在外力撤除后不能恢复原形状,发生塑性变形,则传感器就损坏了
  其中灵敏度的含义如下:传感器在一定的供电条件下Uin(比如5VDC),载荷达到额定满量程(比如10kg)时的输出变化量Uout(比如10mV)与供电电压的比值:S=Uout/Uin=10mV/5V=2mV/V
  针对我所选择的这个传感器,在满载5Kg的情况下,如果电桥电源为1V,则输出为1mV;如果电桥电源为5V,则输出为10mV;如果将电压恒定为5V,改变负载,则当负载为1Kg时,输出电压为2mV,即当电压为5V时此传感器精度为2mV/Kg
  将输出连接到ad转换芯片的输入,通过检测电桥的输出即可计算出对应的电压了,至于为什么需要接ad转换芯片呢,通过以上计算我们可以知道电桥的输出其实很小,为了保证精度,因此需要选择高精度的采集芯片,因此选用了ad芯片

2. CS5530使用配置

  cs5530是一个24 位带有超低噪声放大器的模拟数字转换器( ADC),常用来做电桥的输入转化,中文手册下载地址:https://download.csdn.net/download/qq_43332314/85099254
  本博文采用gd32的硬件spi来完成与cs5530的数据读取,注意cs5530芯片支持的SPI频率小于2MHz!
  配置步骤如下:

  1. 同步并配置芯片进入命令模式
      在访问配置寄存器之前,用户一定要确认使用串口初始化序列操作使得串口同步。
      通过发送至少15个SYNC1命令(十六进制的oxFF)再发一个SYNC0命令(十六进制的oxFE)
  2. 系统初始化
      CS5530没有提供上电复位功能,如果要进行初始化,用户一定要通过配置寄存器进行软复位。
      将配置寄存器的RS位置1,等待8个时钟周期之后,往RS写0清除复位使能,之后读RV位判断是否复位完成,注意写配置寄存器之前需要先发一个自己的写配置寄存器命令
    在这里插入图片描述
  3. 校准和设置增益
      这里根据个人需要设置吧,本博文,校准暂时未设置,增益设置为1倍,也就是采用默认值
  4. 根据电路设计配置VRS(参考电压选择)和U/B(单极性/双极性)
    在这里插入图片描述在这里插入图片描述

  本博文电路由于VREF=VA+所以VRS = 0(默认值),电源是单极性,所以U/B是0(也是默认值)
5. 设置其他特定参数
其他的基本上没有了,建议最好把手册下载下来,把手册好好看下,根据自己的电路配置下
6. 发送转换命令执行转换
  发送单次转换命令,转化完成之后,MOSI引脚会拉低,我没有检测改引脚,直接延时一定时间,500ms,等待转化完成之后去读,读的时候注意需要先发送一个字节0x00,取消SDO flag,之后四个字节才是数据,数据的解析需要结合数据寄存器说明,最后一个字节是溢出标志,实际只有三个字节,也就是24位精度
7. 数值换算
  留意手册的2.1.1章节!转换器可数字量化的满量程输入信号与VREF+和VREF-之间的参考电压成函数关系。转换器满量程范围为:((VREF+) – (VREF-))/(64Y),这里的64为放大器的增益,当VRS=0时Y=2,当VRS=1时Y=1,VRS是参考电压的选择标志位,它一定要根据转换器VREF+和VREF-之间的不同参考电压进行设置。
  由于VRS=0,Vref+ = 5V,Vref- = 0V,因此转换器的量程范围是(5-0)/(64*2) = 39.0625mV 约等于 40mV;所以满量程为40mV
  当我放置一个2Kg的砝码在传感器上时,根据第一章节的传感器数据和原理计算可得到现在H桥的输出应该为:
  2Kg x 2mV/Kg = 4mV
  4mV / 40mV = 10%
  进行一次转换读取到的转换数据寄存器的值为0x1a161300,溢出字节为0x00,因为没有发生溢出,将数据字节除以满量程24位精度为
  0x1a1613 / 1 / 0xffffff = 10%("1"指增益倍数)
  芯片测量结果与计算结果一致,至于如何将0x1a161300转化为电压值,相信不用我说了吧!

3. 驱动代码如下:

基于gd32的CS5530驱动程序

  1. spi配置:
#include "./spi/bsp_spi.h"
#include <stdio.h>

static void spi_gpio_config(void)
{
    
    
    rcu_periph_clock_enable(RCU_GPIOB);

    /* SPI1_SCK(PB13), SPI1_MISO(PB14) and SPI1_MOSI(PB15) GPIO pin configuration */
    gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);
    /* SPI1_CS(PB12) GPIO pin configuration */
    gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);
    
    SPI1_CS_HIGH();
}

static void spi_config(void)
{
    
    
    spi_parameter_struct spi_init_struct;
    rcu_periph_clock_enable(RCU_SPI1);
    
    /* SPI1 parameter config */
    spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;
    spi_init_struct.device_mode          = SPI_MASTER;
    spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;
    spi_init_struct.nss                  = SPI_NSS_SOFT;
    spi_init_struct.prescale             = SPI_PSC_128;
    spi_init_struct.endian               = SPI_ENDIAN_MSB;
    spi_init(SPI1, &spi_init_struct);
    
    spi_enable(SPI1);
}

void bsp_spi_init(void)
{
    
    
    spi_gpio_config();
    spi_config();
}

uint8_t spi1_flash_read_byte(void)
{
    
    
    return(spi1_flash_send_byte(0x00));
}

uint8_t spi1_flash_send_byte(uint8_t data)
{
    
    
    /* loop while data register in not emplty */
    while (RESET == spi_i2s_flag_get(SPI1, SPI_FLAG_TBE));

    /* send byte through the SPI0 peripheral */
    spi_i2s_data_transmit(SPI1, data);

    /* wait to receive a byte */
    while(RESET == spi_i2s_flag_get(SPI1, SPI_FLAG_RBNE));

    /* return the byte read from the SPI bus */
    return(spi_i2s_data_receive(SPI1));
}

int8_t spi1_flash_send_buf(uint8_t *txdata, uint8_t *rxdata, uint32_t len)
{
    
    
    if (txdata == NULL || rxdata == NULL)
        return -1;
    
    for (int i = 0; i < len; i ++) {
    
    
        /* loop while data register in not emplty */
        while (RESET == spi_i2s_flag_get(SPI1, SPI_FLAG_TBE));

        /* send byte through the SPI0 peripheral */
        spi_i2s_data_transmit(SPI1, txdata[i]);

        /* wait to receive a byte */
        while(RESET == spi_i2s_flag_get(SPI1, SPI_FLAG_RBNE));

        /* return the byte read from the SPI bus */
        rxdata[i] = spi_i2s_data_receive(SPI1);
    }
    return 0;
}

int8_t spi1_flash_read_buf(uint8_t *data, uint32_t len)
{
    
    
    if (data == NULL)
        return -1;
    
    for (int i = 0; i < len; i ++) {
    
    
        data[i] = spi1_flash_read_byte();
    }
    return 0;
}

spi头文件

#ifndef __BSP_SPI_H__
#define __BSP_SPI_H__

#include <gd32f30x.h>

#define SPI1_CS_HIGH()  {
      
      gpio_bit_write(GPIOB, GPIO_PIN_12, SET);}
#define SPI1_CS_LOW()   {
      
      gpio_bit_write(GPIOB, GPIO_PIN_12, RESET);}

void bsp_spi_init(void);

uint8_t spi1_flash_read_byte(void);
uint8_t spi1_flash_send_byte(uint8_t data);
int8_t spi1_flash_send_buf(uint8_t *txdata, uint8_t *rxdata, uint32_t len);
int8_t spi1_flash_read_buf(uint8_t *data, uint32_t len);

#endif /* __BSP_SPI_H__ */

  1. cs5530驱动:
#include "./cs5530/cs5530.h"
#include "./spi/bsp_spi.h"
#include <rtthread.h>
#include <string.h>

#define WO_OFFSET_REG_CMD       (0x00|0x01)
#define RO_OFFSET_REG_CMD       (0x08|0x01)
#define WO_GAIN_REG_CMD         (0x00|0x02)
#define RO_GAIN_REG_CMD         (0x08|0x02)
#define WO_CONFIG_REG_CMD       (0x00|0x03)
#define RO_CONFIG_REG_CMD       (0x08|0x03)
#define SINGLE_CONVER_CMD       (0x80|0x00) 
#define CONTINU_CONVER_CMD      (0x80|0x40)
#define SYS_OFFSET_CALI_CMD     (0x85)
#define SYS_GAIN_CALI_CMD       (0x86)
#define SYNC1_CMD               (0xff)
#define SYNC0_CMD               (0xfe)
#define NULL_CMD                (0x00)

/**
 * @brief cs5330初始化
 * @return -1:失败  0:成功
 */
int8_t cs5530_init(void)
{
    
    
    uint32_t txdata = 0;
    uint8_t tx_buf[5] = {
    
    0};
    uint8_t rx_buf[5] = {
    
    0};
    
    /* 复位串口进入命令模式 */
    SPI1_CS_LOW();
    for (int i = 0; i < 16; i++) {
    
    
        spi1_flash_send_byte(SYNC1_CMD);
    }
    spi1_flash_send_byte(SYNC0_CMD);
    SPI1_CS_HIGH();
    rt_thread_mdelay(5);
    
    /* 系统复位 */
    memset(tx_buf, 0, sizeof(tx_buf));
    txdata = 1 << 29;
    tx_buf[0] = WO_CONFIG_REG_CMD;
    tx_buf[1] = txdata >> 24; tx_buf[2] = txdata >> 16;
    tx_buf[3] = txdata >> 8;  tx_buf[4] = txdata;
    SPI1_CS_LOW();
    spi1_flash_send_buf(tx_buf, rx_buf, 5);
    SPI1_CS_HIGH();
    rt_thread_mdelay(5);
    
    /* 清除系统复位标志 */
    memset(tx_buf, 0, sizeof(tx_buf));
    txdata = 0;
    tx_buf[0] = WO_CONFIG_REG_CMD;
    tx_buf[1] = txdata >> 24; tx_buf[2] = txdata >> 16;
    tx_buf[3] = txdata >> 8;  tx_buf[4] = txdata;
    SPI1_CS_LOW();
    spi1_flash_send_buf(tx_buf, rx_buf, 5);
    SPI1_CS_HIGH();
    rt_thread_mdelay(5);
    
    /* 读取系统复位完成标志 */
    memset(tx_buf, 0, sizeof(tx_buf));
    txdata = 0;
    tx_buf[0] = RO_CONFIG_REG_CMD;
    SPI1_CS_LOW();
    spi1_flash_send_buf(tx_buf, rx_buf, 5);
    SPI1_CS_HIGH();
    rt_kprintf("1_config:%02x%02x%02x%02x\n", rx_buf[0],
                                            rx_buf[1],
                                            rx_buf[2],
                                            rx_buf[3],
                                            rx_buf[4]);
    rt_thread_mdelay(5);
    if (rx_buf[1] != 0x10)
        return -1;
    
    /* 设置为单极性模式 */
    memset(tx_buf, 0, sizeof(tx_buf));
    txdata = 1 << 10;
    tx_buf[0] = WO_CONFIG_REG_CMD;
    tx_buf[1] = txdata >> 24; tx_buf[2] = txdata >> 16;
    tx_buf[3] = txdata >> 8;  tx_buf[4] = txdata;
    SPI1_CS_LOW();
    spi1_flash_send_buf(tx_buf, rx_buf, 5);
    SPI1_CS_HIGH();
    rt_thread_mdelay(5);
    
    /* 读取配置寄存器值 */
    memset(tx_buf, 0, sizeof(tx_buf));
    txdata = 0;
    tx_buf[0] = RO_CONFIG_REG_CMD;
    SPI1_CS_LOW();
    spi1_flash_send_buf(tx_buf, rx_buf, 5);
    SPI1_CS_HIGH();
    rt_kprintf("2_config:%02x%02x%02x%02x\n", rx_buf[0],
                                            rx_buf[1],
                                            rx_buf[2],
                                            rx_buf[3],
                                            rx_buf[4]);
    rt_thread_mdelay(5);
    if (rx_buf[3] != 0x04)
        return -1;
    return 0;
}

/**
 * @brief cs5330执行单次转换
 */
void cs5330_perform_single_conversion(void)
{
    
    
    SPI1_CS_LOW();
    spi1_flash_send_byte(SINGLE_CONVER_CMD);
    SPI1_CS_HIGH();
}

/**
 * @brief 获取cs5330转化结果
 * @return 1:完成 0:失败
 */
uint8_t get_cs5330_conver_state(void)
{
    
    
    if (gpio_input_bit_get(GPIOB, GPIO_PIN_14) == 0)
        return 1;
    else
        return 0;
}

/**
 * @brief cs5330读取转化结果
 */
uint32_t cs5330_read_conver_reslut(void)
{
    
    
    uint32_t ret = 0;
    uint8_t tx_buf[5] = {
    
    0};
    uint8_t rx_buf[5] = {
    
    0};
    
    memset(tx_buf, 0, sizeof(tx_buf));
    tx_buf[0] = NULL_CMD;
    SPI1_CS_LOW();
    spi1_flash_send_buf(tx_buf, rx_buf, 5);
    SPI1_CS_HIGH();
    rt_thread_mdelay(5);
    rt_kprintf("%02x%02x%02x%02x\n",rx_buf[1],rx_buf[2],rx_buf[3],rx_buf[4]);
    ret = (uint32_t)((rx_buf[1] << 16) | (rx_buf[2] << 8) | rx_buf[3]);
    if ((rx_buf[4] & 0x04) == 0) {
    
    
        rt_kprintf("%d\n", ret * 100 / 0xffffff);
    }
    
    return ret;
}

cs5530头文件

#ifndef __CS5530_H__
#define __CS5530_H__

#include <stdint.h>

int8_t cs5530_init(void);
void cs5330_perform_single_conversion(void);
uint32_t cs5330_read_conver_reslut(void);
uint8_t get_cs5330_conver_state(void);
#endif /* __CS5530_H__ */
  1. 主函数
void main()
{
    
    
    /* 等待模块稳定 */
    rt_thread_mdelay(500);
    while (cs5530_init() != 0) {
    
    
        rt_thread_mdelay(500);
        rt_kprintf("cs5330 init fail!");
    }
    
    while (1) {
    
    
        cs5330_perform_single_conversion();
        rt_thread_mdelay(500);
        while( cs5330_read_conver_reslut() == 0)
            rt_thread_mdelay(500);
    }
}

注意:以上程序基于rtthread编写,因此程序中有部分函数需要根据各自的平台切换,如rt_thread_mdelayrt_kprintf

  支持开源分享,记得点赞支持下哦 ^ _ ^

猜你喜欢

转载自blog.csdn.net/qq_43332314/article/details/124104349
今日推荐