一个人的角落,也许并没人会看到这里~~ 祝福你,工作顺心,学习快乐~

MCU

一款小型哺乳动物仓笼体温监测与活动检测的装置以及部署方案

本方案使用物联网技术,通过高精度嵌入式红外摄像头,持续采集仓笼内热源信号,以及动物运动状态,使用嵌入式MCU对感知数据进行前端采集与简单分析,通过Wifi网络上传到数据中心,可形成数据汇总,采集时间跨度短可几周,长则跨年。装置。可按照需求,定制化按时按天按月或按年,生成可视化温度变化曲线,成本低廉,可供学术研究参考,或生物实验佐证使用。

单套设备仅需人民币300~500左右,约合美元60~80,成本低廉。对于单个仓笼,可同时部署多套设备,多套设备数据融合处理,以达到最佳采样角度,确保检测无死角。

方案包含完整装置采购清单,设备运行程序以及设备程序烧录指导,设备联网方法,现场部署指导等一系列指导文档,以及售后指导服务。

方案主体框架如下:

system_flow

一些国内用户的使用范例:

example_all

view_2

example_view

类似竞品论文参考:

https://journals.plos.org/plosbiology/article?id=10.1371/journal.pbio.3000406 http://journals.plos.org/plosbiology/article/asset?unique&id=info:doi/10.1371/journal.pbio.3000406.s014

合作洽谈可联系:iziy#mypre.cn / c834606877#163.com

一例MusicBox的固件简单分析,基于STM32芯片固件分析总结(待续)

从硬件到软件,制作一个高还原性,高保真的音乐盒子一直是我的兴趣之一。因此,我尝试在淘宝买一些音乐盒子做为设计参考,从双轨正负电压供电电路,电池充放电电路,stm32核心电路,到dac的解码电路,前极放大电路。到USB声卡,sdcard播放等功能。

最近发现某款音乐播放器的固件很有趣,因此,我在这简单记录一下分析过程,顺便总结一下从中学到的一些分析经验。

固件是通过SDcard更新的,商家发布新固件后,将固件放入SDcard后,芯片上的bootloader会检测并触发更新。

固件的部分内容如下:

bin-img

经过分析,固件使用了简单的压缩,一来减少固件体积,二是有校验机制。

通常,固件前一部分内容是,固件定义的栈顶指针以及芯片的中断向量表,均为4字节对齐。

在学习AZ3166碰到的一些常见问题,贴出来以免新手再绕弯路。

一、Mico开发环境的搭建

Mico开发环境由三部分组成,

  1. Mico Cube : 用于在命令行下对mico-os 编译链接下载,以及对mico-os进行版本管理的工具, 这是个python工具,所以需要使用pip install mico-cube 进行安装,并且需要在环境中安装git,以用于版本管理和os的import。

  2. Micoder:用于编译链接的组件,提供的gcc、gdb等交叉编译调试工具链: 这个需要和mico Cube配合使用,下载解压后, 需要使用 mico config --global micoder D:\MXCHIP\MiCO_SDK\MiCO\MiCoder 进行配置,使得mico可以调用micoder,进行编译,链接,下载。 否则会报错[mico]: can not found micoder

  3. Micoder IDE:用于在图形环境下进行版本管理,开发,还可以进行调试。 需要java运行环境,并且设置好path系统环境参数。 在安装Micoder IDE时会自动附带安装一个mico-os的sdk,

二、AZ3166对应mico-os的配置

在你的项目文件夹下通过命令 mico import mico-demos 从aliyun的git上下载一最新的demos和os

从eclipse中导入mico-demos, 右键根项目(mico-demos) Team->Switch To->Other : Remote Tracking->origin/experiment 右键mico-osTeam->Switch To->Other : Remote Tracking->origin/mico-os-4.0

三、Build Targets

添加编译命令可编译运行下载:

application.wifi_uart@AZ3166 total download run JTAG=stlink-v2-1

这里对解释一下编译命令:

@前面的部分代表要编译的程序所在文件夹,用.代替\表示文件目录的结构层次。

