物联网笔记2

tcp_write() errors on snd_queuelen
作者 codercjg23 三月 2016, 4:21 下午

stm32f107+lwip1.3.1长时间实时上传数据, 当lwip tcp client连续高速向PC server发送数据时,tcp_write()失败,跟踪进入发现snd_queuelen超出限制,而实际发送队列根本没满。
snd_queuelen是tcp发送队列中包的数目, tcp_enqueue()增加snd_queuelen的长度,tcp_receive()收到包的Ack后,减短snd_queuelen。
使用stm32例程时,tcp_receive()是在ethernet接收中断中间接调用的,程序主循环中tcp_write()间接调用tcp_receive()。
中断上下文中tcp_receive()和主循环中tcp_enqueue()对pcb->snd_queuelen的操作引起了竞态。因为裸跑没有操作系统,不能加锁,也不打算关中断。
解决办法:
文件tcp_out.c函数tcp_enqueue()中修改两个地方:
1)213行
useg = queue = seg = NULL;
seglen = 0;
queuelen = 0; /* add by chengjg */

2)401行
/* update number of segments on the queues /
pcb->snd_queuelen += queuelen; /
pcb->snd_queuelen = queuelen; edit by chengjg /
之后就跑得很稳定了, 连续跑了3个多小时候抓包出现out of order错误,原来seqno也被改了。
所以正确的做法是
进入tcp_enqueue()后先关网络接收中断,出tcp_enqueue()前开网络接收中断*,防止执行tcp_enqueue()期间,相关变量在中断里被修改导致程序出bug。
具体过程分析可参考一老外遇到的情况http://lwip.100.n7.nabble.com/tcp-write-errors-on-snd-queuelen-td8599.html
部分内容如下:
I did change the data I am sending to more human readable data and found that all the data is going out in WireShark until the tcp_write returns the error. I spent a lot of time digging into this and finally found the problem. I am hoping you can help me determine if it is something I am doing wrong or Texas Instruments or lwip. First, below is a portion of the attached log file from lwip running with the TCP_QLEN_DEBUG enabled. … [line 3642] tcp_enqueue: 37 (after enqueued) tcp_enqueue: queuelen: 37 tcp_enqueue: 38 (after enqueued) tcp_receive: queuelen 38 … 23 (after freeing unacked) tcp_receive: queuelen 23 … 0 (after freeing unacked) tcp_enqueue: queuelen: 0 tcp_enqueue: 1 (after enqueued) tcp_enqueue: queuelen: 1 … … [line 3764] tcp_enqueue: 59 (after enqueued) tcp_enqueue: queuelen: 59 tcp_receive: queuelen 59 … 35 (after freeing unacked) tcp_receive: queuelen 35 … 11 (after freeing unacked) tcp_enqueue: 60 (after enqueued) tcp_enqueue: queuelen: 60 tcp_enqueue: 61 (after enqueued) tcp_enqueue: queuelen: 61 tcp_enqueue: 62 (after enqueued) tcp_enqueue: queuelen: 62 … … [line 3853] tcp_enqueue: queuelen: 102 tcp_enqueue: 103 (after enqueued) pcb->nrtx > 12 tcp_enqueue: queuelen: 103 tcp_enqueue: 104 (after enqueued) tcp_enqueue: queuelen: 104 tcp_enqueue: 105 (after enqueued) tcp_enqueue: queuelen: 105 tcp_enqueue: 106 (after enqueued) tcp_enqueue: queuelen: 106 tcp_enqueue: 107 (after enqueued) tcp_receive: queuelen 107 … 95 (after freeing unacked) tcp_receive: queuelen 95 … 71 (after freeing unacked) tcp_receive: queuelen 71 … 48 (after freeing unacked) tcp_receive: valid queue length tcp_enqueue: queuelen: 48 tcp_enqueue: pbufs on queue => at least one queue non-empty tcp_enqueue: 49 (after enqueued) tcp_receive: queuelen 49 … 48 (after freeing unacked) tcp_receive: valid queue length Normally, we see the tcp_receive prefix take out queues and usually to 0 (though not always – line 3241 in file). However, at line 3765, the tcp receive interrupt went off during a tcp_write (tcp_enqueue). We found that in the tcp_write, the queue length is read near the beginning of the function into a local variable and then stored back into the global variable toward the end of the function (see below). From lwip version 1.3.2 in the tcp_out.c file: Line 195 queuelen = pcb->snd_queuelen; Line 411 pcb->snd_queuelen = queuelen; As you can see from line 3765 of the log file, the tcp_receive removed queues from the buffer after the tcp_enqueue had read the value to process. It then sets the queue length at the end to the internally modified local value. So when we get to line 3866, the queue length is incorrect and the value will not ever get back to a zero. If this situation happens enough over time, eventually it will reach the TCP_SND_QUEUELEN limit and not function any longer. So it explains why I don’t see missed packets on WireShark as it is an lwip variable that is getting set wrong. I assume this would affect the number of pbufs in use after this point. It would seem to me that the tcp_enqueue function should only add to the global value the number of packets that it uses and not resave the entire value over the global to something that could now be old. Do you agree? Is there something else in the lwipopts.h file that I may not have configured correctly that is suppose to prevent this? I am using a TI Cortex-M3 Stellaris port of the lwip code.

分类: lwip协议栈 | 评论

用python实现TCP Server
作者 codercjg21 三月 2016, 4:27 下午

之前调试stm32以太网实时上传数据时,需要一个上位机的Server端进行测试。
比较了之后决定选择python,因为它比VC和java更方便。它的函数库比较强,学习曲线也比较平缓,几句代码就能实现其他语言几十甚至几百行的功能。贴出来,方便以后回头复习。
import socket
import time
import struct
def GetNowTime():
return time.strftime(“%Y-%m-%d %H:%M:%S”, time.localtime(time.time()))

def GetNowTimeX():
return time.strftime(“%Y-%m-%d %H-%M-%S”, time.localtime(time.time()))

def Log(msg, f):
time = GetNowTime()
print “%s %s”%(time, msg)
f.write(“%s %s\n”%(time, msg))
f.flush()

def HanldeData(conn, fdata, flog):
Log(“begin receive ecg data”, flog)
global seq
while 1:
try:
data = conn.recv(36)
except:
Log(“detect client off-line”, flog)
break;

if not data:

Log(“receive error”, flog)

break
if len(data)<36:
print “recv error”
break

results = struct.unpack(“iiiiiiiii”, data)
seq +=1
print GetNowTime(),
fdata.write(GetNowTime())
for result in results:
print “%10d “%result,
fdata.write(“%10d “%result)
print “”
fdata.write(“\n”)
fdata.flush()

if name == “main”:
host = “”
port = 4 # tcp server port
seq = 0
fdata = file(“ecg_%s.txt”%GetNowTimeX(), “w”) # data file
flog = file(“log_%s.txt”%GetNowTimeX(), “w”) # log file
Log(“server start”, flog)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
s.ioctl(socket.SIO_KEEPALIVE_VALS, (1, 4, 2))
s.bind((host, port))
s.listen(4)
Log(“listen on tcp port 4″, flog)
try:
while 1:
Log(“waiting client to connect”, flog)
conn,addr = s.accept()
Log(“connected by %s”%str(addr), flog)

HanldeData(conn, fdata, flog)

conn.close()
Log(“close the client”, flog)
finally:
Log(“server stop”, flog)
conn.close()
fdata.close()
flog.close()

分类: Python | 评论

心电图机
作者 codercjg21 三月 2016, 3:20 下午

12导连心电图机有10个电极,电极名称分别为RA、LA、LL(F)、RL(N)和V1 V2 V3 V4 V5 V6,导联名称为
I II II aVR aVL aVF C1 C2 C3 C4 C5 C6, 如下图所示



12个导连为电极间的电势差,其中RA、LA和LL三个电极的平均值称作威尔逊中心Vw, 12导联名称和各个导连值计算方法如下:
Vw = (RA+LA+LL)/3
I = LA-RA
II=LL-RA
III=LL-LA
aVR = RA-(LA+LL)/2 = 3/2(RA-Vw)
aVL = LA-(LA+LL)/2= 3/2(LA-Vw)
aVF = LL-(LA+LL)/2= 3/2(LL-Vw)
C1=V1-Vw
C2=V2-Vw
C3=V3-Vw
C4=V4-Vw
C5=V5-Vw
C6=V6-Vw
导联值其实就是电势差
加权系数误差:
心电图机检查时有这一项,给R导联加一个3MV的正弦波,然后看aVR(3MV)、aVL(1.5MV)、aVF(1.5MV)、C1-C6(1MV)的值是否在允许的误差范围之内(+-10%)

分类: Medical | 评论

STM32串口ISP烧写固件和IAP升级固件
作者 codercjg21 三月 2016, 2:50 下午

ISP:
拉高stm32 MCU boot0,拉低reset脚延时,然后拉高reset脚,MCU复位从bootloader启动,该bootloader支持串口对stm32 MCU Flash进行擦除、读写等操作。
ISP协议可参考ST官方文档AN3155,ISP工具可参考flash_loader_demo_v2.8.0.exe,安装目录下也有该工具相关的源代码,可以参考下。
如果自己实现ISP下载工具,通过RS232流控脚控制Stm32 MCU 引脚Boot0和Reset 烧写stm32 Flash是一种比较好的ISP烧写方式。
要注意的是,像去读保护命令和去写保护命令执行完后会复位MCU的,这时需要重新发送0x7F进入ISP模式。
上位机可用C#的SerialPort类控制串口, SerialPort.RtsEnable 和 SerialPort.DtrEnable可直接拉高或拉低RS232的RTS脚和DTR脚。
去除读保护后,会擦除整片Flash;加读保护后,JLink等无法调试和读出Flash内容,可防止产品代码被复制。
IAP:
当STM32 MCU Flash中已存在通过ISP方式烧写的固件,若要进行升级,可通过IAP方式升级。把STM32 Flash分成IAP bootloader和APP区。
IAP升级相当于擦掉Flash APP区代码,然后通过串口或者SD卡等读取要升级的代码,烧写到Flash APP区替换原来的部分完成升级。
要注意的地方:
1.APP区代码需要设置中断向量表 NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0×9000);
2.要更改起始烧写地址


分类: Stm32 | 评论

STM32F107使用LWIP协议栈
作者 codercjg21 三月 2016, 2:19 下午

STM32F107自带一个以太网口,官网的例子和实际项目还是有一定差距的。
有几点要注意的地方:
1.官网的例子如果不插网线上电,之后再插网线是一直连不上的,实际使用中需要读取phy寄存器PHY_BSR来判断网线是否插入和断开
2.PHY_ADDRESS默认为0,这个得根据phy地址线上电时的值决定,实在不行也可以通过读取phy id 的方式确定地址是多少

  1. Ethernet DMA接收缓存ETH_RXBUFNB 和发送缓存ETH_TXBUFNB的长度需根据需要进行修改
    4.需要设置回掉函数
    建立连接:tcp_connect(pcb, &serveraddr, TCP_PORT, tcp_client_connected);
    收到数据:tcp_recv(pcb, tcp_client_recv);
    出错:tcp_err(pcb, tcp_error);
    5.可开启心跳包机制LWIP_TCP_KEEPALIVE,通过定期发送心跳包,是否收到ACK检测tcp异常的发生(如突然拔网线等)
    6.发送数据前需检查缓冲区剩余长度,tcp_write()只是把数据包加入发送队列,实际发送动作由tcp_output()执行。
    if((tcp_sndbuf(TcpPCB) > len) && (tcp_write(TcpPCB, data, len, TCP_WRITE_FLAG_MORE) == ERR_OK)) {
    /* send current data /
    tcp_output(TcpPCB);
    } else {
    /
    send data in tcp queue */
    tcp_output(TcpPCB);
    }

