基于STM32F107与RT-Thread的数据采集器方案设计与解析

作者孙冬梅:南京工业大学自动化与电气工程学院博士、副教授
设计了基于stm32f107设计的数据采集器,实现多种数据(串口、can口)采集处理后通过 gprs模块 无线上传。重点编写了can设备驱动; 使用设备方式实现gprs模块串口数据的上传下载;最后提出了使用线程过程中出现的一些问题。
一、 功能分析 系统功能如图1 所示,不算太复杂。由于下级传感器模块的上报的数据内容很多,导致编写处理程序内容较多。
二、can驱动编写 为了模块化地处理传感器的主动上报数据,can设备不再用以前的中断处理,而是采用了rtt的设备框架,重新编写了device的驱动。研究rtt里的can总线收发设备:
发现只有框架,没有内容。就仿着串口写一个candevice。研究组件使用 中的串口驱动:
这是一个读代码的过程,弄清楚框架后,编写类似于linux中的驱动编写。
以上程序全部写好后,就可以使用设备通用操作函数来操作can。在主程序中首先要初始化设备,再注册设备。
三、设备方式实现串口数据处理 gprs模块使用实际上是串口数据的收到处理。首先创建gprswatch进程,用来监控串口接收数据。
void gprswatch(void){ rt_thread_t thread; thread = rt_thread_find(gprswatch); if( thread != rt_null) rt_thread_delete(thread); /* 创建gprswatch线程*/ thread = rt_thread_create(gprswatch, gprswatch_entry, rt_null, 0x1000, 0x12, 200); /* 创建成功则启动线程*/ if( thread != rt_null) { rt_thread_startup(thread); //rt_thread_delay(rt_tick_per_second/2); } } 监视gprs串口线程中,当收到串口数据后,接收并分析,置位网络状态。
/* 监视gprs串口线程入口*/void gprswatch_entry(void* parameter){ rt_err_t result = rt_eok; rt_uint32_t event; unsigned char gprs_rx_buffer[gprs_rx_len]={0x00}; while(1) { result = rt_event_recv(&rev_event, rev_mask, rt_event_flag_or | rt_event_flag_clear, rt_waiting_forever, &event); if (result == rt_eok) { if (event & rev_data) { rt_memset(gprs_rx_buffer,0x00,sizeof(gprs_rx_buffer)); rt_thread_delay(rt_tick_per_second/10); rt_device_read(gprs_device, 0, gprs_rx_buffer, gprs_rx_len); rt_kprintf(gprs_rx_buffer); /*监视gprs模块接收数据*/ if(rt_strstr((char const*)gprs_rx_buffer,myurcclose: 0))//网络断 { net_status = connect_error; rt_kprintf( 网络断。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。 ); } else if(rt_strstr((char const*)gprs_rx_buffer,call ready))//模塊重啟 { net_status = connect_null; rt_kprintf( 模塊重啟。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。 ); } else if(rt_strstr((char const*)gprs_rx_buffer,+cpin: not ready))//卡被拔出 { net_status = connect_error; rt_kprintf( 卡被拔出。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。 ); } else if(rt_strstr((char const*)gprs_rx_buffer,$myurcact: 0,0))//網絡斷開 { net_status = connect_disconnect; rt_kprintf( 网络断开。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。 ); } else if(rt_strstr((char const*)gprs_rx_buffer,myurcread: 0))//有网络数据 { net_status = connect_gprsdatain; } else if(rt_strstr((char const*)gprs_rx_buffer,+cmti:))//有短信来 { net_status = connect_msgdatain; } else { } } if (event & rev_stopwatch) { return; } } }} 在程序其它地方完成对应gprs模块的监控和操作。对gprs模块读和写操作也编写了一个设备操作函数,主要是利用前面编写的gprswatch线程操作:
/*gprs模块发送和接收*/rt_bool_t gprs_send_data_package(unsigned char *cmd,char *ack,rt_uint32_t waittime, rt_uint8_t retrytime, rt_uint32_t len){ rt_bool_t res = rt_false; rt_err_t result = rt_eok; rt_uint32_t event; unsigned char gprs_rx_buffer[gprs_rx_len]={0x00}; rt_thread_t thread; thread = rt_thread_find(gprswatch); if( thread != rt_null) { rt_thread_delete(thread); } do { rt_device_write(gprs_device, 0, cmd, len); result = rt_event_recv(&rev_event, rev_mask, rt_event_flag_or | rt_event_flag_clear, waittime*rt_tick_per_second, &event); if (result == rt_eok) { if (event & rev_data) { rt_memset(gprs_rx_buffer,0x00,sizeof(gprs_rx_buffer)); rt_thread_delay(rt_tick_per_second/2); rt_device_read(gprs_device, 0, gprs_rx_buffer, gprs_rx_len); rt_kprintf(gprs_rx_buffer); if(rt_strstr(cmd,msg_imsi))//如果是读imsi 解析出imsi数据 { unsigned char *addr; addr = rt_strstr((char const*)gprs_rx_buffer,at+cimi)+10; if(addr!=null) { strncpy(&imsi[0],addr,15); rt_kprintf(imsi = :%s ,imsi); } } if(rt_strstr(cmd,msg_imei))//如果是读imei 解析出imei数据 { unsigned char *addr; addr = rt_strstr((char const*)gprs_rx_buffer,)+1; if(addr!=null) { strncpy(&imei[0],addr,15); rt_kprintf(imei = :%s ,imei); } } if(rt_strstr(cmd,csq_cmd))//如果是读csq 解析出dbm数据 { unsigned char csq[5] = {0x00}; unsigned char *addr; rt_int16_t dbm; addr = rt_strstr((char const*)gprs_rx_buffer,,) - 3; rt_strncpy(csq, addr,3); if(addr!=null) { dbm = 2* atoi(csq) - 109; dbm_data[0] = dbm; dbm_data[1] = dbm>>8; rt_kprintf( dbm = %d ,dbm); rt_kprintf( rssi = %02x%02x ,dbm_data[0],dbm_data[1]); } } if((rt_strstr(gprs_rx_buffer,ack))||(rt_strstr(gprs_rx_buffer,ok))) { res = rt_true; if(rt_strstr(cmd,mg323_read_cmd))//如果是读数据命令,将数据拷出 { rt_memcpy(gprs_rx_data, gprs_rx_buffer, gprs_rx_len); } } else res = rt_false; } if(rt_strstr((char const*)gprs_rx_buffer,myurcread: 0))//有网络数据 { net_status = connect_gprsdatain; rt_kprintf( 收到网络数据! ); } } retrytime--; }while((!res)&&(retrytime>=1)); gprswatch(); return res;} 至此,基本实现了gprs模块的设备操作。
四、调试过程中的经验 1.进程初始化及分配内存 在rtt工程中,int rt_application_init(void) 函数给出了一个最基本的使用方法,动态创建线程rt_thread_create,动态分配内存。在程序编写的过程,由于内存太小,不得不心划分分配的内存。手册建议在程序运行过程中使用命令查看线程的占用内存,再按经验分内存,这样操作,还是地调试过程中出现很多次错误。后来再翻看手册,仿造例子修改程序为静态分配内存的线程创建,rt_thread_init,上面的错误就不再出现了。
2.使用finsh 在调试过程中大量使用了finsh, 极大地方便了调试。
引用用户手册的说明:编写了一个函数,如果不在程序中运行,便可以将此函数引出到finsh中。
在串口控制台中操作,就可以很方便地实现gprs相关函数的调试,而并需要在主程序中运行以上函数。
3.rtt例程的格式 编写了基于rtt的 stm32f107平台的例程,发布在github上:https://github.com/sundm75/stm32f107board-rttproject每个example下的 applications中,都有一个对应的 test**** 文件。该文件中,全部使用的finsh 在串口控制中操作。
- end -

半导体后面还需要几代人的努力,才能走到世界前列
亨通光电全力支持“新基建”推进,实现万物互联赋能千行百业
软通动力作为首批合作伙伴入驻北京昇腾人工智能计算中心
直流屏选型规格_直流屏在选型时要考虑的因素
光源频闪的来由科普及解决办法
基于STM32F107与RT-Thread的数据采集器方案设计与解析
综合实力超群的静态代码测试工具Klocwork 2022.4 中的新增功能
当心!你的汽车GPS导航系统会引贼上门
Adam Taylor玩转MicroZed系列73:用其他的Zynq
网卡分解结构
美容仪什么牌子好,高效护肤 助力美丽容颜
亿纬锂能荣膺“2023年度企业奖”
诺安智能获批设立博士后创新实践基地
新基建位列“两新一重”首位
网上爆出小米MIX 3新技能,上滑可解锁小米笔记本
Type-C音频转接器专用芯片LDR6023SS概述
Silicon Labs突破未来IoT发展瓶颈
iPhoneXR和iPhoneXs的区别
智能魔镜,让你拥有控制全屋的神奇魔法!
小米迎来第二春,Q3全球智能机销量增长80%排名第五