499|11

3526

帖子

0

资源

纯净的硅(高级)

【短时天气预报系统】outdoor装置基本设计与实现 [复制链接]

 
本帖最后由 ljj3166 于 2021-6-6 17:50 编辑

周末,撸一贴

来看看室外端装置的设计

基本硬件框架,上灵魂画作

 

1.jpg

 

撸了一板,大概长这个样子

 

2.jpg

 

其中bq25570的反馈网络设计

 

3.jpg

 

大概意思是

bq25570为电容充电,充电截止电压为3.8v

充电阈值区间为2.6v-3.6v

同时为后级电路提供工作电源,电压为1.8v

投板,焊接

吐槽一下

BME280这颗传感器,采集气压、温度、湿度

印象中10块钱左右的价格

现在涨到60左右,还基本上没货

样品直接被喊到100+

实在没办法,花50闷买了微雪的模块

拆机再焊上,真囧

 

4.jpg

 

 

5.jpg

 

接下来是outdoor装置中的“电池”

一颗70F的法拉电容

锂离子的法拉电容,标称等效电池容量大约在25mAh

自放电等参数相当牛鬼,价格也是十分感人,10多闷

 

 

6.jpg

 

唉,海内半导体产业任重道远啊

开机测试一下,模拟一个弱光情况下的太阳能板电压

大约0.7v的输入

 

7.jpg 8.jpg

 

基本上能够达到反馈电阻网络的预设值

电容充电电压大约在3.8v,对后级电路供电的电压大约在1.8v

这块表读数似乎稍微偏低一些

找了个艳阳天,中午时刻测试了一下充电电流

 

9.jpg

 

 

充电电流大约在20mA左右

基本上,一个晴朗的中午,就能在一个多小时

不到两个小时把25mAh的电容充满

接下来就是撸码了

Outdoor端的内容不多

主要是iic与BME280通信,采集气压温度湿度

然后通过广播包或者扫描响应包把数据发送出来

先看看IIC的通信

直接贴代码

ARM_DRIVER_I2C *i2c;
extern ARM_DRIVER_I2C Driver_I2C0;
void I2C_MasterCallback(uint32_t event);
void I2C_SlaveCallback(uint32_t event);
/* ----------------------------------------------------------------------------
 * Function      : void I2C_MasterCallback(uint32_t event)
 * ----------------------------------------------------------------------------
 * Description   : This function is a master callback. The parameter event
 *                 indicates one or more events that occurred during driver
 *                 operation.
 * Inputs        : event - I2C Events notification mask
 * Outputs       : None
 * Assumptions   : None
 * ------------------------------------------------------------------------- */
void I2C_MasterCallback(uint32_t event) {
        bool direction = (i2c->GetStatus().direction == 1U);

        /* Check if transfer is done */
        if (event & ARM_I2C_EVENT_TRANSFER_DONE) {
                /* Device is configured as transmitter */
                if (direction == I2C_STATUS_DIRECTION_TX) {
                        /* Delay time for switching modes of operation */
//                        Sys_Delay_ProgramROM((uint32_t) (0.5 * SystemCoreClock) / 1000);

//            /* MasterTransmit finished, start MasterReceive */
//            i2c->MasterReceive(RTE_I2C0_SLAVE_ADDR_DEFAULT, buff_rx, buff_size, false);
                }
                /* Check if device is configured as receiver */
                else if (direction == I2C_STATUS_DIRECTION_RX) {
                        /* Check the address nack event */
                        if (event & ARM_I2C_EVENT_ADDRESS_NACK) {
//                /* MasterTransmit finished, start MasterReceive */
//                i2c->MasterReceive(RTE_I2C0_SLAVE_ADDR_DEFAULT, buff_rx, buff_size, false);
                                return;
                        }

                        /* Delay time for switching modes of operation */
//                        Sys_Delay_ProgramROM((uint32_t) (0.5 * SystemCoreClock) / 1000);

                }
        }
        /* Check if transfer error occurred */
        else if ((event & ARM_I2C_EVENT_TRANSFER_INCOMPLETE)
                        || (event & ARM_I2C_EVENT_BUS_ERROR)) {
                /* Abort current transfer */
                i2c->Control(ARM_I2C_ABORT_TRANSFER, 0);

//        /* Go back to SlaveReceive default mode */
//        i2c->SlaveReceive(buff_rx, buff_size);
//
//        /* Toggle LED state 10 times for 100 milliseconds to indicate error */
//        ToggleLed(10, 100);
//        PRINTF("TRANSFER INCOMPLETE OR BUS ERROR\n");
        }
}