@后面的部分代表要编译使用的板子库型号。

最后,Eclipse IDE,会组合成命令:

mico make application.wifi_uart@AZ3166 total download run JTAG=stlink-v2-1

并在因为只有在根目录中,才能同时相对的找到mico-os和其相关的库文件和程序,否则会报找不到mico-os等等编译错误。

四、添加AZ3166驱动组件

在一个现有的MiCO项目中添加AZ3166组件,并运行附带的示例程序。在进行以下操作之前,先确保MiCO Cude已经正确安装。 导入AZ3166组件

进入一个MiCO项目的根目录,例如:cd helloworld。(如果本地没有现成的MiCO项目,可以使用mico new xxx创建一个项目,或者使用mico import xxx从版本库导入一个项目),
执行指令:mico add https://code.aliyun.com/mico/drv_AZ3166.git,从远程版本库中下载组件并且添加到当前项目。

运行AZ3166示例

1.组件中内置示例程序:drv_AZ3166/demo,执行编译命令。

例如在MXCHIP Microsoft Azure IoT Developer Kit平台AZ3166上:

mico make drv_AZ3166.demo@AZ3166@RTX total download JTAG=stlink-v2-1 run

详细的编译选项参考MiCO Cude

2.程序运行后,在设备OLED显示屏上显示传感器信息,并且在设备串口终端上显示传感器信息,例如:

50885 humidity = 52.90%, temp = 31.30C
50892 magnet =     84,    193,   -207
50899 lps22hb tmep = 32.00C, press = 1010.00hPa
50907 LSM6DSL [acc/mg]:       26,    -104,    1011
50915 LSM6DSL [gyro/mdps]:        0,   -1680,    1260

在学习CC3220碰到的一些常见问题,贴出来以免新手再绕弯路。

每次程序启动之后,可以正常运行,但reset之后,会运行out of box 程序

通过uniflash工具,删除 /sys/下的bin文件 参考 https://e2e.ti.com/support/wireless_connectivity/simplelink_wifi_cc31xx_cc32xx/f/968/t/625681


provisioning的时候,可以连上simplelink的wifi,但是在checking if device is simplelink的时候显示失败,原因不明。

通过在安卓手机上使用logcat显示smartconfig日志,发现程序访问了,mysimplelink.net这个域名,并且无法解析这个域名。

所以恍然大悟发现自己使用了影梭vpn,将会使用我的代理进行DNS解析,由于这是个本地域名,需要使用板载dns服务器。

此外,还发现,板载的HTTP服务器还提供,RESTful API接口,可以对通过POST对芯片配网等参数进行修改。


让cc3220自动连接Wifi。

成功配网后,串口调试信息显示: [Provisioning] Profile Added: SSID: LanBing’s_Wiki-Free [Provisioning] Profile confirmation: WLAN Connected! [Provisioning] Profile confirmation: IP Acquired!

但reset之后,设备不会自动使用保存的密码进行连接。

原因是cc3220的连接策略有四种选项组合:

  1. Auto
  2. Fast
  3. AnyP2P
  4. Auto Provisioning

其中Auto Provisioning就是在设备启动之后自己进行连接。

详见文档SWRU455E3.3.3.1节。

这里有三种方式可以对策略进行修改:

  1. 调用库函数: sl_WlanPolicySet(SL_WLAN_POLICY_CONNECTION,SL_WLAN_CONNECTION_POLICY(1,1,0,1),NULL,0);
  2. POST RESTful API: POST /api/1/wlan/policy_set HTTP/1.1 Host: mysimplelink.net Content-Type: application/x-www-form-urlencoded __SL_P_P.E=&__SL_P_P.F= 详见文档 SWRU455E 节8.4.7
  3. 通过SimpleLink Uniflash工具设置。

MSP432 Graphics Library

MSP432提供了对LCD屏操作的库函数,而从整个对LCD的操控来说,自下面上的分成了三层:HAL硬件抽象层,LCD屏的驱动层以及grlib上层图形库。

