搞嵌入式开发却不会写上位机?来了解下小白的福音Micro-Lab(多个案例带你快速入门)...

在工作中,以什么样的方式向领导汇报工作最直接高效呢?当然是图形界面!图形界面更好表达一个程序设计的逻辑思维,一目了然,本次介绍的Micro-Lab出自风媒科技-赵工之手,赵工在前两年也出版本过一本技术书籍<<STM32物联网实战教程>>,将自己毕生的开发经验汇集成册并开源pdf,并在电子发烧友等论坛推出了自己的30天入门物联网的免费教程。

1、什么是Micro-Lab?

Micro-Lab可以称得上是迄今为止最棒的嵌入式调试工具,在调试过程中遇到的所有痛点,在这里都得以解决,并引入了很多新的功能,如串口/网络示波器,串口/网络指令编程,革命性的事件驱动型上位机生成器——组态画布等数十个功能,无论是在使用手感上还是功能实用方面都是绝对一流,可以说Micro-Lab重新定义了嵌入式调试工具,同时也成为行业工具的标杆。(当前介绍引自码云:https://gitee.com/fengmeitech/Micro-Lab),来看看具体长什么样吧:

功能很丰富,基本上常见的调试功能都已经集成了,但这篇文章的重点是怎么用Micro-Lab与MCU进行交互。

2、Micro-Lab图形界面设计与使用

2.1 设计基本图形界面

切换到组态画布页面,然后手动将左边相关控件拖放到中间控件放置区域,选中其中一个控件时,可以通过右边更改控件的属性,和玩串口屏是类似的操作,即使完全没有用过,不看说明文档也能快速上手。

当控件布局完毕以后,在控件放置区域点击鼠标右键选择Run,这时候就会弹出刚刚画好的界面,如下:

在MCU与Micro-Lab建立通信之前我们要先了解三大控件类的用途:

  • 控制组件

控制组件目前有按键、开关、滑动条、旋钮,意思就是通过Micro-Lab去操作这些控件,Micro-Lab就会下发对应的协议指令给MCU,MCU收到对应的指令后,则根据SDK提供的接口去处理不同的事务。

  • 显示组件

显示组件目前有进度球、电池、仪表盘、时钟、指南针、点阵屏、数码管、LED、飞行仪、刻度计、文本、时间日期这些组件,当组态画图布局了对应组件,MCU往Micro-Lab发送对应的协议指令,Micro-Lab接收到对应的协议指令后进行数据解析,将数据展示在显示组件上。

  • 图表组件

图表控件目前仅有曲线波形,用于展示波形数据。

该项目还在持续成长中,相信将来会有越来越多的控件加入。

2.2 MCU与MicroLab建立通信

2.2.1 下载SDK

界面设计完毕,接下来需要下载SDK,将SDK移植到MCU上,让MCU和Micro-Lab建立通信,建立通信目前有两种方式,一种是串口通信,还有另一种是网络通信。

下载SDK的方法如下,在组态画布控件布局区域鼠标右键,然后选择Download MCU SDK

接下来会跳转到码云,点击下载SDK包,SDK包提供了stm32f103有关组态画布的例程,sdk则是与Micro-Lab通信的协议代码,关于怎么使用这个Micro-Lab以及如何移植到MCU上,up主赵工也在bibi上开了视频专门讲解,感兴趣的可以去看看,地址如下:

https://space.bilibili.com/281029341?from=search&seid=2143076487604252529

2.2.1 将SDK移植到小熊派上

这里我直接用小熊派自带的光强传感器例程进行移植

(1)将SDK包拷贝到工程中

(2)在Keil中包含SDK包

(3)修改工程

当然这个发送字节的接口也可以改成TCP/IP传输,下面再结合智能小车控制的例程进行修改。

(3)使用Micro-Lab

1、包含相应的头文件并导入外部event变量

2、初始化组态画布

3、调用更新画布接口发送数据到上位机

这里我用的是数码管控件,简单来说明下updateCanvas这个接口:

/**
 * brief : update Canvas`components state with serialport/net
 * parameter:
 *      componenttype  : component type
 *      componentnumber: component number
 *      data           : data that to canvas
 *      datalen        : data lenth
 * ret: none
 */
void updateCanvas(COMPONENT_TYPE componenttype, unsigned short componentnumber, char * data, unsigned short datalen)
{
    static char tbuffer[TBUFFERSIZE];
    static short size;

    packProtocol(ORGANIZATION, SECTION, DATAPOINT, componenttype, componentnumber, NONE_MSG, data, datalen, tbuffer, &size);
    sendBytes(tbuffer, size);
}

其中componenttype表示控件类型,sdk中用枚举进行描述,对应如下:

/* component type */
typedef enum
{
    NONE_COMPONENT =    -1,

    /*define ctrol component*/
    PUSHBUTTON     =    1,      //push button
    SWITCHBUTTON   =    2,      //switch
    SLIDER         =    3,      //slider
    DIAL           =    4,      //dial

    /*define show component*/
    WATERLEVER     =    1001,   //water level
    BATTERY        =    1002,   //battery power
    CARDASHBOARD   =    1003,   //car dashboard
    CLOCK          =    1004,   //clock with hour, minute, second hands
    COMPASS        =    1005,   //compass
    LEDDOT         =    1006,   //LED dot matrix
    LCDNUMBER      =    1007,   //lcd number
    LEDSTATE       =    1008,   //LED state
    PLANEDASHBOARD =    1009,   //plane dashboard
    TEMPMETER      =    1010,   //temperature meter
    LABEL          =    1011,   //text label
    LCDDATETIME    =    1012,   //datetime lcdnumber

    /*define chart component*/
    WAVECHART      =    2001,   //wave chart

    /*KEY & MOUSE*/
    KEYBOARD       =    9001,
    MOUSE          =    9002
}COMPONENT_TYPE;

对应的这些数值,比如1001、1002,其实就是画布上的属性区域的type值,例如我用的是数码管控件,对应的type值为1007。

componentnumber表示的是画布中控件的序号,比如创建了第一个数码管控件,那么索引值为0,第二个则为1,以此类推。

data表示显示在控件上数据

datalen表示数据长度

(4)运行结果

将移植后的工程编译下载后,设置好Micro-Lab串口参数,然后打开串口,运行画布即可看到数据已经上传上来了:

3、Micro-Lab按钮组件控制开发板上灯亮灭

3.1 设计基本图形界面

然后设置按钮按下和释放的事件值

这里设置按下为0,释放为1。

3.2 移植SDK,添加处理接口

以下只写关键的代码部分,其余部分请下载完整工程查看。

在串口接收中断中添加处理函数:processBytes

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
    if(RESET != __HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE))
    {
        __HAL_UART_CLEAR_IDLEFLAG(&huart1);
        HAL_UART_DMAStop(&huart1);
        cmd_parse_typedef.no_rx_count  =  __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数
        cmd_parse_typedef.rx_count =  CMD_STR_SIZE - cmd_parse_typedef.no_rx_count; //总计数减去未传输的数据个数,得到已经接收的数据个  
        processBytes((char *)cmd_parse_typedef.cmd_buffer, cmd_parse_typedef.rx_count);
        HAL_UART_Receive_DMA(&huart1,cmd_parse_typedef.cmd_buffer,CMD_STR_SIZE);//重新打开DMA接收  
        cmd_parse_typedef.BufferReady = 1 ;
    }
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

