yljon 发表于 2020-4-29 07:47

【RTOS】+ 基于RT-Thread与东软载波的上位机下位机开发

顶楼:RT-Thread活动前传:
【表扬】东软载波的王永全工程师

沙发:RT-Thread活动正文:
【RTOS】+ 基于RT-Thread与东软载波的上位机下位机开发

参加RTT活动,我申请了东软载波的开发板,由于我自己设置不当,
一直没有能够正确烧录程序,晚上22点尝试在群里询问,
东软载波的王(永全)工立即响应回复,不停的为我排查问题,
一直持续到晚上23点30分,终于找到原因:是我设置错误。
前前后后王工持续解答了90分钟,辛苦了!!!
随后,我尝试发一个红包给王工,王工一直没有领取,十分的诚恳,
说明东软载波的服务是靠谱的。











在此,特别【表扬】东软载波的王永全工程师。

RT-Thread活动正文将陆续更新在沙发层。

yljon 发表于 2020-4-29 07:48

本帖最后由 yljon 于 2020-6-28 20:42 编辑

基于RT-Thread与东软载波的上位机下位机开发
作者:李俊(论坛ID:yljon)
一、概述:
1. 下位机系统实现功能:
   1) 电参数自动采集,演示系统用开发板上VR采集ADC,实际系统中用RS-485采配电房交流2000A配电柜;
   2) A/B路合闸开关切换控制,演示系统用开发板上K1,K2,实际系统用隔离IO采集合闸开关;
   3) A/B路合闸开关状态显示,演示系统用开发板上LED,实际系统用隔离IO输出;
   4) 触摸屏显示电流和A/B路状态,触摸屏也可以操作A/B路合闸开关切换;
2. 上位机系统实现功能:
   1) 采集的数据上传到电脑上位机程序中,在上位机程序显示采集数据和状态;
   2) 上位机程序下发控制指令,使A/B路合闸开关进行动作;
   3. 下位机准备用东软载波开发板,MCU型号:ES32F0654LT;
   4. 下位机系统准备用RT-Thread;

二、RT-Thread使用情况概述:
首次接触RT-Thread,感觉比UCOS比FreeRTOS强大,RT-Thread 与其他很多 RTOS 如 FreeRTOS、uC/OS 的主要区别之一是,它不仅仅是一个实时内核,还具备丰富的中间层组件。详细内容参见RT-Thread官网:https://www.rt-thread.org/document/site/tutorial/quick-start/introduction/introduction/
我这个演示项目主要用RT-Thread的内容有:
1. 由于申请的板卡是M0的板卡,那就用RT-Thread精简版的吧,就用:RT-Thread Nano;
2. 虽然这个演示项目足够简单,多线程还是要用的;
3. 各线程里面的延时肯定用RT-Thread自带的,时钟管理要用滴;
4. 由于通信主要用UART,预计接收发送啥的应该用中断;
5. 开发板上的按钮,预计用中断吧;
其它RT-Thread高级功能估计这次用不上了,暂时就不去搞了。

三、硬件框架:

1. 硬件包括:
   1) 开发板:东软载波开发板,MCU型号:ES32F0654LT
   2) 烧录器:东软载波配套的ES-LINK II
   3) 串口屏:型号TJC3224T124_011(这个也是向官方申请的样品)
2. 硬件连接示意图:


3. 实际硬件图:



四、软件框架:
1. 先将驱动装起来,由于此次是二姨家配套的板子提供的烧录器是ES-LINK II,到官网(http://www.essemi.com/)下载ES-LINK II的驱动ESburner_1.0.0.98.zip,安装好驱动之后,将ES-LINKII插入电脑,在电脑上会看到如下:




打开KEIL,在设置里看见这样,至此驱动安装就OK啦。



2.我的开发板是ES32F0654,要将PACK装起来,到官网(http://www.essemi.com/)上下载PACK,我下载的是Eastsoft.ES32_DFP.1.0.5,安装好之后,在KEIL中可以看见如下:



3.为了编程方便,先选择一个官网(http://www.essemi.com/)上的DEMO基础程序,我选择的是ES32_SDK_V1.05下的02_send_recv_by_interrupt,



4.下面就是重点了:移植RT-Thread Nano,精彩的部分即将开始:
1)点击 Manage Run-Time Environment,在 Manage Rum-Time Environment 里 "Software Component" 栏找到 RTOS,Variant 栏选择 RT-Thread,然后勾选 kernel,点击 "OK" 就添加 RT-Thread 内核到工程了。



2)这个时候将在工程里看见添加内容:




3)下面是让这个RT-Thread接管我们的裸机程序:
A. 异常处理函数的接管:删除原来裸机程序irq.c中原来的HardFault_Handler() ,这个在RT-Thread中有重新定义;
B.挂机处理函数的接管:删除原来裸机程序irq.c中原来的PendSV_Handler() ,这个在RT-Thread中有重新定义;
C.时钟处理函数的接管:删除原来裸机程序irq.c中原来的SysTick_Handler() ,这个在RT-Thread中有重新定义;
D. 在main.c 加入RT-Thread的头文件rtthread.h即可。
至此一个RT-Thread程序已经诞生了!!!