按照官方文档的介绍grlib库提供了一些上下文控制函数以及一系列简单的图表绘制函数:圆,位图,直线,矩形,字符串,按钮,图像按钮,单选框,多选框,以及位图数据转C格式数据工具。

HAL实现对MCU与LCD之间接口的配置,以及底层LCD控制器的串行通信,实现控制字符与数据字符的传输。 LCD驱动层利用HAL实现LCD的基本元素的绘制(点、直线等的绘制),为 grlib 提供调用接口。

以下为三层架构所对应的头文件,包含各自了所在层的函数原型,以及函数调用的方法

#include <ti/grlib/grlib.h>
#include "LcdDriver/Crystalfontz128x128_ST7735.h"
#include "LcdDriver/HAL_MSP_EXP432P401R_Crystalfontz128x128_ST7735.h"

LCD的初始化代码一般如下:

    /* Graphic library context */
    Graphics_Context g_sContext;  //全局上下文

    /* Initializes display */
    Crystalfontz128x128_Init();   //驱动层的LCD初始化代码

    /* Set default screen orientation */
    Crystalfontz128x128_SetOrientation(0);  //设置屏幕方向

    /* Initializes graphics context */
    Graphics_initContext(&g_sContext, &g_sCrystalfontz128x128, &g_sCrystalfontz128x128_funcs); 
    Graphics_setForegroundColor(&g_sContext, GRAPHICS_COLOR_BLUE);
    Graphics_setBackgroundColor(&g_sContext, GRAPHICS_COLOR_WHITE);
    GrContextFontSet(&g_sContext, &g_sFontFixed6x8);

可以看出初始化函数调用关系是逐层的,驱动层初始化通过调用HAL层进行硬件初始化,其中包括PortInit、SpiInit与LCD芯片初始化。 PortInit:为TI通用的端口复用配置,针对不同TI芯片电路进行端口的开启与复用,此部分代码同样为通用。

需要定制的只有HAL通信功能引脚的配置:

// Ports from MSP432 connected to LCD
#define LCD_SCK_PORT          GPIO_PORT_P1
#define LCD_SCK_PIN_FUNCTION  GPIO_PRIMARY_MODULE_FUNCTION
#define LCD_MOSI_PORT         GPIO_PORT_P1
#define LCD_MOSI_PIN_FUNCTION GPIO_PRIMARY_MODULE_FUNCTION
#define LCD_RST_PORT          GPIO_PORT_P5
#define LCD_CS_PORT           GPIO_PORT_P5
#define LCD_DC_PORT           GPIO_PORT_P3

// Pins from MSP432 connected to LCD
#define LCD_SCK_PIN           GPIO_PIN5
#define LCD_MOSI_PIN          GPIO_PIN6
#define LCD_RST_PIN           GPIO_PIN7
#define LCD_CS_PIN            GPIO_PIN0
#define LCD_DC_PIN            GPIO_PIN7

// Definition of USCI base address to be used for SPI communication
#define LCD_EUSCI_BASE        EUSCI_B0_BASE

grlib的初始化内容则是主要围绕一个g_sContext结构体进行, Graphics_Context 结构体定义如下:

typedef struct Graphics_Context
{
    int32_t size;                         //!< The size of this structure.
    const Graphics_Display *display;    //!< The screen onto which drawing operations are performed.
    Graphics_Rectangle clipRegion;        //!< The clipping region to be used when drawing onto the screen.
    uint32_t foreground;                 //!< The color used to draw primitives onto the screen.
    uint32_t background;                 //!< The background color used to draw primitives onto the screen.
    const Graphics_Font *font;            //!< The font used to render text onto the screen.
} Graphics_Context;

Graphics_Display *display结构体包括了特定LCD的宽高信息以及驱动函数指针。由此可以看出,LCD屏与上grlib也是分离的,当硬件更换为不同的LCD时,只需修改对应LCD驱动层函数指针,甚至可以实现LCD热更换。

但三层架构的设计架构移植性较高,牺牲了一部分屏幕控制器的硬件性能。