/* ----------------------------------------------------------------------------
 * Function      : void I2C_SlaveCallback(uint32_t event)
 * ----------------------------------------------------------------------------
 * Description   : This function is a slave callback. The parameter event
 *                 indicates one or more events that occurred during driver
 *                 operation.
 * Inputs        : event - I2C Events notification mask
 * Outputs       : None
 * Assumptions   : None
 * ------------------------------------------------------------------------- */
void I2C_SlaveCallback(uint32_t event) {
        bool direction = (i2c->GetStatus().direction == 1U);

        /* Check if transfer is done */
        if (event & ARM_I2C_EVENT_TRANSFER_DONE) {
                if (direction == I2C_STATUS_DIRECTION_TX) {
                }
                /* Check if device is configured as receiver */
                else if (direction == I2C_STATUS_DIRECTION_RX) {

                }
        }
        /* Check if transfer error occurred */
        else if ((event & ARM_I2C_EVENT_TRANSFER_INCOMPLETE)
                        || (event & ARM_I2C_EVENT_BUS_ERROR)) {
                /* Abort current transfer */
                i2c->Control(ARM_I2C_ABORT_TRANSFER, 0);

        }
}
/* ----------------------------------------------------------------------------
 * Function      : void I2C_CallBack(uint32_t event)
 * ----------------------------------------------------------------------------
 * Description   : This function is a callback registered by the function
 *                 Initialize. The parameter event indicates one or more events
 *                 that occurred during driver operation.
 * Inputs        : event - I2C Events notification mask
 * Outputs       : None
 * Assumptions   : None
 * ------------------------------------------------------------------------- */
void I2C_EventCallback(uint32_t event) {
        static volatile uint32_t I2C_Event;
        bool mode = (i2c->GetStatus().mode == 1U);
        I2C_Event |= event;

        /* Refresh the watchdog */
        Sys_Watchdog_Refresh();

        if (mode == I2C_STATUS_MODE_MASTER) {
                I2C_MasterCallback(event);
        } else if (mode == I2C_STATUS_MODE_SLAVE) {
                I2C_SlaveCallback(event);
        }
}

void iic_init(void) {

        /* Initialize i2c driver structure */
        i2c = &Driver_I2C0;
        /* Initialize i2c, register callback function */
        i2c->Initialize(I2C_EventCallback);

}

bool iic_write_byte(uint8_t device_address, uint8_t reg_address, uint8_t value) {
        uint8_t data[] = { reg_address, value };
        //return i2c->MasterTransmit(handle->device_address, data, 2, false) == 0;
        i2c->MasterTransmit(device_address, data, 2, false);
        while (i2c->GetStatus().busy)
                ;
        return true;
}

bool iic_write_buff(uint8_t device_address, uint8_t reg_address, uint8_t *data,
                size_t length) {
        if (length == 0 || length > 4) {
                return false;
        }

        uint8_t transfer[5];
        transfer[0] = reg_address;

        memcpy(&transfer[1], data, length);
        i2c->MasterTransmit(device_address, transfer, length + 1, false);
        while (i2c->GetStatus().busy)
                ;
        return true;
        //return i2c->MasterTransmit(handle->device_address, transfer, length + 1, false) == 0;
}

bool iic_read(uint8_t device_address, uint8_t reg_address, uint8_t *dest,
                size_t length) {
        uint8_t addr = reg_address;
        i2c->MasterTransmit(device_address, &addr, 1, false);
        while (i2c->GetStatus().busy)
                ;
        i2c->MasterReceive(device_address, dest, length, false);
        while (i2c->GetStatus().busy)
                ;
        return true;
}

 

从官方例程“I2c_cmsis_driver”扒拉下来的

要注意,官方例程在IIC回调中加了master和slaver的角色转换

并不是单纯的master示例,夹带了不少私货

要注意修改,对初次使用带来不少的问题

总体来说,过程还是比较清晰

但作为官方例程,还需要整理一下再发布

 

再一个是休眠

这个是比较重要的一环

阅读文档,可以知道,RSL10分为运行、待机和休眠这几种工作模式

其中待机大约在30uA左右

休眠又根据内部rom、ram和外设电源保持的情况

从几uA到几十nA

休眠的时候,会产生POR

除了ACS相关的配置外

其他所有的配置都会复位

唤醒后从PROM开始运行至main

所以干脆唤醒后直接复位,重新配置RSL10,完成一次采集和广播的操作