5.在移植好的程序里添加需要的功能:
1)添加数据采集功能:
这个系统是A路和B路切换功能,但是采集只需要采集一路即可,使用ADC采集,ADC初始化代码如下:

void adc_init(void)
{
      gpio_init_t x;

      /* Initialize ADC pin */
      x.mode = GPIO_MODE_INPUT;
      x.pupd = GPIO_PUSH_UP;
      x.odrv = GPIO_OUT_DRIVE_NORMAL;
      x.flt= GPIO_FILTER_DISABLE;
      x.type = GPIO_TYPE_CMOS;
      x.func = GPIO_FUNC_0;
      ald_gpio_init(GPIO_CH_PORT, ADC_CH14_PIN, &x);
      
      /* clear adc_handle_t structure */
      memset(&h_adc, 0x0, sizeof(adc_handle_t));
      /* clear adc_nch_conf_t structure */
      memset(&nm_config, 0x0, sizeof(adc_nch_conf_t));      
      /* Initialize adc */
      h_adc.perh = ADC0;
      h_adc.init.data_align            = ADC_DATAALIGN_RIGHT;
      h_adc.init.scan_mode               = DISABLE;
      h_adc.init.cont_mode               = DISABLE;
      h_adc.init.nch_len               = ADC_NCH_LEN_1;
      h_adc.init.disc_mode                  = ADC_ALL_DISABLE;
      h_adc.init.disc_nbr                = ADC_DISC_NBR_1;
      h_adc.init.conv_res                   = ADC_CONV_RES_12;
      h_adc.init.clk_div               = ADC_CKDIV_128;
//      h_adc.init.nche_sel                = ADC_NCHESEL_MODE_ALL;
      h_adc.init.neg_ref                  = ADC_NEG_REF_VSS;
      h_adc.init.pos_ref                  = ADC_POS_REF_VDD;
//      h_adc.adc_reg_cplt_cbk             = normal_convert_complete;
//      h_adc.adc_inj_cplt_cbk             = insert_convert_complete;
//      h_adc.adc_out_of_win_cbk         = out_window_complete;
//      h_adc.adc_error_cbk                = error_complete;
//      h_adc.adc_ovr_cbk                  = ovr_complete;
      ald_adc_init(&h_adc);

      /* Initialize normal convert channel */
      nm_config.channel       = ADC_CHANNEL_14;
      nm_config.rank          = ADC_NCH_RANK_1;
      nm_config.samp_time   = ADC_SAMPLETIME_4;
         ald_adc_normal_channel_config(&h_adc, &nm_config);
      
      return;
}



在ADC初始化之后,就可以在主程序中直接使用了,直接将采集数据给myvolt即可,
代码如下:

ald_adc_start_by_dma(&h_adc, &myvolt, 1, ADC_CHANNEL_0);
2)由于我设计的数据采集系统支持本地按钮切换操作,按钮才操作使用GPIO的INPUT功能即可,为了方便使用,使用中断方式,GPIO初始化代码如下:

void key_init(void)
{
      gpio_init_t x;
      exti_init_t exti;

      /* Initialize key exti pin as input */
      x.mode = GPIO_MODE_INPUT;
      x.odos = GPIO_PUSH_PULL;
      x.pupd = GPIO_PUSH_UP;
      x.odrv = GPIO_OUT_DRIVE_NORMAL;
      x.flt= GPIO_FILTER_DISABLE;
      x.type = GPIO_TYPE_CMOS;
      x.func = GPIO_FUNC_1;
      ald_gpio_init(GPIO_KEY_1_PORT, GPIO_KEY_1_PIN, &x);
      ald_gpio_init(GPIO_KEY_2_PORT, GPIO_KEY_2_PIN, &x);      

      /* Initialize external interrupt */
      exti.filter      = ENABLE;
      exti.cks         = EXTI_FILTER_CLOCK_10K;
      exti.filter_time = 10;
      ald_gpio_exti_init(GPIO_KEY_1_PORT, GPIO_KEY_1_PIN, &exti);
      ald_gpio_exti_init(GPIO_KEY_2_PORT, GPIO_KEY_2_PIN, &exti);

      /* Clear interrupt flag */
      ald_gpio_exti_clear_flag_status(GPIO_KEY_1_PIN);
      ald_gpio_exti_clear_flag_status(GPIO_KEY_2_PIN);

      /* Configure interrupt */
      ald_gpio_exti_interrupt_config(GPIO_KEY_1_PIN, EXTI_TRIGGER_TRAILING_EDGE, ENABLE);      
      ald_gpio_exti_interrupt_config(GPIO_KEY_2_PIN, EXTI_TRIGGER_TRAILING_EDGE, ENABLE);      
}