这里我用的是串口1的接收,采用空闲中断+DMA的方式接收。

在主函数中,处理接收到的数据,根据触发添加做自己想做的事情。

int main(void)
{
    /* USER CODE BEGIN 1 */

    /* USER CODE END 1 */

    /* MCU Configuration--------------------------------------------------------*/

    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();

    /* USER CODE BEGIN Init */

    /* USER CODE END Init */

    /* Configure the system clock */
    SystemClock_Config();

    /* USER CODE BEGIN SysInit */

    /* USER CODE END SysInit */

    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_DMA_Init();
    MX_USART1_UART_Init();
    /* USER CODE BEGIN 2 */
    //初始化画布
    initCanvas(events);
    /* USER CODE END 2 */

    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1)
    {
        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
        //如果串口数据接收完毕了,开始处理串口数据
        if(1 == cmd_parse_typedef.BufferReady)
        {
            cmd_parse_typedef.BufferReady = 0 ;

            ///遍历事件
            for(char i = 0; i < CANVASEVENTLIST_SIZE; ++i)
            {
                //如果当前SDK包含对应的控件事件,则处理
                if(events[i].componenttype != NONE_COMPONENT)
                {
                    //当前componenttype为按钮
                    if(PUSHBUTTON == events[i].componenttype)
                    {
                        //当前画布的Index为0
                        if(events[i].componentnumer == 0)
                        {
                            //当前触发了按下,点灯
                            if(PUSHBUTTON_PRESS == events[i].componentmsgtype)
                            {
                                HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
                            }
                            //当前触发了释放,灭灯
                            else if(PUSHBUTTON_RELEASE == events[i].componentmsgtype)
                            {
                                HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
                            }
                        }
                    }

                    //清除事件
                    events[i].componenttype = NONE_COMPONENT;
                    events[i].componentnumer = 0;
                    events[i].componentmsgtype = NONE_MSG;
                    events[i].msglen = 0;
                }
            }
        }

        /* USER CODE END 3 */
    }
}