这样会带来几百ms到数s的启动时间(一般消耗在时钟源启动、休眠配置、蓝牙配置)

并带来额外的电流消耗

但是简单粗暴有效,楼主最喜欢了

就这么定了

采用RTC唤醒,保持PROM及片上FLASH电源

几处关键配置

 

配置外部RTC及唤醒周期

 

/* Configure RTC */
        ACS->RTC_CFG = RTC_CNT_START_VALUE
        ;
        *((volatile uint8_t*) &ACS->RTC_CTRL) = (uint8_t) (RTC_CLK_SRC |
        RTC_ALARM_ZERO);

        /* Reset and enable RTC */
        ACS_RTC_CTRL->RESET_ALIAS = RTC_RESET_BITBAND;
        ACS_RTC_CTRL->ENABLE_ALIAS = RTC_ENABLE_BITBAND;

 

CFGCTRL

 

/* Set delay and wake-up sources, use
         *    WAKEUP_DELAY_[ 1 | 2 | 4 | ... | 128],
         *    WAKEUP_DCDC_OVERLOAD_[ENABLE | DISABLE],
         *    WAKEUP_WAKEUP_PAD_[RISING | FALLING],
         *    WAKEUP_DIO*_[RISING | FALLING],
         *    WAKEUP_DIO*_[ENABLE | DISABLE] */
        sleep_mode_flash_env->wakeup_cfg = WAKEUP_DELAY_32 |
        WAKEUP_WAKEUP_PAD_RISING |
        WAKEUP_DIO3_DISABLE |
        WAKEUP_DIO2_DISABLE |
        WAKEUP_DIO1_DISABLE |
        WAKEUP_DIO0_DISABLE;

        /* Set wake-up control/status registers, use
         *    PADS_RETENTION_[ENABLE | DISABLE],
         *    BOOT_FLASH_APP_REBOOT_[ENABLE | DISABLE],
         *    BOOT_[CUSTOM | FLASH_XTAL_*],
         *    WAKEUP_DCDC_OVERLOAD_[SET | CLEAR],
         *    WAKEUP_PAD_EVENT_[SET | CLEAR],
         *    WAKEUP_RTC_ALARM_[SET | CLEAR],
         *    WAKEUP_BB_TIMER_[SET | CLEAR],
         *    WAKEUP_DIO3_EVENT_[SET | CLEAR],
         *    WAKEUP_DIO2_EVENT_[SET | CLEAR],
         *    WAKEUP_DIO1_EVENT_[SET | CLEAR],
         *    WAKEUP_DIO0_EVENT_[SET | CLEAR] */
        sleep_mode_flash_env->wakeup_ctrl = PADS_RETENTION_ENABLE |
        BOOT_FLASH_APP_REBOOT_DISABLE |
        BOOT_FLASH_XTAL_DEFAULT_TRIM |
        WAKEUP_DCDC_OVERLOAD_CLEAR |
        WAKEUP_PAD_EVENT_CLEAR |
        WAKEUP_RTC_ALARM_CLEAR |
        WAKEUP_BB_TIMER_CLEAR |
        WAKEUP_DIO3_EVENT_CLEAR |
        WAKEUP_DIO2_EVENT_CLEAR |
        WAKEUP_DIO1_EVENT_CLEAR |
        WAKEUP_DIO0_EVENT_CLEAR;

 

 

保持的电源

 

/* Power-off/retain memory instances */
    SYSCTRL->MEM_POWER_CFG = sleep_mode_env->mem_power_cfg |
                             PROM_POWER_ENABLE             |
                             FLASH_POWER_ENABLE;

 

 

电源模式这里混杂了大量寄存器操作

比较不友好,希望后面的固件库能够有重大更新

进入休眠大约在0.6uA

 

10.jpg

 

 

每3分钟唤醒一次,并疯狂连续广播6s,广播间隔40ms

测了一下连续10次采集广播的平均电流

 

11.jpg

 

 

大约在12uA左右

从这个角度,如果每s进行一次广播

再仔细优化一下,可能可以到10uA左右

功耗控制得勉强能看,基本堪用吧

 

大概计算一下

25mAh的电容,大约可以工作2083小时

大约相当于85天

如果85天内有一个艳阳天,基本上就能在2小时内完成充电

保障下一个85天的使用

这颗bq25570的启动电压是0.3v

阴天应该也有充电,只是多少的问题,毕竟大部分时间只有nA级的电流

如果85天完全极夜,那…,洗洗睡吧