分类: Stm32 | 评论

STM32F107使用LWIP协议栈
作者 codercjg21 三月 2016, 2:18 下午

STM32F107自带一个以太网口,官网的例子和实际项目还是有一定差距的。
有几点要注意的地方:
1.官网的例子如果不插网线上电,之后再插网线是一直连不上的,.实际使用中需要读取phy寄存器PHY_BSR来判断网线是否插入和断开
2.PHY_ADDRESS默认为0,这个得根据phy地址线上电时的值决定,实在不行也可以通过读取phy id 的方式确定地址是多少

  1. Ethernet DMA接收缓存ETH_RXBUFNB 和发送缓存ETH_TXBUFNB的长度需根据需要进行修改
    4.需要设置回掉函数
    建立连接:tcp_connect(pcb, &serveraddr, TCP_PORT, tcp_client_connected);
    收到数据:tcp_recv(pcb, tcp_client_recv);
    出错:tcp_err(pcb, tcp_error);
    5.可开启心跳包机制LWIP_TCP_KEEPALIVE,通过定期发送心跳包,是否收到ACK检测tcp异常的发生(如突然拔网线等)
    6.发送数据前需检查缓冲区剩余长度,tcp_write()只是把数据包加入发送队列,实际发送动作由tcp_output()执行。
    if((tcp_sndbuf(TcpPCB) > len) && (tcp_write(TcpPCB, data, len, TCP_WRITE_FLAG_MORE) == ERR_OK)) {
    /* send current data /
    tcp_output(TcpPCB);
    } else {
    /
    send data in tcp queue */
    tcp_output(TcpPCB);
    }

