Lua 位和字节

一、位运算

从 Lua 5.3 版本开始,提供了针对数值类型的一组标准位运算符,与算数运算符不同的是,运算符只能用于整型数。

运算符描述
&按位与
|按位或
按位异或
>>逻辑右移
<<逻辑左移
~(一元运算)按位取反

位的使用,可以参考小盆友的另一篇文章 《android位运算简单讲解》
https://blog.csdn.net/weixin_37625173/article/details/83796580

print(string.format("%x", 0xff & 0xabcd))       --> cd
print(string.format("%x", 0xff | 0xabcd))       --> abff
print(string.format("%x", 0xff ~ 0xabcd))       --> ab32
print(string.format("%x", ~0))                  --> ffffffffffffffff (16 个 6,每一个十六进制 4 位,刚好是 64 位)
print(string.format("%x", 0xff << 12))          --> ff000
print(string.format("%x", 0xff >> -12))         --> ff000
-- 移位数等于或大于整型表示的位数,由于所有的位都被移出,则结果为 0
print(string.format("%x", -1 << 80))            --> 0

1-1、注意小点

所有的位运算针对一个整数型的所有位。

Lua 的两个移位操作都会用 0 填充空出的位,这种行为称为逻辑移位。 Lua 中没有提供算术右移(即使用符号位填充空出的位),但是可以通过向下取整除法( floor 除法)达到算数右移