MSP432 launchpad User Application Port UART

串口通信的配置

串口的配置 主要包括两个方面:

  • 初始化结构体的配置
  • 串口模块中断的开启

以下是基于DriverLib的初始化代码

    const eUSCI_UART_Config uartConfig =
    {
            EUSCI_A_UART_CLOCKSOURCE_SMCLK,        // SMCLK Clock Source 48Mhz
            312,                                   // BRDIV = 312
            8,                                     // UCxBRF = 8
            0xAA,                                  // UCxBRS = 0xAA
            EUSCI_A_UART_NO_PARITY,                // No Parity
            EUSCI_A_UART_LSB_FIRST,                // MSB First
            EUSCI_A_UART_ONE_STOP_BIT,             // One stop bit
            EUSCI_A_UART_MODE,                     // UART mode
            EUSCI_A_UART_OVERSAMPLING_BAUDRATE_GENERATION  // overSampling Mode
    };

    /* Configuring UART Module */
     UART_initModule(EUSCI_A0_BASE, &uartConfig);

    /* Enable UART module */
     UART_enableModule(EUSCI_A0_BASE);

     UART_enableInterrupt(EUSCI_A0_BASE, EUSCI_A_UART_RECEIVE_INTERRUPT);
     Interrupt_enableInterrupt(INT_EUSCIA0);

     MAP_Interrupt_enableSleepOnIsrExit(); //低功耗模式

比较困难的地方在于波特率的设置主要涉及到BRDIV,UCxBRF,UCxBRS, overSampling Mode这几个寄存器参数。

DriverLib手册中提到了一个参数计算器 http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSP430BaudRateConverter/index.html

如果上位机可以接收到FF FE之类的乱码一般是波特率设置不正确导致,不同的时钟源频率需要将其分离成所需要的频率,具体的计算公式在Reference Manual 的22.3.10小节有详细说明,将计算出来的波特率只要与上位机设置的相差不远也是可以进行通信的。

此外,网上有一部分相关帖子提到中断名的问题在CCS里边,系统会为所有中断定义一个默认中断,如果程序没有定义相关中断,在程序中又开启了此中断,中断触发后将会陷入到默认中断中。这里的默认中断内容是一个死循环,中断向量表在startup_msp432p401r_ccs.c中有定义。

  //Configure UART pins

  P1SEL0 |=BIT2 | BIT3;                  // set2-UART pin as second function



 __enable_interrupt();

 NVIC->ISER[0] = 1 << ((EUSCIA0_IRQn) & 31); // EnableeUSCIA0 interrupt in NVIC module

  //Configure UART

  UCA0CTLW0|= UCSWRST;
  UCA0CTLW0|= UCSSEL__SMCLK;             // PuteUSCI in reset

  // BaudRate calculation
  //12000000/(16*9600) = 78.125
  //Fractional portion = 0.125
  // User'sGuide Table 21-4: UCBRSx = 0x10
  // UCBRFx =int ( (78.125-78)*16) = 2

  UCA0BR0 =78;                           //12000000/16/9600
  UCA0BR1 =0x00;
  UCA0MCTLW= 0x1000 | UCOS16 | 0x0020;    //注意这一行的设定

  UCA0CTLW0&= ~UCSWRST;                  //Initialize eUSCI

  UCA0IE |=UCRXIE;                       // EnableUSCI_A0 RX interrupt

近几年来使用DriverLib开发的越来越多了,个人认为DriverLib可以在一定程度上简化开发流程,对新手友好,上手快,但是由于432官方对DriverLib手册并不是非常的友好,很多函数仅仅是一带而过,函数说明甚至没有源码注释写得好,这一使得学习432的内容需要齐头并进,能够简单易懂的用DriverLib难一些的还是要参考寄存器配置。

以下是串口中断处理函数,利用寄存器操作,将收到的串口数据进行回传。

/*
 * EUSCI A0 UART interrupt handler.
 */
void EUSCIA0_IRQHandler(void)
{
    if(UCA0IFG & UCRXIFG)
    {
     while(!(UCA0IFG&UCTXIFG));
           UCA0TXBUF = UCA0RXBUF;
     __no_operation();

    }
}