其它控制控件也是类似的操作方法,非常简单。

连接好Micro-Lab后,打开串口,运行画布,鼠标按下button,开发板上的LED点亮,释放鼠标,则LED灯熄灭。

4、Micro-Lab按钮组件控制坦克小车

由于坦克小车是基于ESP8266 WIFI无线,所以我们就需要通过Micro-Lab的网络TCP/IP的模式与WIFI坦克小车进行通信,控制流程如下,详细代码见文末,在后台回复关键字获取。

4.1 Micro-Lab端的设置

4.2 小车端程序流程(基于STM32F103ZET6)

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
    /* USER CODE BEGIN 1 */

    /* USER CODE END 1 */

    /* MCU Configuration--------------------------------------------------------*/

    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();

    /* USER CODE BEGIN Init */

    /* USER CODE END Init */

    /* Configure the system clock */
    SystemClock_Config();

    /* USER CODE BEGIN SysInit */

    /* USER CODE END SysInit */

    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_TIM1_Init();
    MX_USART1_UART_Init();
    MX_USART2_UART_Init();
    MX_TIM3_Init();
    /* USER CODE BEGIN 2 */
    HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
    //setServoAngle(6);
    ESP8266_Init();
    //初始化画布
    initCanvas(events);
    printf("正在配置 ESP8266 ......\n" );
    ESP8266_Rst();

    while(!ESP8266_AT_Test());

    while(!ESP8266_Net_Mode_Choose(STA_AP));

    while(!ESP8266_JoinAP(User_ESP8266_ApSsid, User_ESP8266_ApPwd));

    while(!ESP8266_Enable_MultipleId(DISABLE));

    while(!ESP8266_Link_Server(enumTCP, User_ESP8266_TcpServer_IP, User_ESP8266_TcpServer_Port, Single_ID_0));

    while(!ESP8266_UnvarnishSend());

    //使能空闲中断
    __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
    printf("配置 ESP8266 完毕\n");
    //配置完毕,打开指示灯,代表此时已经连接服务器成功
    HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);
    /* USER CODE END 2 */

    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1)
    {
        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
        ESP8266_Data_Handler();
    }

    /* USER CODE END 3 */
}

接收到Micro-Lab发送来的数据后,调用SDK进行协议解析,然后解析得到的指令调用不同的小车控制逻辑:

//ESP8266接收数据处理
void ESP8266_Data_Handler(void)
{
    ESP8266_ReceiveString(ENABLE);

    if ( strEsp8266_Fram_Record .InfBit .FramFinishFlag )
    {
        //接收到数据闪烁灯
        HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);
        strEsp8266_Fram_Record .Data_RX_BUF [ strEsp8266_Fram_Record .InfBit .FramLength ] = '\0';

        for(int i = 0 ; i < strEsp8266_Fram_Record .InfBit .FramLength ; i++)
            printf("%x ", strEsp8266_Fram_Record .Data_RX_BUF[i]);

        printf("\r\n");
        //处理Canvas发来的数据
        processBytes(strEsp8266_Fram_Record .Data_RX_BUF, strEsp8266_Fram_Record .InfBit .FramLength);
        //执行事件处理
        Car_Event_Handler();
        //清楚串口缓存区
        memset(strEsp8266_Fram_Record.Data_RX_BUF, 0, strlen(strEsp8266_Fram_Record.Data_RX_BUF));
    }
}

小车事件处理:

//小车事件处理
void Car_Event_Handler(void)
{
    ///遍历事件
    for(int i = 0; i < CANVASEVENTLIST_SIZE; ++i)
    {
        //如果当前SDK包含对应的控件事件,则处理
        if(events[i].componenttype != NONE_COMPONENT)
        {
            //当前componenttype为按钮
            if(PUSHBUTTON == events[i].componenttype)
            {
                //小车前进
                if(events[i].componentnumer == 0)
                {
                    if(PUSHBUTTON_PRESS == events[i].componentmsgtype)
                        car_go();
                    else if(PUSHBUTTON_RELEASE == events[i].componentmsgtype)
                        car_stop();
                }

                //小车后退
                if(events[i].componentnumer == 1)
                {
                    if(PUSHBUTTON_PRESS == events[i].componentmsgtype)
                        car_back();
                    else if(PUSHBUTTON_RELEASE == events[i].componentmsgtype)
                        car_stop();
                }

                //小车左转
                if(events[i].componentnumer == 2)
                {
                    if(PUSHBUTTON_PRESS == events[i].componentmsgtype)
                        car_left();
                    else if(PUSHBUTTON_RELEASE == events[i].componentmsgtype)
                        car_stop();
                }

                //小车右转
                if(events[i].componentnumer == 3)
                {
                    if(PUSHBUTTON_PRESS == events[i].componentmsgtype)
                        car_right();
                    else if(PUSHBUTTON_RELEASE == events[i].componentmsgtype)
                        car_stop();
                }

                //小车瞄准
                if(events[i].componentnumer == 4)
                {
                    if(PUSHBUTTON_PRESS == events[i].componentmsgtype)
                        car_aim();
                    else if(PUSHBUTTON_RELEASE == events[i].componentmsgtype)
                        car_stop();
                }

                //小车发射
                if(events[i].componentnumer == 5)
                {
                    if(PUSHBUTTON_PRESS == events[i].componentmsgtype)
                        car_shot();
                    else if(PUSHBUTTON_RELEASE == events[i].componentmsgtype)
                        car_stop();
                }
            }

            //清除事件
            events[i].componenttype = NONE_COMPONENT;
            events[i].componentnumer = 0;
            events[i].componentmsgtype = NONE_MSG;
            events[i].msglen = 0;
        }
    }

}

运行效果:

实际演示效果:

Micro-Lab还在不断的成长中,相信未来会有越来越多好玩的功能,敬请期待!

5、案例下载

公众号后台回复:Micro-Lab 即可获取本节所有程序案例以及Micro-Lab软件的下载链接。

往期精彩

STM32在线升级OTA,看这一篇就够啦~

第10期 | ringbuff,通用FIFO环形缓冲区实现库

ESP8266实战贴:使用HTTP POST请求上传数据到公有云OneNet

会C/C++就可以开发Linux/Android应用程序?替代传统串口屏的Yoxios了解一下!

觉得本次分享的文章对您有帮助,随手点[在看]并转发分享,也是对我的支持。