关于BME280的数据广播

先是GCC使用printf打印浮点数的问题

GCC默认是不支持printf打印浮点数的

需要在工程中做配置修改

Project-->properties-->……

12.jpg

 

 

启用浮点printf

然后可以愉快地打印浮点数了

浮点数转数组

一般情况下使用union结构体

typedef union
{
   struct
   {
       unsigned char low_byte;
       unsigned char mlow_byte;
       unsigned char mhigh_byte;
       unsigned char high_byte;
    }float_byte;

    float  value;
}FLAOT_UNION;

 

 

但是实际使用中貌似不行,不知道哪里又被优化掉了

后来直接粗暴作业

unsigned char tmp[8];
                        tmp[0] = rcvAdvData[6];
                        tmp[1] = rcvAdvData[7];
                        tmp[2] = rcvAdvData[8];
                        tmp[3] = rcvAdvData[9];
                        tmp[4] = rcvAdvData[10];
                        tmp[5] = rcvAdvData[11];
                        tmp[6] = rcvAdvData[12];
                        tmp[7] = rcvAdvData[13];
                        double f1 = *(double *)(tmp);

 

解决问题,淋漓

蓝牙调试助手能够清晰扫描到相关数据

 

13.jpg

 

Indoor端也能正确收到广播和扫描响应包

并解析出对应的气压温度湿度

14.jpg

 

 

到此,基本上解决了项目的主要节点问题

接下来就是大量细小的作业和数据测试

不知道有没有时间去彻底完成

甚至不知道可行性有多大

自己选的题目,含泪跪完

以上

 

 

个人签名

So TM what......


回复

1万

帖子

14

资源

版主

法拉电容是亮点,没想到BME280也涨的这么厉害了。

点评

现在好多器件涨价涨得看不懂了 不知道要淘汰掉多少小作坊  详情 回复 发表于 2021-6-7 16:06

回复

3526

帖子

0

资源

纯净的硅(高级)

dcexpert 发表于 2021-6-7 10:37 法拉电容是亮点,没想到BME280也涨的这么厉害了。

现在好多器件涨价涨得看不懂了

不知道要淘汰掉多少小作坊

个人签名

So TM what......


回复

981

帖子

25

资源

一粒金砂(高级)

好厉害


回复

104

帖子

2

资源

一粒金砂(中级)

楼主技术棒棒哒,富含幽默感,“楼主最喜欢了”,“淋漓”

回复

737

帖子

210

资源

管理员

楼主霸气~

个人签名在路上……

EEworld 有你有我

回复

1998

帖子

3

资源

版主

一看就是经常搞DIY的博主!


回复

4744

帖子

1

资源

五彩晶圆(中级)

你那两个丝印图标是哪个汉子帮你画的?

点评

麻花疼大儿子截的,巨硬mspaint小妾画的  详情 回复 发表于 2021-6-9 22:40
个人签名

默认摸鱼


回复

3526

帖子

0

资源

纯净的硅(高级)

freebsder 发表于 2021-6-9 17:44 你那两个丝印图标是哪个汉子帮你画的?

麻花疼大儿子截的,巨硬mspaint小妾画的

点评

丑  详情 回复 发表于 2021-6-11 17:42
个人签名

So TM what......


回复

4744

帖子

1

资源

五彩晶圆(中级)

ljj3166 发表于 2021-6-9 22:40 麻花疼大儿子截的,巨硬mspaint小妾画的

点评

@eric_wang报告班长,有人说论坛logo丑 统统拉进黑名单  详情 回复 发表于 2021-6-11 18:20
个人签名

默认摸鱼


回复

4744

帖子

1

资源

五彩晶圆(中级)

ljj3166 发表于 2021-6-9 22:40 麻花疼大儿子截的,巨硬mspaint小妾画的

我换了个键盘,啪啪啪的特爽,因此我决定多打几个字:

丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑

个人签名

默认摸鱼


回复

3526

帖子

0

资源

纯净的硅(高级)

@eric_wang报告班长,有人说论坛logo丑

统统拉进黑名单

个人签名

So TM what......


回复
您需要登录后才可以回帖 登录 | 注册

最新文章 更多>>
    关闭
    站长推荐上一条 1/8 下一条

    About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

    站点相关: 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

    北京市海淀区知春路23号集成电路设计园量子银座1305 电话:(010)82350740 邮编:100191

    电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2021 EEWORLD.com.cn, Inc. All rights reserved
    快速回复 返回顶部 返回列表