而DriverLib版:

    int receiveByte;
    receiveByte = UART_receiveData(EUSCI_A0_BASE);
    UART_transmitData(EUSCI_A0_BASE,receiveByte);

DriverLib的底层实现也是通过寄存器来完成的,这样可以通过学习源代码的方式了解某些寄存器的功能,省略某些技术细节,随着处理器执行效率的提高,代码越来越复杂,这样的库函数操作将会非常常见。

MSP432 使用Timer_A输出PWM

Timer_A 输出 PWM的问题

在官方例程中给出的是一种直接操作寄存器的方式启动的,这让我们这些刚刚接触MSP432这种嵌入式芯片的来说是很难看懂的,然而PWM的输出是可以重新映射到其它端口输出的,例程中又掺杂了PMAP GPIO端口的映射,这更使得学习无从下手。

通过寄存器操作的方式输出PWM波形

    TIMER_A0->CCR[0] = PWM_PERIOD;
    TIMER_A0->CCTL[1] = TIMER_A_CCTLN_OUTMOD_7;      // CCR1 Reset/set
    TIMER_A0->CCR[1] = PWM_PERIOD * (55 / 100.0);    // CCR1 PWM duty cycle
    TIMER_A0->CTL = TIMER_A_CTL_SSEL__SMCLK // 使用SMCLK时钟源
                  | TIMER_A_CTL_MC__UP   
                  | TIMER_A_CTL_CLR;  // SMCLK, up mode, clear TAR

引用msp432p401r.h头文件定义:

#define TIMER_A_CTL_MC__UP    ((uint16_t)0x0010)    /*!< Up mode: Timer counts up to TAxCCR0 */

通过 动态修改 TIMER_A0->CCR[1] 内容可以改变PWM间隔时间

通过DriverLib操作的方式输出PWM波形

/* Timer_A PWM Configuration Parameter */
Timer_A_PWMConfig pwmConfig =
{
    TIMER_A_CLOCKSOURCE_SMCLK,
    TIMER_A_CLOCKSOURCE_DIVIDER_1,
    32000,
    TIMER_A_CAPTURECOMPARE_REGISTER_1,
    TIMER_A_OUTPUTMODE_RESET_SET,
    3200
};
Timer_A_generatePWM(TIMER_A1_BASE, &pwmConfig);

Timer_A_setCompareValue(TIMER_A2_BASE, TIMER_A_CAPTURECOMPARE_REGISTER_1, PWM_PERIOD * (50/100.0f)); //动态设置PWM 间隔时间

而timer_a.C文件中对Timer_A_generatePWM函数定义如下

void Timer_A_generatePWM(uint32_t timer, const Timer_A_PWMConfig *config)
{
    //此处省略一部分,参数合法判断...

    privateTimer_AProcessClockSourceDivider(timer, config->clockSourceDivider);

    TIMER_A_CMSIS(timer)->CTL &=
            ~(TIMER_A_CLOCKSOURCE_INVERTED_EXTERNAL_TXCLK + TIMER_A_UPDOWN_MODE
                    + TIMER_A_DO_CLEAR + TIMER_A_TAIE_INTERRUPT_ENABLE);

    TIMER_A_CMSIS(timer)->CTL |= (config->clockSource + TIMER_A_UP_MODE
            + TIMER_A_DO_CLEAR);

    TIMER_A_CMSIS(timer)->CCR[0] = config->timerPeriod;

    TIMER_A_CMSIS(timer)->CCTL[0] &= ~(TIMER_A_CAPTURECOMPARE_INTERRUPT_ENABLE
            + TIMER_A_OUTPUTMODE_RESET_SET);

    uint8_t idx = (config->compareRegister>>1) - 1;
    TIMER_A_CMSIS(timer)->CCTL[idx] |= config->compareOutputMode;
    TIMER_A_CMSIS(timer)->CCR[idx] = config->dutyCycle;
}

