記事ディレクトリ
序文
1. 要件:
1. MCU データ取得とホスト コンピューター通信:
STM32F103 開発ボード + AHT20 を使用して、複数の監視ポイントの温度と湿度のデータ取得をシミュレートします。シリアル ポート/485 バスを使用して、取得端末とホスト コンピューター間のデータ転送を完了します。上のコンピューターは Linux システムの Raspberry Pi 3B+ を実行できます (Raspberry Pi にはシリアル ポートと有線および無線ネットワークがあります)。本物の Raspberry Pi がなくても、PC でシミュレートし、標準の C 言語または Python プログラミングで実現できます。
2. センサー シミュレーション データのリモート取得:
研究内容には、RS485 バスおよび ModBus 通信プロトコル、データベース技術、ネットワーク通信プログラミング開発が含まれます。
上位コンピュータは、UDP/TCP ソケットと Modbus アプリケーション層プロトコルを介して、少なくとも 2 種類のデータ (温度、湿度、ストレスなど) のリモート定期的 (30 秒など) リアルタイム収集と保存を完了します。収集されたデータは Mysql データベースに書き込まれ、Mysql はローカル PC の Win10 または Ubuntu システムにインストールできます。収集データ レコードには、少なくともタイム スタンプ、収集ポイント番号、収集データ タイプ、収集データ情報が含まれます。ソフトウェア開発のためのプログラミングソフトウェアの使用に制限はありません。
3. 思考:
実際のエンジニアリング アプリケーションにおけるマルチセンサー、マルチプロトコル、およびマルチネットワーク方式の複雑で変化しやすい技術要件に対して、FPGA 開発ボード プログラミングの柔軟なカスタマイズ機能を使用してさまざまなインターフェイス プロトコルを設計および実装する場合、合理的ですか?セックス? 長所と短所は何ですか? 簡単に比較して分析してください。
注: 主にスキームの設計とプログラミングのデバッグです。
2. システムブロック図
2.1 全体システムブロック図
2.2. stm32 は、AHT20 を介して温度と湿度のブロック図を収集します。
このモジュールには AHT20 温度および湿度センサーを使用し、I2C を介して stm32 と通信し、ModBus プロトコルを介して stm32 をアップロードおよび通信する予定です。
2.3. modbus プロトコルを介してホスト コンピューターと通信する stm32 のブロック図:
ホスト コンピュータは Modbus プロトコルを使用してコマンドを送信し、スレーブからデータを受け取ります。同時に、ホスト コンピュータは、受信したデータをデータベースに保存し、配置されたブリッジ センサーを特定の dmo-monitor.igong.com から読み取る必要があります。データ。
3. ModBus プロトコル
上記のシステムブロック図からわかるように、modbus プロトコルをより多く使用しています。ここでは、modbus プロトコルの内容について説明します。
1. プロトコルの概要
1. Modbus プロトコルは、1979 年に Modicon (現 Schneider Electric) によって発行された、プログラマブル ロジック コントローラ (PLC) 通信を使用するためのシリアル通信プロトコルです。Modbusプロトコルは、産業分野の通信プロトコルの業界標準となったアプリケーション層プロトコルであり、産業用電子機器間の一般的な接続方式です。
2. Modbus はマスター/スレーブ アーキテクチャ プロトコルです. 1 つのノードがマスター ノードであり, Modbus プロトコルを使用して通信に参加する他のノードはスレーブ ノードです. 各スレーブ デバイスには一意のアドレスがあります. マスター ノードとして指定されたノードのみがコマンドを開始できます。すべての Modbus データ フレームには、伝送の正確性を保証するためのチェック コードが含まれています。基本的な ModBus コマンドは、スレーブ デバイスにそのレジスタの 1 つの値を変更し、I/O ポートを制御または読み取り、1 つまたは複数のレジスタのデータを送り返すようにデバイスに指示できます。
2. Modbus マスター/スレーブプロトコルの原理
Modbus シリアル リンク プロトコルは、マスター/スレーブ プロトコルです。同時に、バスに接続できるマスター ステーションは 1 つだけで、同じシリアル バスに 1 つまたは複数のスレーブ ステーション (最大数 247) を接続できます。Modbus 通信は常にマスタ ステーションから開始され、スレーブ ステーションがマスタ ステーションからの要求を受信しない場合、データは送信されません。マスタ ステーションは同時に 1 つの Modbus トランザクションしか開始できず、スレーブ ステーションは相互に通信できません。
3. 一般的な Modbus フレーム構造 - プロトコル データ ユニット (PDU)
Modbus プロトコルは、基礎となる通信層から独立した単純なプロトコル データ ユニット (PDU) を定義します. 特定のバスまたはネットワーク上の Modbus プロトコル マッピングは、アプリケーション データ ユニット (ADU) にいくつかの追加フィールドを導入できます.
4. 2 つの Modbus シリアル伝送モード
RTU モード: 各 8 ビット バイトには 2 つの 4 ビット 16 進文字が含まれます.利点は、同じボー レートで、ASCII よりも多くのデータを送信できることですが、各メッセージは連続したデータ ストリーミングでなければなりません.
ASCII モード: 情報の 8 ビット バイトごとに 2 つの ASCII 文字が必要です。これには、文字の送信間隔をエラーなしで 1 秒にできるという利点があります。
5. ModbusTCP 通信構造
Modbus TCP/IP 用の通信デバイス: TCP/IP ネットワークに接続された Modbus TCP/IP クライアントおよびサーバー デバイス。
TCP/IP ネットワークとシリアル リンク サブネット間を相互接続するブリッジ、ルーター、ゲートウェイなどの相互接続デバイス。
6. システムに関わる通信プロトコル命令
7. 一部のプロトコル機能コード
1. MCU データ取得とホストコンピュータ通信
1. 温度と湿度の収集 (I2C プロトコル)
温度と湿度の読み取りモジュール ここで使用するセンサーは、AHT20 と I2C プロトコルです。I2C 通信プロトコル (Inter-Integrated Circuit) は Philps によって開発されました. ピン数が少なく、ハードウェアの実装が簡単で、スケーラビリティが高いため、USART や CAN などの外部トランシーバ デバイスを必要としません. 現在、相互通信で広く使用されてい
ますシステム内の複数の集積回路 (IC)。
1.電源を入れた後、温度と湿度を読み取る前に、しばらく待ってデバイスが正常に動作するのを待ちます。
MX_GPIO_Init();
MX_I2C1_Init();
MX_USART1_UART_Init();
uint32_t CT_data[2]={
0,0};
volatile int c1,t1;
rt_thread_delay(50);
AHT20_Init();
rt_thread_delay(2500);
2. CRC チェック後に温度と湿度のデータを読み取り、5 分間遅延します。
AHT20_Read_CTdata_crc(CT_data); //crc校验后,读取AHT20的温度和湿度数据
c1 = CT_data[0]*100*10/1024/1024; //计算得到湿度值c1(放大了10倍)
t1 = CT_data[1]*200*10/1024/1024-500;//计算得到温度值t1(放大了10倍)
printf("湿度:%d%s",c1/10,"%");
printf("温度:%d%s",t1/10,"℃");
printf("\r\n");
rt_thread_mdelay(300000);//线程睡眠大约5分钟,挂起执行其他操作
2. ホストコンピュータ通信
ここでの要件は、複数の監視ポイントでの温度と湿度のデータの収集をシミュレートすることですが、ここでは、RT スレッド Nano を移植するマルチタスク フレームワークを採用して、他のセンサーの正常な動作を防ぐことができます。
メイン関数で Modbus プロトコルを有効にし、RT-Thread プロセスを初期化します。
eMBInit( MB_RTU, 0x01, 1, 115200, MB_PAR_NONE);//初始化modbus,走modbusRTU,从站地址为0x01,端口为1。
eMBEnable();//使能modbus
MX_RT_Thread_Init(); //初始化 RT-Thread 进程
AHT20 コードと modbus ドライバー ファイルを構成したら、関連するヘッダー ファイルをインポートします。
#include "rtthread.h" // RT-Thread 头文件
#include "mb.h" // Modbus 头文件
#include "mbport.h" // Modbus 端口头文件
#include "AHT20.h" // AHT20传感器驱动头文件
extern void MX_RT_Thread_Init(void); // RT-Thread 初始化函数,初始化并执行各种进程
extern void MX_RT_Thread_Process(void); // RT-Thread 主进程
マルチスレッドをオンにします。メイン タスクは modbus の監視であり、サブタスクはプログラムのテストです。
#include "rtthread.h"
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
#include "stdio.h"
#include "AHT20.h"
#include "mb.h"
#include "mbport.h"
struct rt_thread led1_thread;
rt_uint8_t rt_led1_thread_stack[128];
void led1_task_entry(void *parameter);
//初始化线程函数
void MX_RT_Thread_Init(void)
{
//初始化LED1线程
rt_thread_init(&led1_thread,"led1",led1_task_entry,RT_NULL,&rt_led1_thread_stack[0],sizeof(rt_led1_thread_stack),3,20);
//开启线程调度
rt_thread_startup(&led1_thread);
}
//主任务
void MX_RT_Thread_Process(void)
{
(void)eMBPoll(); //启动modbus监听
}
//LED1任务
void led1_task_entry(void *parameter)
{
while(1)
{
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13, GPIO_PIN_RESET);
rt_thread_delay(500);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13, GPIO_PIN_SET);
rt_thread_delay(500);
}
}
eMBRegInputCB 関数の関数コードを 04 に変更して、入力レジスタを読み取ります。
eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
eMBErrorCode eStatus = MB_ENOERR;
int iRegIndex;
int i;
InputBuff[0] = 0x11;
InputBuff[1] = 0x22;
InputBuff[2] = 0x33;
InputBuff[3] = 0x44;
if( ( usAddress >= REG_INPUT_START ) && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
{
iRegIndex = ( int )( usAddress - usRegInputStart );
for(i=0;i<usNRegs;i++)
{
*pucRegBuffer=InputBuff[i+usAddress-1]>>8;
pucRegBuffer++;
*pucRegBuffer=InputBuff[i+usAddress-1]&0xff;
pucRegBuffer++;
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
出力関数 eMBRegInputCB で、返された温度と湿度のデータを出力用に InputBuff[] 配列に格納します。
eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
eMBErrorCode eStatus = MB_ENOERR;
int iRegIndex;
int i;
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET); // 使用 PC13 引脚上的板载小灯泡进行测试——小灯泡亮
AHT20_Read_CTdata_crc(CT_data); //经过CRC校验,读取AHT20的温度和湿度数据 推荐每隔大于1S读一次
c1 = CT_data[0]*1000/1024/1024; //计算得到湿度值c1(放大了10倍)
t1 = CT_data[1]*2000/1024/1024-500;//计算得到温度值t1(放大了10倍)
t2 = t1/10 + (t1/10)%10; //温度的整数部分
t3 = t1%10; //温度的小数部分
c2 = c2/10 + (c1/10)%10; // 湿度的整数部分
c3 = c3%10; //湿度的小数部分
InputBuff[0] = t2;
InputBuff[1] = t3;
InputBuff[2] = c2;
InputBuff[3] = c3;
if( ( usAddress >= REG_INPUT_START ) && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
{
iRegIndex = ( int )( usAddress - usRegInputStart );
for(i=0;i<usNRegs;i++)
{
*pucRegBuffer=InputBuff[i+usAddress-1]>>8;
pucRegBuffer++;
*pucRegBuffer=InputBuff[i+usAddress-1]&0xff;
pucRegBuffer++;
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
手元に AHT20 モジュールがないため、効果のデモは実行できません. ここでは、プロセスの一般的な説明といくつかの重要なモジュール コードの表示のみを示します.
2. センサーシミュレーションデータの遠隔収集
1.TCPソケットとmodbusプロトコル
TCP プロトコルを介してリモート データ サービスとの接続を確立する内容では、主にブリッジの応力と温度の収集が完了します。
crc チェック:
def crc16(string):
#data = bytes.fromhex(string)
data=string
crc = 0xFFFF
for pos in data:
crc ^= pos
for i in range(8):
if ((crc & 1) != 0):
crc >>= 1
crc ^= 0xA001
else:
crc >>= 1
return hex(((crc & 0xff) << 8) + (crc >> 8))
データベース接続:
def MySQLConnect():
connection = pymysql.connect(
host='localhost', # IP,MySQL数据库服务器IP地址
port=3306, # 端口,默认3306,可以不输入
user='root', # 数据库用户名
password='密码', # 数据库登录密码
database='sensor', # 要连接的数据库
charset='utf8' # 字符集,注意不是'utf-8'
)
return connection
データベースにデータを挿入します。
def AddData(num,yb,wd,time):
# 连接数据库
conn = MySQLConnect()
# 使用cursor()方法创建一个游标对象cursor
cursor = conn.cursor()
# 插入数据库
sql = "INSERT INTO strain_sensor(id ,mic, strain_temp, time) VALUES (%s,%s,%s,%s); "
cursor.execute(sql, [num,yb, wd, time])
# 提交事务
conn.commit()
# 关闭游标
cursor.close()
# 关闭数据库连接
conn.close()
データを取得します:
def getStain(cmd,num,time):
#print(cmd)
#print(num)
cmd = bytes.fromhex(cmd)
crc = crc16(cmd)
crc = bytes.fromhex(crc[2:])
cmd = cmd + crc
#print(cmd)
#发送对应的指令
tcp.send(cmd)
try:
data = tcp.recv(8192)
except socket.timeout:
print("超时")
sys.exit(1)
crc = data[-2:]
crc1 = crc16(data[:-2])
crc1 = crc1[2:]
if len(crc1) == 3:
crc1 = '0' + crc1
crc1 = bytes.fromhex(crc1)
if crc != crc1:
print("CRC16校验失败!")
sys.exit(2)
yb, wd = struct.unpack('>ii', data[4:12])
yb = yb / 100.0
wd = wd / 100.0
print("应变:", yb, "温度:", wd)
print(time)
yb = str(yb)
wd = str(wd)
AddData(num, yb, wd, time)
TCP 接続を確立します。
効果:
No. 3 センサーがひずみ力と温度情報を収集:
データベース:
2.UDPソケットとmodbusプロトコル
UDPプロトコルを用いて遠隔橋梁監視データサービスシステムからデータを取得する内容で、温度センサーの収集と静的レベルの情報収集を実現しました。
CRC チェック:
def crc16(string):
#data = bytes.fromhex(string)
data=string
crc = 0xFFFF
for pos in data:
crc ^= pos
for i in range(8):
if ((crc & 1) != 0):
crc >>= 1
crc ^= 0xA001
else:
crc >>= 1
return hex(((crc & 0xff) << 8) + (crc >> 8))
データベース接続:
def MySQLConnect():
connection = pymysql.connect(
host='localhost', # IP,MySQL数据库服务器IP地址
port=3306, # 端口,默认3306,可以不输入
user='root', # 数据库用户名
password='password', # 数据库登录密码
database='sensor', # 要连接的数据库
charset='utf8' # 字符集,注意不是'utf-8'
)
取得した温度と湿度のタイムスタンプをデータベースに挿入します。
def AddData1(wd,sd,time):
# 连接数据库
conn = MySQLConnect()
# 使用cursor()方法创建一个游标对象cursor
cursor = conn.cursor()
# 插入数据库
sql = "INSERT INTO temp_hum_sensor(temp, hum, time) VALUES (%s,%s,%s); "
cursor.execute(sql, [wd, sd, time])
# 提交事务
conn.commit()
# 关闭游标
cursor.close()
# 关闭数据库连接
conn.close()
取得したセンサー ID、偏差、タイムスタンプはデータベースに保存されます。
def AddData2(id,water_level,time):
# 连接数据库
conn = MySQLConnect()
# 使用cursor()方法创建一个游标对象cursor
cursor = conn.cursor()
# 插入数据库
sql = "INSERT INTO static_level(id, water_level, time) VALUES (%s,%s,%s); "
cursor.execute(sql, [id, water_level, time])
# 提交事务
conn.commit()
# 关闭游标
cursor.close()
# 关闭数据库连接
conn.close()
温度と湿度の収集プロセス (modbus):
def getDataTemp(cmd):
#flag标志采集的次数
flag=0
last = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
print(last)
last1 = time.time()
cmd = bytes.fromhex(cmd)
#print(cmd)
crc = crc16(cmd)
crc = bytes.fromhex(crc[2:])
#得到发送的指令(modbus协议定义内容+校验)
cmd = cmd + crc
udp.sendto(cmd, ('demo-monitor.igong.com', 8001))
try:
data, addr = udp.recvfrom(8192)
except socket.timeout:
print("超时")
sys.exit(1)
crc = data[-2:]
crc1 = crc16(data[:-2])
crc1 = crc1[2:]
if (len(crc1) == 3):
crc1 = '0' + crc1
crc1 = bytes.fromhex(crc1)
# print(crc1)
if crc != crc1:
print("CRC16校验失败!")
sys.exit(2)
# 解析数据
wd, sd = struct.unpack('>ii', data[4:12])
wd = wd / 100.
print("温度:", wd, "湿度:", sd)
AddData1(wd, sd, last)
flag=flag+1
while True:
now= time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
#print(s)
now1=time.time()
#每隔5s获取一次数据
if(now1-last1>5):
udp.sendto(cmd, ('demo-monitor.igong.com', 8001))
try:
data, addr = udp.recvfrom(8192)
except socket.timeout:
print("超时")
sys.exit(1)
crc = data[-2:]
crc2=bytes.hex(crc)
#print(crc2)
crc1 = crc16(data[:-2])
crc1=crc1[2:]
if(len(crc1)==3):
crc1='0'+crc1
#print(crc1)
crc1=bytes.fromhex(crc1)
#print(crc1)
if crc != crc1:
print("CRC16校验失败!")
sys.exit(2)
#解析数据
wd, sd = struct.unpack('>ii', data[4:12])
wd = wd / 100.0
#当前时间
print(now)
#获取得到的数据
print("温度:", wd, "湿度:", sd)
last=now
last1=now1
wd=str(wd)
sd=str(sd)
AddData1(wd,sd,now)
flag = flag + 1
if flag >= 5:
str1 = input("请选择是否继续采集(y表示继续,n表示退出):")
if str1 == 'y':
flag = 0
continue
else:
break
センサーを選択:
def getDataStaticLevel(cmd):
id=cmd[0:2]
#print(id)
if id=='02':
#print("2号")
id='00'+id
getData(id,cmd)
elif id=='03':
#print("3号")
id = '00' + id
getData(id,cmd)
elif id=='04':
#print("4号")
id = '00' + id
getData(id,cmd)
elif id=='05':
#print("5号")
id = '00' + id
getData(id,cmd)
リモート データとの接続を確立するには:
効果:
温度 湿度 モジュール:
データベース:
たわみ:
データベース:
3. 考える質問
実際のエンジニアリング アプリケーションにおけるマルチセンサー、マルチプロトコル、およびマルチネットワーク方式の複雑で変化しやすい技術要件に対して、FPGA 開発ボード プログラミングの柔軟なカスタマイズ機能を使用して、さまざまなインターフェイス プロトコルを設計および実装することは合理的ですか? 長所と短所は何ですか? 簡単に比較して分析してください。
fpga 自体のプログラミングの柔軟性とデータ処理能力は、彼が複数のプロトコルを実装するのをサポートするのに十分であるため、妥当だと思います。利点: 現在のミッドレンジ FPGA は、既にさまざまなインターフェイス プロトコルを試しています。2 つ目は、FPGA 処理情報を取得できることです。短所: 完全なプロトコル スタックを構築するには、より多くの費用がかかり、多くの実験を行う必要があります. 合格したとしても、情報セキュリティにはまだ危険が隠されています.
4.まとめ
このプロジェクトの設計で最も重要なプロトコルは modbus で、以前の割り当てで使用されていましたが、この設計プロセスで理解と親しみが深まりました。ハードウェアリソースのサポートが不足しているため、最初の部分の内容は主に以前に実現した宿題に基づいており、説明と復習で十分です。リモート検出データ サービスへの接続の 2 番目の部分では、UDP\TCP の 2 つの接続方法が使用されますが、最も重要なことは、コードで modbus 通信の実装プロセスを理解することです。このデザインは modbus の研究から多くのものを得ました!