local data = -0x100
print("逻辑右移:", string.format("%x >> 1 --> %x", data, data >> 1))    --> 逻辑右移:	ffffffffffffff00 >> 1 --> 7fffffffffffff80
-- 达到算数右移一位,2^n ( n 即为右移位数)
print("算数右移:", string.format("%x >> 1 --> %x", data, data // 2))    --> 算数右移:	ffffffffffffff00 >> 1 --> ffffffffffffff80

移位数是负数则表示向相反方向移位。a>>na<<-n 相等。

二、无符号整型数

在有符号整型数中使用一个比特位来存储符号位。所以 64 的整型数中最大可以表示为 2^63-1 ,而不是 2^64-1 。

值得注意的是 Lua 语言不显示支持无符号整型数, 但这并不妨碍我们在 Lua 中使用无符号整型这一特性,只是在使用过程中需要注意一些细节。

2-1、细节一:打印无符号整数

对于一个数最直观的就是展示出来,所以我们可以使用 string.format 进行格式化数值, 使用 %u(无符号整数)或 %X (十六进制)进行展示,这样就能很直观的感知无符号整数每一位的数值是多少。如果直接将无符号整数打印,会被认为是有符号整数,就不利于阅读。

local x = 3 << 62
print("有符号整数显示:", x)        --> 有符号整数显示:	-4611686018427387904
print("使用十进制无符号显示", string.format("%u", x))       --> 使用十进制无符号显示	13835058055282163712
print("使用十六进制无符号显示", string.format("0x%X", x))   --> 使用十六进制无符号显示	0xC000000000000000

2-2、细节二:无符号整数,加减乘运算

对于无符号整数的加减乘运算,和有符号是一样的,只是要注意溢出

local x = 3 << 62
print("使用十进制无符号显示", string.format("%u", x))                   --> 使用十进制无符号显示	13835058055282163712
print("使用十进制无符号显示 +1", string.format("%u", x + 1))            --> 使用十进制无符号显示 +1	13835058055282163713
print("使用十进制无符号显示 -1", string.format("%u", x - 1))            --> 使用十进制无符号显示 -1	13835058055282163711
x = 1 << 62
print("使用十六进制无符号显示", string.format("0x%X", x))               --> 使用十六进制无符号显示	    0x4000000000000000
print("使用十进制无符号显示 * 2", string.format("%X", x * 2))           --> 使用十进制无符号显示 * 2	0x8000000000000000

2-3、细节三:无符号整数,除法运算

对于无符号整数的除法会有些不一样,需要注意其符号位的影响,可以通过下面函数进行无符号整数的除法(细节也在每一行的注释中)

function udiv(n, d)-- d<0 实质比较除数是否大于 2^63if d < 0 then-- 如果除数大于被除数(n<d),则商为 0 ;否则为 1if math.ult(n, d) thenreturn 0elsereturn 1endend-- n >> 1 等价于将无符号整数 n / 2 , 这样的做法是先去除符号位置的影响-- 最后 << 1 等价于 * 2 ,这样是为了纠正一开始 >> 1-- // d 进行有符号整数的正常向下取整除法local q = ((n >> 1) // d) << 1-- 计算因为移位和向下取整导致的丢失数值,计算出两个结果后,如果偏差大于除数,说明需要加一local r = n - q * dif not math.ult(r, d) thenq = q + 1endreturn q
endlocal u1 = 1 << 1
-- 正常数字除法
local div1 = 2
print(string.format("%X/%X = %X", u1, div1, udiv(u1, div1)))        --> 2/2 = 1
-- (符号位为 1 )很大的数进行无符号除法
local u2 = 1 << 63
print(string.format("%X/%X = %X", u2, div1, udiv(u2, div1)))        --> 8000000000000000/2 = 4000000000000000 
-- (符号位为 0 )正常的数进行无符号除法
local u3 = 1 << 62
print(string.format("%X/%X = %X", u3, div1, udiv(u3, div1)))        --> 4000000000000000/2 = 2000000000000000
-- 除数很大,符号为为 1 ,被除数 < 除数
local div2 = 1 << 63
print(string.format("%X/%X = %X", u3, div2, udiv(u3, div2)))        --> 4000000000000000/8000000000000000 = 0
-- 被除数 > 除数
local u4 = (1 << 63) + 1
print(string.format("%X/%X = %X", u4, div2, udiv(u4, div2)))        --> 8000000000000001/8000000000000000 = 1
local u5 = (1 << 63) + 3
print(string.format("%X/%X = %X", u5, div1, udiv(u5, div1)))        --> 8000000000000003/2 = 4000000000000001

2-4、细节四:无符号整数比较

如果直接将无符号整数进行比较,则会导致一种情况:因为第 64 位在有符号整数是符号位,而无符号整数则用于正常表示数值,所以当第 64 位为 1 时,则会导致在直接比较时(此时被当作是有符号整数)无符号越大的值反而越小。

解决此类方法有两种方式:

第一种,使用 math.ult 进行比较无符号整数

local n1 = 0x7fffffffffffffff
local n2 = 0x8000000000000000
print(n1, n2, n1 < n2)              --> 9223372036854775807	-9223372036854775808	false
print(n1, n2, math.ult(n1, n2))     --> 9223372036854775807	-9223372036854775808	true

第二种,在进行有符号比较前先用掩码去两个操作数的符号位

local n3 = -10
local n4 = 10
local mask = 0x8000000000000000
print("有符号:", n3, "<", n4, n3 < n4)                     --> 有符号:	-10<10	true
print("无符号:", n4, "<", n3, math.ult(n4, n3))            --> 无符号:	10<-10	true
print("无符号:", n4, "<", n3, (n4 ~ mask) < (n3 ~ mask))   --> 无符号:	10<-10	true

2-5、细节五:整型数和浮点数互转

整型转浮点数

local u = 0xC000000000000000
print(math.type(u), string.format("%X", u))         --> integer	C000000000000000
-- + 0.0 是为将 u 转为 float , % 取余的规则符合通用规则,只要其中有一个为浮点数,结果则为浮点数
-- %(2 ^ 64) 是为将结果约束在这其中,否则显示时会被认为是有符号
local f = (u + 0.0) % 2 ^ 64
print(math.type(f), string.format("%.0f", f))       --> float	13835058055282163712
local f1 = (u + 0.0)
print(math.type(f1), string.format("%.0f", f1))     --> float	-4611686018427387904

浮点数转整型

function utointerger(value)if math.type(value) == "integer" thenreturn valueend-- f + 2 ^ 63 是为了让数转为一个大于 2 ^ 64 的数-- % (2 ^ 64) 是为了让数值限制在 [0, 2 ^ 64) 范围-- - 2 ^ 63 是为了把结果改为一个"负值"(最高位为 1 )-- 对于小于 2 ^ 63 的数:-- 其实没有什么特殊的,加完一个数值(2 ^ 63)之后有减掉了,所以没有什么特殊local result = math.tointeger(((value + 2 ^ 63) % (2 ^ 64)) - 2 ^ 63)return result + (math.tointeger(value - result) or 0)
endlocal f = 0xF000000000000000.0
local u = utointerger(f)
print(math.type(u), string.format("%X", u))     --> integer	F000000000000000

值得注意:

因为这里是一个浮点数的计算,在我们之前的分享浮点数的文章中,有讲到 在 [-2^53, 2^53] 范围内,只需要将 整型数值 + 0.0 则可以进行转换为 浮点数,如果超出范围,会导致精度丢失,取近似值。

所以上述代码的 f 值如果超出这一范围,低位数可能会有问题

local f1 = 0x8000000000000001.0
local u1 = utointerger(f1)
print(math.type(u1), string.format("%X", u1))       --> integer	8000000000000000

三、二进制数据

Lua 从 5.3 开始提供了对二进制数和基本类型值之间进行转换的函数。

string.pack 会把值 “打包” 为二进制字符串

string.unpack 则从字符串中提取二进制

3-1、string.pack(fmt, v1, v2, …)

按照 fmt 格式将 v1、v2 进行打包为二进制字符串

参数:

  • fmt:打包格式
  • v1、v2:需要打包的数据

返回值:

字符串类型,即打包后的二进制数据

local format = "iii"
local s = string.pack(format, 3, -27, 450)
print(s, #s)        --> �����	12

3-2、string.unpack(fmt, s, pos)

按照 fmt 格式对字符串 s 偏移了 pos 个位置后,进行解析

参数:

  • fmt:解析格式
  • s:需要解析的字符串
  • pos:可选,从字符串 s 的第 pos 个位置开始解析,默认为 1

返回值:

会有两个返回值,第一个为解析后的内容,第二个为解析的字符串 s 中最后一个读取的元素在字符串中的位置。

local format = "iii"
local s = string.pack(format, 3, -27, 450)  --> �����	12
-- 最后的 13 即,最后一个读取的元素位置
print(string.unpack(format, s))             --> 3	-27	450	13

3-3、整型数格式可选参数

每种选项对应一种类型大小

格式描述
bchar
hshort
iint(可以跟一个数字,表示多少字节的整型数)
llong
jLua 语言中的整型数大小

3-3-1、固定字节

在使用 i 的时候,大小会与机器有关,如果想要达到固定大小,可以考虑在后面加上数字(1~16),类似 i8,则会产生 8 个字节的整型数。

local n1 = 1 << 54
print(string.format("%X", n1))          --> 40000000000000
local x1 = string.pack("i8", n1)
print(#x1, string.format("%X", string.unpack("i8", x1)))    --> 8	40000000000000

3-3-2、打包和解包都会检测溢出

打包的时候,如果发现字节不够装数值,则会抛出异常 bad argument #2 to 'pack' (integer overflow)

local x2 = string.pack("i8", 1 << 63)
print(string.format("%X", string.unpack("i8", x2)))-- 打包会进行检查是否溢出
-- bad argument #2 to 'pack' (integer overflow)
print(string.pack("i7", 1 << 63))

解包也是一样的,会抛出异常 12-byte integer does not fit into Lua Integer ,因为对于 Lua 的整型数是 8 字节。

local x = string.pack("i12", 2 ^ 61)
print(string.unpack("i12", x))
x = "aaaa" .. "aaaa" .. "aaaa"
-- 解包也会检查是否能装得下
-- 12-byte integer does not fit into Lua Integer
--string.unpack("i12", x)

3-3-3、格式化无符号

每一个格式化选项都有一个大写的版本,对应无符号整型数:

local s = "\xFF"
print(string.unpack("b", s))        --> -1	2
print(string.unpack("B", s))        --> 255	2

3-4、字符串格式可选参数

可以使用三种形式打包字符串

格式描述
z\0 结尾的字符串
cn定长字符串,n 是被打包字符串的字节数
sn显式长度字符串,会在存储字符串前加上该字符串的长度,n 是用于保存字符串长度的无符号整型数的大小

举些例子

s1 表示将字符串长度保存在一个字节中

print("----- sn -----")
local s1 = string.pack("s1", "hello")
print("s1 长度", #s1)
for i = 1, #s1 doprint(string.unpack("B", s1, i))
end--> s1 长度	6
--> 5	2   (length)
--> 104	3   ('h')
--> 101	4   ('e')
--> 108	5   ('l')
--> 108	6   ('l')
--> 111	7   ('o')

s2 则表示用两个字节存储

s1 = string.pack("s2", "hello")
print("s2 长度", #s1)
for i = 1, #s1 doprint(string.unpack("B", s1, i))
end--> s2 长度	7
--> 5	2   (length)
--> 0	3   (length)
--> 104	4   ('h')
--> 101	5   ('e')
--> 108	6   ('l')
--> 108	7   ('l')
--> 111	8   ('o')

值得注意,如果保存长度的字节容纳不下字符串的长度,则会有抛出异常

当然也可以直接使用 s 进行打包字符串,会使用 size_t 类型进行保存,在 64 位的机子上,一般使用 8 个字节保存,大多数情况这会有些浪费。

s1 = string.pack("s", "hello")
print("s 长度", #s1)
print(string.unpack("s", s1, i))
for i = 1, #s1 doprint(string.unpack("B", s1, i))
end--> s 长度	13
--> hello	14
--> 5	2   (length)
--> 0	3   (length)
--> 0	4   (length)
--> 0	5   (length)
--> 0	6   (length)
--> 0	7   (length)
--> 0	8   (length)
--> 0	9   (length)
--> 104	10  ('h')
--> 101	11  ('e')
--> 108	12  ('l')
--> 108	13  ('l')
--> 111	14  ('o')

cn 打包固定长度自负串

-- 因为这里每个中文占 3 个字节,所以是 c9 ,后面打印也就有九行
local name = "江澎涌"
local fmt = string.format("c%d", #name)
local s3 = string.pack(fmt, name)
for i = 1, #s3 doprint(string.unpack("B", s3, i))
end
print(string.unpack(fmt, s3))--> 230	2
--> 177	3
--> 159	4
--> 230	5
--> 190	6
--> 142	7
--> 230	8
--> 182	9
--> 140	10
--> 江澎涌	10

3-5、浮点数格式可选参数

格式描述
f单精度浮点数
d双精度浮点数
nLua 语言浮点数
local p = string.pack("fdn", 3.14, 1.70, 10.89)
print(string.unpack("fdn", p))                  --> 3.1400001049042	1.7	10.89	21

3-6、大小端模式

默认情况下,格式使用的是机器原生的大小端模式。可以在格式中使用,一旦使用,后续的都会作用

格式描述
>大端模式、网络字节序
<小端模式
=改回机器默认模式

大端模式

local s1 = string.pack(">i2 i2", 500, 24)
for i = 1, #s1 doprint(string.unpack("B", s1, i))
end--> 1	2
--> 244	3
--> 0	4
--> 24	5

小端模式

local s2 = string.pack("<i2 i2", 500, 24)
for i = 1, #s2 doprint(string.unpack("B", s2, i))
end--> 244	2
--> 1	3
--> 24	4
--> 0	5

一图胜千言

改回默认

local s3 = string.pack(">i2 =i2", 500, 24)
for i = 1, #s3 doprint(string.unpack("B", s3, i))
end--> 1	2
--> 244	3
--> 24	4
--> 0	5

3-7、对齐数据

使用 !n 进行强制数据对齐到 n 为倍数的索引上。如果只是使用 ! 则会使用机器默认对其方式

如果数据比 n 小,则对其到其自身大小,否则对其到 n 上。

例如 !4 ,则 1 字节整型数会被写入以 1 位倍数的所以位置上,2 字节的整型数会被写入到以 2 为倍数的索引位置上,而 4 字节或更大的整型数会被写入到以 4 为倍数的索引位置上。

local s = string.pack("!4 i1 i2 i4", 10, 10, 10)
print("#s", #s)
for i = 1, #s doprint(string.unpack("i1", s, i))
end--> #s	8
--> 10	2
--> 0	3
--> 10	4
--> 0	5
--> 10	6
--> 0	7
--> 0	8
--> 0	9

string.pack 通过补 0 的形式实现对齐。string.unpack 则会在读取字符串时简单的跳过这些补位。

对齐只对 2 的整数次幂有效,如果不是则会报错 format asks for alignment not power of 2

在所有的格式化字符串默认的都会带有前缀 =!1,即表示使用默认的大小端模式且不对齐。

3-8、手工添加补位

可以通过 x 进行 1 字节的补位,string.pack 会在结果字符串中增加一个 0 字节,string.unpack 则会从目标字符串中跳过这一字节

local s = string.pack("i1i1xi1", 10, 10, 10)
print("#s", #s)
for i = 1, #s doprint(string.unpack("i1", s, i))
end--> #s	4
--> 10	2
--> 10	3
--> 0	4
--> 10	5

四、拓展一下

可以使用这一节的内容实现一些类似二进制文件查看器的功能,如下图所示

具体代码 https://github.com/zincPower/lua_study_2022/blob/master/7%20%E4%BD%8D%E5%92%8C%E5%AD%97%E8%8A%82/dump.lua

还可以对图片进行操作,具体可以看 https://github.com/zincPower/lua_study_2022/blob/master/7%20%E4%BD%8D%E5%92%8C%E5%AD%97%E8%8A%82/writeBinary.lua

五、写在最后

Lua 项目地址:Github传送门 (如果对你有所帮助或喜欢的话,赏个star吧,码字不易,请多多支持)

如果觉得本篇博文对你有所启发或是解决了困惑,点个赞或关注我呀

公众号搜索 “江澎涌”,更多优质文章会第一时间分享与你。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/40852.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Git 如何使用TortoiseGit 操作本地仓库

初始化仓库 方法一: 新建一个文件夹,进入文件夹内部操作 1、右键--> 在这里创建Git 版本库 注意: 不要直接在桌面上操作,否则桌面就是一个仓库 方法二: 1、右键-->Git GUI here 方法三: 命令行模式 1、 git init 创建完毕仓库,我们发现,此时我们创建的文件夹下…

leetcode做题笔记83删除排序链表中的重复元素

给定一个已排序的链表的头 head &#xff0c; 删除所有重复的元素&#xff0c;使每个元素只出现一次 。返回 已排序的链表 。 输入&#xff1a;head [1,1,2] 输出&#xff1a;[1,2] 思路一&#xff1a;模拟题意 struct ListNode* deleteDuplicates(struct ListNode* head){i…

FreeRTOS qemu mps2-an385 bsp 移植制作 :系统运行篇

相关文章 FreeRTOS qemu mps2-an385 bsp 移植制作 &#xff1a;环境搭建篇 FreeRTOS qemu mps2-an385 bsp 移植制作 &#xff1a;系统启动篇 开发环境 Win10 64位 VS Code&#xff0c;ssh 远程连接 ubuntu VMware Workstation Pro 16 Ubuntu 20.04 FreeRTOSv202212.01&a…

React 全栈体系(二)

第二章 React面向组件编程 一、基本理解和使用 1. 使用React开发者工具调试 2. 效果 2.1 函数式组件 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>1_函数式组件</title> </head> &l…

计算机竞赛 python 爬虫与协同过滤的新闻推荐系统

1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; python 爬虫与协同过滤的新闻推荐系统 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;3分创新点&#xff1a;4分 该项目较为新颖&…

软件压力测试对软件产品起到什么作用?

一、软件压力测试是什么? 软件压力测试是一种通过模拟正常使用环境中可能出现的大量用户和大数据量的情况&#xff0c;来评估软件系统在压力下的稳定性和性能表现的测试方法。在软件开发过程中&#xff0c;经常会遇到一些性能瓶颈和稳定性问题&#xff0c;而软件压力测试的作…

C语言刷题指南(一)

&#x1f4d9;作者简介&#xff1a; 清水加冰&#xff0c;目前大二在读&#xff0c;正在学习C/C、Python、操作系统、数据库等。 &#x1f4d8;相关专栏&#xff1a;C语言初阶、C语言进阶、数据结构刷题训练营、有感兴趣的可以看一看。 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &am…

认识excel篇3之数据的有效性(数据验证)

数据有效性不仅能够对单元格的输入数据进行条件限制&#xff0c;还可以在单元格中创建下拉列表菜单方便用户选择输入。如果没有做数据验证&#xff0c;单元格内默认可以输入任意类型的数据。数据验证就是限制单元格输入数据&#xff08;必须输入符合要求的才能输入&#xff09;…

VS2022如何查看类成员都在哪里被调用了(VS如何打开Call Hierarchy视图)

文章目录 打开Call Hierarchy视图查看成员的调用 打开Call Hierarchy视图 单击菜单栏的“视图” > “调用层次结构”&#xff0c;即可打卡Call Hierarchy视图。 查看成员的调用 在代码编辑窗口&#xff0c;右键单击想要查看的类成员&#xff0c;然后选择“查看调用层次结…

机器学习算法之-逻辑回归(2)

为什么需要逻辑回归 拟合效果太好 特征与标签之间的线性关系极强的数据&#xff0c;比如金融领域中的 信用卡欺诈&#xff0c;评分卡制作&#xff0c;电商中的营销预测等等相关的数据&#xff0c;都是逻辑回归的强项。虽然现在有了梯度提升树GDBT&#xff0c;比逻辑回归效果更…

一、数学建模之线性规划篇

1.定义 2.例题 3.使用软件及解题 一、定义 1.线性规划&#xff08;Linear Programming&#xff0c;简称LP&#xff09;是一种数学优化技术&#xff0c;线性规划作为运筹学的一个重要分支&#xff0c;专门研究在给定一组线性约束条件下&#xff0c;如何找到一个最优的决策&…

JavaScript请求数据的4种方法总结(Ajax、fetch、jQuery、axios)

JavaScript请求数据有4种主流方式&#xff0c;分别是Ajax、fetch、jQuery和axios。 一、Ajax、fetch、jQuery和axios的详细解释&#xff1a; 1、 Ajax Ajax&#xff08;Asynchronous JavaScript and XML&#xff09;是一种使用JavaScript在用户的浏览器上发送请求的技术&…

使用低版本vcpkg时,bootstrap-vcpkg.bat无法生成vcpkg.exe的可能原因

缘由 需要使用vcpkg中低版本的第三方库&#xff0c;下载vcpkg后&#xff0c;回退至指定版本&#xff0c;运行bootstrap-vcpkg.bat生成vcpkg.exe时&#xff0c;命令行窗口总是一闪而过&#xff0c;但是vcpkg.exe却没有生成。 添加pause&#xff0c;查看错误 编辑bootstrap-vc…

docker的网络模式

docker0网络 docker容器的 虚拟网关loopback &#xff1a;回环网卡、TCP/IP网卡是否生效virtual bridge&#xff1a;linux 自身继承了一个虚拟化功能&#xff08;kvm架构&#xff09;&#xff0c;是原生架构的一个虚拟化平台&#xff0c;安装了一个虚拟化平台之后就会系统就会自…

区间预测 | MATLAB实现QRBiLSTM双向长短期记忆神经网络分位数回归时间序列区间预测

区间预测 | MATLAB实现QRBiLSTM双向长短期记忆神经网络分位数回归时间序列区间预测 目录 区间预测 | MATLAB实现QRBiLSTM双向长短期记忆神经网络分位数回归时间序列区间预测效果一览基本介绍模型描述程序设计参考资料 效果一览 基本介绍 区间预测 | MATLAB实现QRBiLSTM双向长短…

【回溯】总结

1、 组合和子集问题 组合问题需要满足一定要求才算作一个答案&#xff0c;比如数量要求&#xff08;k个数&#xff09;&#xff0c;累加和要求&#xff08;target&#xff09;。 子集问题是只要构成一个新的子集就算作一个答案。 进阶&#xff1a;去重逻辑。 一般都是要对同…

Linux 5种网络IO模型

Linux IO模型 网络IO的本质是socket的读取&#xff0c;socket在linux系统被抽象为流&#xff0c;IO可以理解为对流的操作。刚才说了&#xff0c;对于一次IO访问&#xff08;以read举例&#xff09;&#xff0c;数据会先被拷贝到操作系统内核的缓冲区中&#xff0c;然后才会从操…

LL库实现SPI MDA发送方式驱动WS2812

1&#xff0c;首先打卡STM32CubeMX&#xff0c;配置一下工程&#xff0c;这里使用的芯片是STM32F030F4P6。 时钟 SPI外设 SPI DMA 下载接口&#xff0c;这个不配置待会下程序后第二次就不好下载调试了。 工程配置&#xff0c;没啥说的 选择生成所有文件 将驱动都改为LL库 然后直…

jmeter errstr :“unsupported field type for multipart.FileHeader“

在使用jmeter测试接口的时候&#xff0c;提示errstr :"unsupported field type for multipart.FileHeader"如图所示 这是因为我们 在HTTP信息头管理加content-type参数有问题 直接在HTTP请求中&#xff0c;勾选&#xff1a; use multipart/form-data for POST【中文…