可以看到相关操作与寄存器类似,但一次只能修改一路PWM输出。难怪官方例程中直接使用寄存器操作,多路输出更加容易控制。

MSP432中GPIO中断相关操作

GPIO的P1,P2,PA支持中断

MSP432 中的中断相关寄存器 PxIFG 中断标志寄存器 PxIV 中断向量寄存器

Driverlib 所涉及到GPIO中断的函数

GPIO_registerInterrupt GPIO_unregisterInterrupt //Registers/UnRegister s an interrupt handler for the port interrupt. GPIO_disableInterrupt GPIO_enableInterrupt GPIO_getEnabledInterruptStatus GPIO_interruptEdgeselect GPIO_getInterruptStatus GPIO_clearInterruptFlag

![GPIO_IES寄存器][/post_res/GPIO_IES.png]

![GPIO_PxIFG寄存器][/post_res/GPIO_PxIFG.png]

![GPIO_PxIV寄存器][/post_res/GPIO_PxIV.png]

对中断的配置

/* Confinguring P1.1 & P1.4 as an input and enabling interrupts */
MAP_GPIO_setAsInputPinWithPullUpResistor(GPIO_PORT_P1, GPIO_PIN1 | GPIO_PIN4);
MAP_GPIO_clearInterruptFlag(GPIO_PORT_P1, GPIO_PIN1 | GPIO_PIN4);
MAP_GPIO_enableInterrupt(GPIO_PORT_P1, GPIO_PIN1 | GPIO_PIN4);
MAP_GPIO_interruptEdgeSelect(GPIO_PORT_P1, GPIO_PIN1 | GPIO_PIN4, GPIO_HIGH_TO_LOW_TRANSITION);

使用STM32可编程芯片的制作的机械键盘主控

曾经是去年四月的想法,自定义一把顺手的机械键盘,终于到了今年暑期才完成,可怕的拖延症。

然而并不是像想象的那么简单,拿到手的是一把 JAGOR TITON 100 ,由于键盘主控制器与轴板是可分离的连接方式,才有了这个想法。

这其中所要经历的工作有:主控芯片的选择,设计键盘主控制器PCB,为其焊接可编程芯片,开发主控代码,完全拆卸键盘结构,重新焊接键盘轴体和布局背光灯。

芯片选择至关重要,最终找到了一块引脚数量足够多,而且又不缺Demo的Stm32f103c8t6。

将芯片所有多功能引脚全改为IO引脚,其中8个IO作为外部中断使用的列矩阵引脚,其余做为行矩阵引脚,留出多余的用作LED指示灯。

PCB板的设计大小长宽不能超过50*13mm,使用芯片的最小核心Demo,研究了一番,将多余芯片功能去掉,最后根据轴板IC座引脚功能,调整芯片和电容电阻位置以达到最大化的引脚对应和最少的线路穿插(AD提供鸡肋的组件布局自动化浪费了半天的时间,最后还是手动布局比较靠谱)。最后将设计文档交给华强北的PCB打印厂,50包邮搞定~

接着是将从taobao买到的STM芯片焊到PCB上,没有专业的工具,多用一些松香增加锡的张力,一支尖角电烙铁也可以搞定。

轴板拆缷要借助吸锡器来完成,像PCB这类除了第一次生产之后,永不变动的东西,要再进行二次焊接的话,焊盘很容易脱落,所以改轴和灯光部分需要一次完成。

在Linux下 使用Scroll键开关键盘背光灯

花了4天半的时间鼓捣好了一个机械键盘,

找了一个老外大牛写的stm32-based 键盘矩阵扫描器,改改装在了键盘上边

dfu功能,移植在keil下没有被识别,估计一阵子也没有多少时间来折腾,暂时先用着,

因为io口不够用,多区背光给改成了Scroll键开关,

但是,发现 Scroll 键 在高版本linux下给屏蔽了,

国内百度了半天没找到好的解决方案

转到google 发现 Why is the Scroll Lock key disabled in Cinnamon/Linux/Xorg? 一文给出了详细的方法