分类: lwip协议栈 | 评论

STM32读保护和写保护
作者 codercjg8 三月 2016, 5:30 下午

所有STM32的芯片都提供对Flash的保护,防止对Flash的非法访问–写保护和读保护。 读保护是作用于整个Flash存储区,一旦设置了Flash的读保护,内置的Flash存储区只能通过程序的正常执行才能读出,而不能通过下述任何一种方式读出: 通过调试器(JTAG或SWD) 从RAM中启动并执行的程序 写保护是以四页(1KB/页)Flash存储区为单位提供保护,对被保护的页实施编程或擦除操作将不被执行,同时产生操作错误标志。 以下是一个简单的小结: 读保护 写保护 对Flash的操作功能 有效 有效 CPU只能读; 禁止调试和非法访问 有效 无效 CPU可以读写; 禁止调试和非法访问;页0~3为写保护 无效 有效 CPU可读; 允许调试和非法访问 无效 无效 CPU可以读写; 允许调试和非法访问
设置为读保护后就不能用调试器调试了,解除读保护时,会擦除整片flash。
产品出厂时直接通过ISP命令设置读保护,别人就不能读出Flash上的内容,可防止产品被抄板。

分类: Stm32 | 评论

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容

  • 5/15/2017 7:06:35 PM 纵观各大组件,配置文件占据极其重要的地位。可配置化也是当下开发的一流行趋...
    爱做梦的胖子阅读 4,422评论 0 8
  • ping error: Destination off-link but no route作者 codercjg ...
    codercjg阅读 1,035评论 0 1
  • # redis 配置文件示例 # 当你需要为某个配置项指定内存大小的时候,必须要带上单位, # 通常的格式就是 1...
    iyimao阅读 793评论 0 2
  • 那天,方潇潇坐在座位上喝着林小男刚给她买的矿泉水。 这时,陈墨轩跑了过来…… “潇潇,我们分手吧!”陈墨轩双手叉腰...
    千陌笙言阅读 275评论 0 0
  • 一览! 装个chrome扩展就更好了! http://www.yilan.io/home
    鸭梨山大哎阅读 309评论 1 2