初始化完成之后,编写中断处理函数,由于分别使用A路和B路按钮,判断一下按钮即可,代码如下:

void gpio_irq_handler(void)
{
      /* Check and clear interrupt flag */
      if (ald_gpio_exti_get_flag_status(GPIO_KEY_1_PIN)) {
                ald_gpio_exti_clear_flag_status(GPIO_KEY_1_PIN);
                //ald_gpio_toggle_pin(LED1_PORT, LED1_PIN);
                ald_gpio_write_pin(LED2_PORT,LED2_PIN,1);
                //
                //这里可以加入防呆
                //
                ald_gpio_write_pin(LED1_PORT,LED1_PIN,0);
                FenLu=1;
      }

      if (ald_gpio_exti_get_flag_status(GPIO_KEY_2_PIN)) {
                ald_gpio_exti_clear_flag_status(GPIO_KEY_2_PIN);
                //ald_gpio_toggle_pin(LED2_PORT, LED2_PIN);
                ald_gpio_write_pin(LED2_PORT,LED2_PIN,0);
                //
                //这里可以加入防呆
                //
                ald_gpio_write_pin(LED1_PORT,LED1_PIN,1);
                FenLu=2;
      }
      return;
}


3)由于我设计这套系统支持触摸屏切换A路和B路,触摸屏使用串口,所以串口的处理如下,先初始化触摸屏的串口UART1,代码如下:

void uart1_init(void)
{
      gpio_init_t x;

      /* Initialize tx pin */
      x.mode = GPIO_MODE_OUTPUT;
      x.odos = GPIO_PUSH_PULL;
      x.pupd = GPIO_PUSH_UP;
      x.odrv = GPIO_OUT_DRIVE_NORMAL;
      x.flt= GPIO_FILTER_DISABLE;
      x.type = GPIO_TYPE_TTL;
      x.func = GPIO_FUNC_3;
      ald_gpio_init(UART1_TX_PORT, UART1_TX_PIN, &x);

      /* Initialize rx pin */
      x.mode = GPIO_MODE_INPUT;
      x.odos = GPIO_PUSH_PULL;
      x.pupd = GPIO_PUSH_UP;
      x.odrv = GPIO_OUT_DRIVE_NORMAL;
      x.flt= GPIO_FILTER_DISABLE;
      x.type = GPIO_TYPE_TTL;
      x.func = GPIO_FUNC_3;
      ald_gpio_init(UART1_RX_PORT, UART1_RX_PIN, &x);

      /* clear uart_handle_t structure */
      memset(&h_uart1, 0x0, sizeof(h_uart1));
      /* Initialize tx_buf */
      memset(tx1_buf, 0x55, 32);
      /* Initialize uart */
      h_uart1.perh             = UART1;
      h_uart1.init.baud      = 115200;
      h_uart1.init.word_length = UART_WORD_LENGTH_8B;
      h_uart1.init.stop_bits   = UART_STOP_BITS_1;
      h_uart1.init.parity      = UART_PARITY_NONE;
      h_uart1.init.mode      = UART_MODE_UART;
      h_uart1.init.fctl      = UART_HW_FLOW_CTL_DISABLE;
      h_uart1.tx_cplt_cbk      = uart1_send_complete;
      h_uart1.rx_cplt_cbk      = uart1_recv_complete;
      h_uart1.error_cbk      = uart1_error;
      ald_uart_init(&h_uart1);      
      return;
}

对于触摸屏的发送和接收直接在主函数里处理,代码如下:

ald_uart_send_by_it(&h_uart1, tx1_buf, 10);
ald_uart_recv_by_it(&h_uart1, rx1_buf, 8);

对于触摸屏的发送,无需我们处理,但是对于接收,我们必须处理,放在中断里处理,中断处理函数如下:

void uart1_recv_complete(uart_handle_t *arg)
{
      
      if(rx1_buf==0x01&&rx1_buf==0x06&&rx1_buf==0x88&&rx1_buf==0x99)
      {
                FenLu=rx1_buf;
                if(FenLu==1)
                {
                        ald_gpio_write_pin(LED2_PORT,LED2_PIN,1);
                        //
                        //这里可以加入防呆
                        //
                        ald_gpio_write_pin(LED1_PORT,LED1_PIN,0);
                }
                if(FenLu==2)
                {
                        ald_gpio_write_pin(LED2_PORT,LED2_PIN,0);
                        //
                        //这里可以加入防呆
                        //
                        ald_gpio_write_pin(LED1_PORT,LED1_PIN,1);
                }
      }
      return;
}

4)在这套系统中,上位机电脑上的程序也是我自己编写,所以通讯速度、格式、协议我都完全掌控,所以采用串口,对串口UART2的初始化如下:

<p>void uart2_init(void)
{
gpio_init_t x;</p><p> /* Initialize tx pin */
x.mode = GPIO_MODE_OUTPUT;
x.odos = GPIO_PUSH_PULL;
x.pupd = GPIO_PUSH_UP;
x.odrv = GPIO_OUT_DRIVE_NORMAL;
x.flt= GPIO_FILTER_DISABLE;
x.type = GPIO_TYPE_TTL;
x.func = GPIO_FUNC_5;
ald_gpio_init(UART2_TX_PORT, UART2_TX_PIN, &x);</p><p> /* Initialize rx pin */
x.mode = GPIO_MODE_INPUT;
x.odos = GPIO_PUSH_PULL;
x.pupd = GPIO_PUSH_UP;
x.odrv = GPIO_OUT_DRIVE_NORMAL;
x.flt= GPIO_FILTER_DISABLE;
x.type = GPIO_TYPE_TTL;
x.func = GPIO_FUNC_5;
ald_gpio_init(UART2_RX_PORT, UART2_RX_PIN, &x);</p><p> /* clear uart_handle_t structure */
memset(&h_uart2, 0x0, sizeof(h_uart2));
/* Initialize tx_buf */
memset(tx2_buf, 0x56, 32);
/* Initialize uart */
h_uart2.perh             = UART2;
h_uart2.init.baud      = 115200;
h_uart2.init.word_length = UART_WORD_LENGTH_8B;
h_uart2.init.stop_bits   = UART_STOP_BITS_1;
h_uart2.init.parity      = UART_PARITY_NONE;
h_uart2.init.mode      = UART_MODE_UART;
h_uart2.init.fctl      = UART_HW_FLOW_CTL_DISABLE;
h_uart2.tx_cplt_cbk      = uart2_send_complete;
h_uart2.rx_cplt_cbk      = uart2_recv_complete;
h_uart2.error_cbk      = uart2_error;
ald_uart_init(&h_uart2);

return;
}

</p><p> </p>
对上位机的串口发送和接收也直接在主函数里处理即可,由于协议是我自己编写,发送、接收格式和触摸屏发送、接收格式一致,这样就方便许多,代码如下:
ald_uart_send_by_it(&h_uart2, tx1_buf, 10);
ald_uart_recv_by_it(&h_uart2, rx2_buf, 8);
对于上位机电脑的发送,无需我们处理,但是对于接收,我们必须处理,放在中断里处理,中断处理函数如下:

void uart2_recv_complete(uart_handle_t *arg)
{
      

      if(rx2_buf==0x01&&rx2_buf==0x06&&rx2_buf==0x88&&rx2_buf==0x99)
      {
                FenLu=rx2_buf;
                if(FenLu==1)
                {
                        ald_gpio_write_pin(LED2_PORT,LED2_PIN,1);
                        //
                        //这里可以加入防呆
                        //                        
                        ald_gpio_write_pin(LED1_PORT,LED1_PIN,0);
                }
                if(FenLu==2)
                {
                        ald_gpio_write_pin(LED2_PORT,LED2_PIN,0);
                        //
                        //这里可以加入防呆
                        //                        
                        ald_gpio_write_pin(LED1_PORT,LED1_PIN,1);
                }
      }               
      return;
}

至此,本套系统的全部代码都编写完成了,烧录到板子上进行试验,可以完美运行。



6.上位机电脑程序的编写,主要功能就是:
A.在上位机上显示A路和B路状态;
B.在上位机上显示采集的数据;
C.在上位机上进行A路和B路切换;
配合下位机可以完美运行:




五.演示视频:
1.使用按钮控制A/B路切换的视频地址;
https://www.bilibili.com/video/BV1Qt4y1C7nz



2.使用触摸屏控制A/B路切换的视频地址;
https://www.bilibili.com/video/BV1Fi4y1t7rk



3.使用上位机控制A/B路切换的视频地址;
https://www.bilibili.com/video/BV1Qp4y1Q7tE



4.设计的配电柜A/B路切换的综合演示视频地址:
https://www.bilibili.com/video/BV1rD4y1Q7Yd




六.源代码地址:
1.触摸屏源代码:按照以上步骤配置的触摸屏源代码只有一个文件:《触摸屏:RT-Thread创新大赛.HMI》,


2.东软载波RT-Thread源代码:按照以上步骤配置的源代码只有一个文件《main.c》,
其余涉及东软载波的代码直接直接从官网(http://www.essemi.com)下载即可;涉及RT-Thread的代码直接从RT-Thread官网(https://www.rt-thread.org)下载即可。



3.上位机使用VB写的,只有一个文件《上位机:RT-Thread创新大赛.frm》



七.总结:
1.东软载波板子功能很多,这个演示系统只用到:时钟、中断、ADC、GPIO、UART等几个简单的功能;
2. RT-Thread是一个功能强大的系统,这个演示系统只用到:RT-Thread内核、时钟、中断、延时等几个简单功能,RT-Thread很多高级功能和组件都没有用的,后续持续学习,争取将其完整版的高级功能和组件都尝试一把。
3.特别说明:RT-Thread的文档中心绝对值得所有程序员去学习的地方,是一个快速入手的最佳地方。



最后一个最重要的是这篇文案。


Fillmore 发表于 2020-4-29 10:17

此处应该艾特王工

21ic小喇叭 发表于 2020-5-9 16:44

感谢分享,论坛送您1000家园币,可以兑换礼品。以及论坛给您寄出一份礼品,以示鼓励~ 您继续加油,尽快把帖子更新、继续完善,上传方案文档哦~

yljon 发表于 2020-5-9 17:41

21ic小喇叭 发表于 2020-5-9 16:44
感谢分享,论坛送您1000家园币,可以兑换礼品。以及论坛给您寄出一份礼品,以示鼓励~ 您继续加油,尽快把帖 ...

谢谢小喇叭,我正在写上位机

yljon 发表于 2020-5-12 15:25

本帖最后由 yljon 于 2020-5-12 15:26 编辑

上2张图片,剧透一下{:lol:} @21ic小喇叭






yljon 发表于 2020-5-13 16:48

正式内容还没有上传,就收到礼物里,感谢 @21ic小喇叭 。

哎呀,这不得不加快更新了{:titter:}{:titter:}{:titter:}




yljon 发表于 2020-5-20 21:28

本帖最后由 yljon 于 2020-5-20 21:30 编辑



@21ic小喇叭 又是送礼物,又是加分,我都不好意思啦。就是时间十分紧张,抽空写了上位机。
这个上位机界面好丑啊{:lol:}

yljon 发表于 2020-5-27 21:01

本帖最后由 yljon 于 2020-5-27 21:05 编辑

报告 @21ic小喇叭   这个星期主要搞B站了,至少视频可以上传了:


第一个视频:使用按钮控制A/B路切换;
https://www.bilibili.com/video/BV1Qt4y1C7nz

第二个视频:使用触摸屏控制A/B路切换;
https://www.bilibili.com/video/BV1Fi4y1t7rk

第三个视频:使用上位机控制A/B路切换;
https://www.bilibili.com/video/BV1Qp4y1Q7tE

这个系统目前实现这3种控制方式。
体会:上传视频太费劲{:lol:}

21ic小喇叭 发表于 2020-6-5 11:07

大大,我看您写的非常专业,您如果完成了作品,千万别忘记按比赛要求,做成文档,上传到论坛

文档模板在咱们参赛群里有,如果没找到,联系大掌柜单独发给您

提交作品以后,大掌柜给您寄礼品

yljon 发表于 2020-6-17 23:10

在沙发贴里下载的是ESburner_1.0.0.98.zip,用在这个演示项目里也是这个版本。现在官网上最新版本是ESburner_1.0.1.3.zip,特此备注一下。

yljon 发表于 2020-6-28 20:46

本帖最后由 yljon 于 2020-6-28 20:47 编辑

@21ic小喇叭

我的文案已经全部完成了,所有的东东都在帖子的沙发层里,包括文案。
页: [1]
查看完整版本: 【RTOS】+ 基于RT-Thread与东软载波的上位机下位机开发