【uart】完善了 uart 实际使用的样例说明。

This commit is contained in:
junhuanchen
2022-07-12 11:17:07 +08:00
parent e31ea1d45d
commit 5da57e0b65

View File

@@ -51,11 +51,7 @@
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"vscode": {
"languageId": "python"
}
},
"metadata": {},
"outputs": [
{
"name": "stdout",
@@ -94,15 +90,305 @@
"\n",
"## UART 用途\n",
"\n",
"这是操作系统的标准 URAT可以和单片机进行串口通讯也可以对带有串口协议的设备、外设通讯。"
"这是操作系统的标准 URAT可以和单片机进行串口通讯也可以对带有串口协议的设备、外设通讯。\n",
"\n",
"由于太多零基础的同学在使用,我这里就介绍一下主流的传输方式和使用方法。\n",
"\n",
"### 传递 ASCII 字符串。\n",
"\n",
"如示例代码的 `ser.write(b\"Hello Wrold !!!\\n\")` 意思就是传递 bytes 字符串,通常出现在 json 或 python 代码作为协议传输的场合,相比其他协议简单易懂,这里用下述代码为例。\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"b'0'\n",
"b'1.200000'\n",
"b'hello'\n",
"0 1.2 b'hello'\n"
]
}
],
"source": [
"\n",
"try:\n",
" a = 0\n",
" b = 1.2\n",
" c = b\"hello\"\n",
" send = b\"%d,%f,%s\\n\" % (a, b, c)\n",
" # 假设串口 Tx Rx 接在一起。\n",
" # ser.write(send)\n",
" # recv = ser.readline() # 读取以 \\n 结尾字符串。\n",
" recv = send\n",
" result = recv.replace(b\"\\n\", b\"\").split(b',')\n",
"except Exception as e:\n",
" print(e)\n",
" result = []\n",
"\n",
"for i in result:\n",
" print(i)\n",
"\n",
"a = int(result[0])\n",
"b = float(result[1])\n",
"c = result[2]\n",
"print(a, b, c)\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 传递 HexString 字符串。\n",
"\n",
"前一种可读性好,但实际传输效率低,因为内容都是可读的 ASCII 字符,而实际场景下比较常用的 modbus ymodem 等与其他芯片沟通的协议。\n",
"\n",
"如果是其他语言,如 C 语言这类底层语言,是可以通过 sprintf 函数转换 int 到 char * 字符串的,如 `sprintf(send, \"%d,%f,%s\\n\", 0, 1.2, \"hello)` 这和前者是一样的。\n",
"\n",
"但还有一种形式,如下 C 代码:\n",
"\n",
"```c\n",
"\n",
"struct data\n",
"{\n",
" uint8_t head;\n",
" uint8_t len;\n",
" uint16_t retain_0;\n",
" uint32_t id;\n",
" float decision;\n",
" uint16_t retain_1;\n",
" uint8_t sum;\n",
" uint8_t end;\n",
"} upload_data = { 0x55, sizeof(struct data), 0, 1234, 5678.9, 3, 4, 0x0A };\n",
"\n",
"write(self->dev_ttyS, (uint8_t *)&upload_data, upload_data.len);\n",
"\n",
"```\n",
"\n",
"那么要如何在 Python 中接收或发送这样的数据给 C 语言呢?\n",
"\n",
"这里我们就要使用 Python 的标准库 `import struct` 来解决这个问题。\n",
"\n",
"只需要知道两个函数 struct.pack() 和 struct.unpack() 对应封包和解包。\n",
"\n",
"> sizeof(struct data) = 1 + 1 + 2 + 4 + 4 + 2 + 1 + 1 = 16 bytes\n",
"\n",
"详细的数据类型定义(`>BBHdfHBB`)请查阅官方文档[格式字符](https://docs.python.org/zh-cn/3/library/struct.html#format-characters)即可了解。\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"serial.write b'U\\x10\\x00\\x00@\\x93H\\x00\\x00\\x00\\x00\\x00E\\xb1w3\\x00\\x03\\x04\\n'\n",
"hexstring 55-10-00-00-40-93-48-00-00-00-00-00-45-b1-77-33-00-03-04-0a\n",
"bytes b'U\\x10\\x00\\x00@\\x93H\\x00\\x00\\x00\\x00\\x00E\\xb1w3\\x00\\x03\\x04\\n'\n",
"data (85, 16, 0, 1234.0, 5678.89990234375, 3, 4, 10)\n",
"<class 'int'> 85\n",
"<class 'int'> 16\n",
"<class 'int'> 0\n",
"<class 'float'> 1234.0\n",
"<class 'float'> 5678.89990234375\n",
"<class 'int'> 3\n",
"<class 'int'> 4\n",
"<class 'int'> 10\n"
]
}
],
"source": [
"def hex_str_to_bytes(hexString: str) -> bytes:\n",
" String = hexString.replace(\"-\", \"\")\n",
" return bytes.fromhex(String)\n",
"\n",
"\n",
"def bytes_to_hex_str(String: bytes) -> str:\n",
" hexString = String.hex()\n",
" import re\n",
" return '-'.join(re.findall(r'.{2}', hexString))\n",
"\n",
"\n",
"import struct\n",
"\n",
"upload_data = struct.pack(\">BBHdfHBB\", 0x55, 16, 0, 1234, 5678.9, 3, 4, 0x0a)\n",
"\n",
"print('serial.write', upload_data)\n",
"\n",
"print('hexstring', bytes_to_hex_str(upload_data))\n",
"\n",
"print('bytes', hex_str_to_bytes(bytes_to_hex_str(upload_data)))\n",
"\n",
"data = struct.unpack(\">BBHdfHBB\", upload_data)\n",
"print('data', data)\n",
"for i in data:\n",
" print(type(i), i)\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"这里 `struct.pack(\">` 的 > 和 < 就引出了大小端的问题,不同芯片可能会有不同的大小端,主要影响 int 或 float 的转换方向问题,如 0x09ABCDEF 会被另一个芯片当成 0xEFCDAB09 可以在[这个大小端网站体验](https://www.toolhelper.cn/Digit/LittleBigEndianConvert)效果。\n",
"\n",
"例如上述案例数据大佬鼠我可能会写成以下 Python 接收代码:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"def checksum(data):\n",
" sum = 0\n",
" for i in range(len(data)):\n",
" sum += data[i]\n",
" # print(i, data[i], sum)\n",
" return sum & 0xff\n",
"\n",
"import serial\n",
"import struct\n",
"\n",
"ser = serial.Serial(\"/dev/ttyUSB0\", 115200) # 连接串口\n",
"\n",
"'''\n",
" struct apriltag_data {\n",
" uint8_t head;\n",
" uint8_t len;\n",
" uint8_t retain_0;\n",
" uint8_t retain_1;\n",
" uint32_t tm;\n",
" uint32_t id;\n",
" float decision_margin;\n",
" float center[2];\n",
" float points[4][2];\n",
" float rotation[3][3];\n",
" uint8_t retain_2;\n",
" uint8_t retain_3;\n",
" uint8_t sum;\n",
" uint8_t end;\n",
" } upload_data = { 0x55, sizeof(struct apriltag_data), 0, 0, gs831_get_ms(), 0, 0, { 0, 0 }, { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }}, 0, 0, 0, 0x0A };\n",
"'''\n",
"\n",
"data, sum = b'', 0\n",
"while True:\n",
" dat = ser.read(1)\n",
" if dat[0] == 0x55:\n",
" data = dat\n",
" dat = ser.read(1) # datlen\n",
" data += dat\n",
" # print(len(data), data)\n",
" dat = ser.readline()\n",
" # print(data[1], len(dat), dat)\n",
" if data[1] - 2 == len(dat):\n",
" data += dat\n",
" sum = checksum(data[:-2])\n",
" # print(len(data), data)\n",
" if sum == data[-2]:\n",
" res = struct.unpack('<BBBBIIffffffffffffffffffffBBbB', data)\n",
" # print(res[4:-4])\n",
" print(\"%02.03f %02.03f %02.03f %02.03f %02.03f %02.03f %02.03f %02.03f %02.03f\" % (res[17:-4]))\n",
" continue\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"和对应的 C 发送代码:\n",
"\n",
"```C\n",
"struct apriltag_data\n",
"{\n",
" uint8_t head;\n",
" uint8_t len;\n",
" uint8_t retain_0;\n",
" uint8_t retain_1;\n",
" uint32_t tm;\n",
" uint32_t id;\n",
" float decision_margin;\n",
" float center[2];\n",
" float points[4][2];\n",
" float rotation[3][3];\n",
" uint8_t retain_2;\n",
" uint8_t retain_3;\n",
" uint8_t sum;\n",
" uint8_t end;\n",
"} upload_data = {0x55, sizeof(struct apriltag_data), 0, 0, gs831_get_ms(), 0, 0, {0, 0}, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, 0, 0, 0, 0x0A};\n",
"\n",
"upload_data.id = det->id;\n",
"upload_data.decision_margin = det->decision_margin;\n",
"upload_data.center[0] = det->c[0];\n",
"upload_data.center[1] = det->c[1];\n",
"upload_data.points[0][0] = det->p[0][0];\n",
"upload_data.points[0][1] = det->p[0][1];\n",
"upload_data.points[1][0] = det->p[1][0];\n",
"upload_data.points[1][1] = det->p[1][1];\n",
"upload_data.points[2][0] = det->p[2][0];\n",
"upload_data.points[2][1] = det->p[2][1];\n",
"upload_data.points[3][0] = det->p[3][0];\n",
"upload_data.points[3][1] = det->p[3][1];\n",
"\n",
"upload_data.rotation[0][0] = R(0,0);\n",
"upload_data.rotation[0][1] = R(0,1);\n",
"upload_data.rotation[0][2] = R(0,2);\n",
"upload_data.rotation[1][0] = R(1,0);\n",
"upload_data.rotation[1][1] = R(1,1);\n",
"upload_data.rotation[1][2] = R(1,2);\n",
"upload_data.rotation[2][0] = R(2,0);\n",
"upload_data.rotation[2][1] = R(2,1);\n",
"upload_data.rotation[2][2] = R(2,2);\n",
"\n",
"uint8_t *ptr = (uint8_t *)&upload_data;\n",
"\n",
"for (int i = 0; i < upload_data.len - 2; i++)\n",
" upload_data.sum += ptr[i];\n",
"\n",
"write(gs831->dev_ttyS, ptr, upload_data.len);\n",
"\n",
"```\n",
"\n",
"当然方法千千万万,按照自己的实际情况和需要写就好,没有绝对正确的代码,相比之前第一种最好写,但效率不高,如果你的场景不在乎效率,那就随意一些吧。\n",
"\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "RPyc-Python",
"language": "Python",
"name": "rpyc"
"display_name": "Python 3.8.10 64-bit",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.10"
},
"vscode": {
"interpreter": {
"hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1"
}
}
},
"nbformat": 4,