Engineer-Bruce_Yang CSDN认证博客专家 嵌入式硬件 单片机 arm开发
本科毕业于华南理工大学,现美国卡罗尔工商管理硕士研究生在读,曾就职于世界名企伟易达、联发科技等,多年嵌入式产品开发经验,在智能玩具、安防产品、平板电脑、手机开发有丰富的实战开发经验,现任深圳市云之手科技有限公司副总经理、研发总工程师。
已标记关键词 清除标记
韦东山老师为啥要录升级版嵌入式视频? 200x年左右,嵌入式Linux在全世界、在中国刚刚兴起。 我记得我2005年进入中兴时,全部门的人正在努力学习Linux。 在2008年,我了一本书《嵌入式Linux应用开发完全手册》。 它的大概内容是:裸、U-boot、Linux内核、Linux设备驱动。 那时还没有这样讲解整个系统的书, 芯片厂家Linux开发包也还不完善,从bootloader到内核,再到设备驱动都不完善。 有全系统开发能力的人也很少。 于是这书也就恰逢其时,变成了畅销书。 我也根据这个思路录制了视频:裸、U-boot、Linux内核、Linux设备驱动。 收获些许名声,领很多人进入Linux世界。11年过去了,嵌入式Linux世界发生了翻天覆地的变化 ① 基本系统能用 芯片厂家都会提供完整的U-boot、Linux内核、芯片上硬件资源的驱动。 方案厂家会做一些定制,比如加上某个WIFI模块,会添加这个WIFI模块的驱动。 你可以使用厂家的原始方案,或是使用/借鉴方案商的方案,做出一个“能用”的产品。 ② 基础驱动弱化;高级驱动专业化 基础的驱动,比如GPIO、UART、SPI、I2C、LCD、MMC等,有了太多的书籍、视频、示例代码,修修改改总是可以用的。 很多所谓的驱动工程师,实际上就是“调参工程师”。 我们群里有名的火哥,提出了一个概念:这些驱动就起一个“hardware enable”的作用。 高级的驱动,比如USB、PCIE、HDMI、MIPI、GPU、WIFI、蓝牙、摄像头、声卡。 体系非常复杂,很少有人能讲清楚,很多时候只是一笔过。 配置一下应用层工具就了事,能用就成。 这些高级驱动,工作中需要专门的人来负责,非常专业。 他们是某一块的专家,比如摄像头专家、音频专家。 ③ 项目为王 你到一个公司,目的是把产品做出来,会涉及APP到内核到驱动全流程。 中小公司玩不起华为中兴的配置,需要的是全面手。 大公司里,只负责很小很小一块的镙丝钉,位置也不太稳固啊。 所以,如果你不是立志成为某方面的专家,那就做一个全栈工程师吧。 ④ 调试很重要 都说代码是3分7分调,各种调试调优技术,可以为你的升职加薪加一把火。 基于上述4点,我录制的全新视频将有这些特点: 1. 快速入门, 2. 实战项目, 3. 驱动大全, 4. 专题, 5. 授人以渔, 6. 要做任务 另外,我们会使用多款芯片同时录制,先讲通用的原理,再单独讲各个板子的操作。 这些芯片涵盖主流芯片公司的主流芯片,让你学习工作无缝对接。 1.快速入门 入门讲究的是快速入门之后再慢慢深入, 特别是对于急着找工作的学生,对于业余时间挑灯夜读的工作了的人,一定要快! 再从裸、U-boot、内核、驱动这样的路线学习就不适合了,时间就拉得太长了。 不好学了后面忘了前面。 并且实际工作中并不需要你去弄懂U-boot,会用就行:U-boot比驱动还复杂。 讲哪些内容? 怎么讲呢? 混着讲 比如先讲LED APP,知道APP怎么调用驱动,再讲LED硬件原理和裸,最后讲驱动的编。 这样可以快速掌握嵌入式Linux的整套开发流程, 不必像以前那样光学习裸就花上1、2个月。 而里面的裸课程,也会让你在掌握硬件操作的同时,把单片也学会了。 讲基础技能 中断、休眠-唤醒、异步通知、阻塞、内存映射等等制,会配合驱动和APP来讲解。 这些技能是嵌入式Linux开发的基础。 而这些驱动,只会涉及LED、按制、LCD等几个驱动。 掌握了这些输入、输出的驱动和对应的APP后,你已经具备基本的开发能力了。 讲配置 我们从厂家、从方案公司基本上都可以拿到一套完整的开发环境,怎么去配置它? 需要懂shell和python等配置脚本。 效果效率优先 以前我都是现场代码、现场文档,字得慢,降低了学习效率。 这次,效果与效率统一考虑,不再追求所有东西都现场。 容易的地方可先好代码文档,难的地方现场。 2.实战项目 会讲解这样的涉及linux网关/服务器相关项目(不限于,请多提建议):                      定位为:快速掌握项目开发经验,丰满简历。 涉及的每一部分都会讲,比如如果涉及蓝牙,在这里只会讲怎么使用,让你能出程序;如果要深入,可以看后面的蓝牙专题。 3. 驱动大全 包括基础驱动、高级驱动。 这些驱动都是独立成章,深入讲解。 虽然基础驱动弱化了,但是作为Linux系统开发人员,这是必备技能,并且从驱动去理解内核是一个好方法。 在讲解这些驱动时,会把驱动的运行环境,比如内核调度,进程线程等概念也讲出来,这样就可以搭建一个知识体系。 没有这些知识体系的话,对驱动的理解就太肤浅了,等于在Linux框架下,一叶障目,不见泰山。 定位为:工具、字典,用到再学习。 4. 专题 想深入学习的任何内容,都可独立为专题。 比如U-boot专题、内核内存管理专题、systemtap调试专题。
©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页