基本的正则表达式

基本的正则表达式,记录一下,方便之后查询; 基本的正则表达式: 在线练习 1. 基本匹配 正则表达式其实就是在执行搜索时的格式,它由一些字母和数字组合而成。 例如:一个正则表达式 the,它表示一个规则:由字母t开始,接着是h,再接着是e。 "the" => The fat cat sat on the mat. 正则表达式123匹配字符串123。它逐个字符的与输入的正则表达式做比较。 正则表达式是大小写敏感的,所以The不会匹配the。 "The" => The fat cat sat on the mat. 2. 元字符 正则表达式主要依赖于元字符。 元字符不代表他们本身的字面意思,他们都有特殊的含义。一些元字符写在方括号中的时候有一些特殊的意思。以下是一些元字符的介绍: 元字符描述.句号匹配任意单个字符除了换行符。[ ]字符种类。匹配方括号内的任意字符。否定的字符种类。匹配除了方括号里的任意字符匹配>=0 个重复的在号之前的字符。+匹配>=1 个重复的+号前的字符。?标记?之前的字符为可选.{n,m}匹配 num 个大括号之间的字符 (n <= num <= m).(xyz)字符集,匹配与 xyz 完全相等的字符串.|或运算符,匹配符号前或后的字符.\转义字符,用于匹配一些保留的字符 { } . * + ? ^ $ \ |^从开始行开始匹配.$从末端开始匹配. 2.1 点运算符 . .是元字符中最简单的例子。 .匹配任意单个字符,但不匹配换行符。 例如,表达式.ar匹配一个任意字符后面跟着是a和r的字符串。 ".ar" => The car parked in the garage. 2.2 字符集 字符集也叫做字符类。 方括号用来指定一个字符集。 在方括号中使用连字符来指定字符集的范围。 在方括号中的字符集不关心顺序。 例如,表达式[Tt]he 匹配 the 和 The。 ...

June 5, 2022 · 4 min · Rancho

Linux_grep命令使用

最近在学Linux,发现grep命令很常用,所以记录一下,方便之后查询; grep命令常见用法 1、字符串搜索: 在文件中搜索一个单词,命令会返回一个包含 “match_pattern” 的文本行: grep match_pattern file_name grep "match_pattern" file_name 例子: 2、在多个文件中查找字符串: grep "match_pattern" file_1 file_2 file_3 ... 例子: 3、输出除之外的所有行 -v 选项: grep -v "match_pattern" file_name 例子: 4、标记匹配颜色 —color=auto 选项: grep "match_pattern" file_name --color=auto 例子: 5、使用正则表达式: 使用正则表达式 -E 选项: grep -E "[1-9]+" # 或 egrep "[1-9]+" 使用正则表达式 -P 选项: grep -P "(\d{3}\-){2}\d{4}" file_name 只输出文件中匹配到的部分 -o 选项: echo this is a test line. | grep -o -E "[a-z]+\." line. echo this is a test line. | egrep -o "[a-z]+\." line. 6、统计文件或者文本中包含匹配字符串的行数: -c 选项: ...

June 5, 2022 · 3 min · Rancho

lvgl显示txt文本(指定字体)

前些天做微机课设,给小一加了几个功能,其中一个重要功能是显示中文文本; 字体取模: 屏幕要显示图案,例如某个汉字或者数字、图案等,都需要对图案进行取模操作; 使用的是LvglFontTool工具,LVGL官网的字体转化用于单个字体取模比较方便;批量的话,使用这个离线取模软件比较方便; 操作界面如下所示: 步骤还是很简单的: 首先选择字体,包括一个TFF字体文件还有选择需要取模的字体大小; 然后加入汉字,我是将所有常用的汉字都加入了; 然后在右边配置一些选项,按照图片上的配置就可以; 最后点击开始转换就可以生成一个myFont.c和myFont.bin文件,bin文件加载到SPI FLASH中,C文件加入Keil工程即可; 如果只需要显示数量比较少的文本,取模后得到的数组可以直接放在一个.c或者.h文件中,直接下载到单片机的FLASH中即可,但是如果要显示各种不同样式和不同大小的字体,取模后得到的文件会很大,加载到FLASH中存放是不合理的。 文件放置: 小一这一版的硬件是带了一个8M的SPI FLASH,所以取模后的数据可以放在这块SPI FLASH中,可以用哪些方法通过单片机读取bin文件中的内容呢? 一般是有两个方法: 放入移植好的Fatfs文件系统中,通过文件系统提供的接口读取该bin文件; 加载进SPI FLASH中,直接通过最底层的读取函数读取; 两种方案各有优缺点,第一种更换字体取模文件很方便,但是由于字体取模文件会频繁被读取,所以这个方案的效率会比较差;第二种方案更换字体取模文件比较麻烦,但是读取的效率会高不少。我是选择了第二种方案,第一种我也试了,效率确实不是很高。 对于第二种方案,首先要将取模文件从PC机放入SPI FLASH中,我采用的方案是将SPI FLASH划分为两部分: 前4MB 后4MB 用于存储字体取模数据 用于建立文件系统 然后将SPI FLASH模拟为USB设备,插入PC机,会弹出一个U盘,将字体文件拖入;然后通过一个函数,将bin文件分段读取并分段写入SPI FLASH的前4MB部分中,具体函数如下所示: void write_to_flash(void) { uint8_t i; f_res = f_open(&file1, "myFont.bin", FA_READ);//打开对应文件 count_f = 0; for (i = 0; i 如果有多个字体文件,可以将写入的地址偏移一个大小即可。 ## 文件读取: 字体取模文件读取只需要修改`myFont.c`中的一个函数: ```c static uint8_t __g_font_buf[324]; //如bin文件存在SPI FLASH可使用此buff static uint8_t *__user_font_getdata(int offset, int size) { //如字模保存在SPI FLASH, SPIFLASH_Read(__g_font_buf,offset,size); //如字模已加载到SDRAM,直接返回偏移地址即可如:return (uint8_t*)(sdram_fontddr+offset); my_W25QXX_Read(__g_font_buf, offset, size); return __g_font_buf; } 如果有多个字体文件,可以在对应的myFont文件中将读出的地址偏移一个大小即可。 ...

May 16, 2022 · 1 min · Rancho

结构体中定义函数指针

前些天看了一个RobMaster哨兵机器人的开源代码,里边用了C语言结构体定义函数指针,看得有点迷糊,所以自己查了一些相关的资料; C语言结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。所以,标准C中的结构体是不允许包含成员函数的,当然C++中的结构体对此进行了扩展。那么,我们在C语言的结构体中,只能通过定义函数指针的方式,用函数指针指向相应函数,以此达到调用函数的目的。 结构体定义: struct data_demo { int result; int (*add)(int, int); int (*sub)(int, int); }; 完整例子: #include struct data_demo { int result; int (*add)(int, int); int (*sub)(int, int); }; int add_demo(int i, int j) { return i+j; } int sub_demo(int i, int j) { return i-j; } int main(void) { struct data_demo data = {0, add_demo, sub_demo}; // 也可以如下初始化结构体 完全等价 /* struct data_demo data = { .result = 0, .add = add_demo, .sub = sub_demo }; */ data.result = data.add(20, 30); printf("add:\t%d\n", data.result); data.result = data.sub(30, 20); printf("sub:\t%d\n", data.result); return 0; } 运行结果: ...

May 10, 2022 · 1 min · Rancho

常用的通信协议

常用通讯协议(SPI、IIC、UART); 一、USART和UART: USART:通用同步异步收发器,USART是一个串行通信设备,可以灵活地与外部设备进行全双工数据交换。 UART: 通用异步收发器,异步串行通信口(UART)就是我们在嵌入式中常说的串口,它还是一种通用的数据通信议。 异步通讯时二者无区别,同步通讯时USART可以提供主动时钟。 均为全双工通信。 起始位:先发出一个逻辑”0”的信号,表示传输数据的开始。 数据位:传输N bits。 校验位(可选):数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性。 如传输“A”(01000001)为例,”A”字符的8个bit位中有两个1。当为奇数校验时该位为1;当为偶数校验时该位为0。 停止位:它是一帧数据的结束标志。可以是1bit、1.5bit、2bit的空闲电平。 空闲位:没有数据传输时线路上的电平状态。为逻辑1。 传输方向:即数据是从高位(MSB)开始传输还是从低位(LSB)开始传输。比如传输“A”如果是MSB那么就是01000001,如果是LSB那么就是10000010 帧间隔:即传送数据的帧与帧之间的间隔大小,可以以位为计量也可以用时间(知道波特率那么位数和时间可以换算)。比如传送”A”完后,这为一帧数据,再传”B”,那么A与B之间的间隔即为帧间隔。 波特率定义:有效数据信号调制载波的速率,每秒传输1或0的个数; 例如:串口传输速率为9600bps,每秒可传输多少字节? 起始位:1 数据位:8 停止位:1 校验位:0 传输1字节数据,需要传输10bit,因此: 9600 ÷ 10 = 960Byte 二、IIC通讯协议: IIC协议为半双工协议。 全双工指在发送数据的同时也能够接收数据; 半双工就是指一个时间段内只有一个动作发生; 数据有效传输在scl信号的高电平期间,sda数据线保持稳定,在scl为低电平时允许sda数据线变化。 起始条件在scl为高电平期间,sda出现下降沿,则为起始信号。 结束条件在scl为高电平期间,sda出现上升沿,则为结束信号。 注意:注意起始和终止信号都是由主机发出的,总线在起始条件之后,视为忙状态,在停止条件之后被视为空闲状态。 应答(ACK,Acknowledgement)。即确认字符,在数据通信中,接收站发给发送站的一种传输类控制字符。主机每向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,来确认从机是否成功接收到了数据,从机应答主机所需要的时钟也是由主机提供的,应答出现在每一次主机完成8个数据位传输后紧跟着的时钟周期,低电平0表示应答,1表示非应答。,需要应答时,数据发出方将SDA总线设置为3态输入,由于IIC总线上有上拉电阻,因此此时总线默认高电平,若数据接收方正确接收到数据,则数据接收方将SDA总线拉低,以示正确应答。 IIC传输时时从MSB开始传输到LSB结束。MSB是Most Significant Bit的缩写,最高有效位。在二进制数中,MSB是最高加权位。与十进制数字中最左边的一位类似。通常,MSB位于二进制数的最左侧,LSB位于二进制数的最右侧。LSB,英文 least significant bit,中文义最低有效位。 写时序: ​ ID_Address, REG_Address, W_REG_Data 产生start位; 传送器件地址ID_Address,器件地址的最后一位为数据的传输方向位,R/W,低电平0表示主机往从机写数据(W),1表示主机从从机读数据(R)。ACK应答,应答是从机发送给主机的应答,这里不用管; 传送写入器件寄存器地址,即数据要写入的位置。同样ACK应答不用管; 传送要写入的数据。ACK应答不用管; 产生stop信号; 读时序: ​ {ID_Address + REG_Address} + {ID_Address + R_REG_Data} 产生start信号 传送器件地址(写ID_Address),ACK。 传送字地址(写REG_Address),ACK。 再次产生start信号 再传送一次器件地址,ACK。 读取一个字节的数据,读数据最后结束前无应答ACK信号。 产生stop信号。 ...

May 9, 2022 · 1 min · Rancho

判断大端和小端模式

测试自己的电脑是小端模式还是大端模式; 方法一:地址转换 将int 48存起来,然后取得其地址,再将这个地址转为char* ,这时候,如果是小端存储,那么char*指针就指向48; 48对应的ASCII码为字符0; void judge_bigend_littleend1() { int i = 48; int* p = &i; char c = 0; c = *((char*)p); if (c == '0') printf("小端\n"); else printf("大端\n"); } 方法二:同方法一 定义变量int i=1;将 i 的地址拿到,强转成char*型,这时候就取到了 i 的低地址,这时候如果是1就是小端存储,如果是0就是大端存储。 void judge_bigend_littleend2() { int i = 1; char c = (*(char*)&i); if (c) printf("小端\n"); else printf("大端\n"); } 方法三:利用联合体对齐规则 定义联合体,一个成员是多字节,一个是单字节,给多字节的成员赋一个最低一个字节不为0,其他字节为0 的值,再用第二个成员来判断,如果第二个字节不为0,就是小端,若为0,就是大端。 void judge_bigend_littleend3()//因为联合体小的总是在低位 { union { int i; char c; }un; un.i = 1; if (un.c == 1) printf("小端\n"); else printf("大端\n"); }

May 9, 2022 · 1 min · Rancho

CAN通讯解析

CAN通讯解析; 控制器局域网 (Controller Area Network,简称CAN或者CAN bus) 是一种功能丰富的车用总线标准。被设计用于在不需要主机(Host)的情况下,允许网络上的单片机和仪器相互通信。 它基于[消息传递协议,设计之初在车辆上采用复用通信线缆,以降低铜线使用量,后来也被其他行业所使用。 CAN创建在基于信息导向传输协定的广播机制(Broadcast Communication Mechanism)上。其根据信息的内容,利用信息标志符(Message Identifier,每个标志符在整个网络中独一无二)来定义内容和消息的优先顺序进行传递,而并非指派特定站点地址(Station Address)的方式。 因此,CAN拥有了良好的弹性调整能力,可以在现有网络中增加节点而不用在软、硬件上做出调整。除此之外,消息的传递不基于特殊种类的节点,增加了升级网络的便利性。 架构: CAN是一个用于连接电子控制单元(ECU)的多主机串行总线标准。电子控制单元有时也被称作节点。CAN网络上需要至少两个节点才可进行通信。节点的复杂程度可以只是简单的输入输出设备,也可以是包含有CAN交互器并搭载了软件的嵌入式组件。节点还可能是一个网关,允许普通计算机通过USB或以太网端口与CAN网络上的设备通信。 所有节点通过两根平行的总线连接在一起。两条电线组成一条双绞线,并且接有120Ω的特性阻抗。 ISO 11898-2,也称为高速度CAN。它在总线的两端均接有120Ω电阻。 高速CAN网络 ISO 11898-2 高速CAN总线在传输显性(0)信号时,会将CAN_H端抬向5V高电平,将CAN_L拉向0V低电平。当传输隐性(1)信号时,并不会驱动CAN_H或者CAN_L端。 显性信号CAN_H和CAN_L两端差分标称电压为2V。 终端电阻在没有驱动时,将差分标称电压降回0V。显性信号(0)的共模电压需要在1.5V到3.5V之间。隐性信号(1)的共模电压需要在+/-12V。 高速CAN信令 ISO 11898-2 ISO 11898-3,也被称作低速或者容错CAN。它使用线性主线,星形主线或者连接到一个线性主线上的多星结构主线著称。每个节点都有终端电阻作为全局终端电阻的一部分。全局终端电阻不应低于100 Ω。 低速容错CAN网络 ISO 11898-3 低速/容错CAN信号在传输显性信号(0)时,驱动CANH端抬向5V,将CANL端降向0V。在传输隐性信号(1)时并不驱动CAN 总线的任何一端。在电源电压Vcc为5V时,显性信号差分电压需要大于2.3V,隐性信号的差分电压需要小于0.6V。CAN总线两端未被驱动时,终端电阻使CANL端回归到RTH电压(当电源电压Vcc为5V时,RTH电压至少为Vcc-0.3V=4.7V),同时使CANH端回归至RTL电压(RTL电压最大为0.3V)。两根线需要能够承受-27V至40V的电压而不被损坏。 低速CAN信令 ISO 11898-3 在高速和低速CAN中,从隐性信号向显性信号过渡的速度更快,因为此时CAN线缆被主动积极地驱动。显性向隐性的过渡速度主要取决于CAN网络的长度和导线的电容。 高速CAN通常被用于汽车和工业应用,在这些应用环境中,总线通常从一端横跨至另一端。容错CAN总线则经常被用在需要连接在一起的一组节点。 ISO规格只要求总线共模电压必须保持在最小和最大范围内,但不定义如何将总线电压保持在这个范围。 CAN总线必须使用终端电阻。终端电阻可以用来抑制信号反射,同时可以使总线电压回到隐性状态或者闲置状态。 高速CAN在总线两端使用120Ω电阻。低速CAN在每个节点均使用电阻。也有其他类型的终端,例如ISO 11783中定义了终端偏压电路。 终端偏压电路使用由4条导线组成的线缆,除了CAN信号线以外还有电源线和地线。这在每段总线两端提供自动偏压和终端功能。ISO11783网络是专为热拔插总线段和电子控制单元设计的。 CAN通信节点: 每个节点需要: 中央处理器、微处理器或主处理器 处理主机决定收到的信息的意思以及想要传输的信息。 传感器、驱动器和控制设备可以与主处理器连接。 CAN控制器;通常是集成单片机的一部分 接收:CAN控制器将从总线上接收的串位字节存储直到整个消息可用,之后主处理器可以获取这个消息(通常由于CAN控制器触发一个中断)。 发送:主处理器发送传递信息到CAN控制器,之后当总线空闲时将串位信息传递至总线。 收发器;由ISO11898-2/3介质访问单元(MAU)标准定义 接收:把数据流从CAN总线层转换成CAN控制器可以使用的标准。 CAN控制器通常配有保护电路。 传输:把来自CAN控制器的数据流转换至CAN总线层。 每个节点能够发送和接收信息,但不是同时进行的。 一个消息或帧主要包括标识符(ID),它表示信息的优先级,最多八个数据字节。CRC、ACK和其他帧部分也是消息的一部分。改进了的CAN FD将每个帧拓展至最多64字节。 消息采用不归零(NRZ)格式串联传送到主线并可被所有节点接收。 被CAN网络连接的设备通常是传感器,驱动器和其他控制设备。 这些设备通过一个中央处理器、一个CAN控制器和一个CAN接收器连接至总线。 数据传输: CAN数据传输如果出现争执,将会使用无损位仲裁解决办法。该仲裁法要求CAN网络上的所有节点同步,对每一位的采样都在同一时间。这就是为什么有人称之为CAN同步。然而,同步这个术语在此并不精确,因为数据以异步格式传输而不包含时钟信号。 ...

May 8, 2022 · 2 min · Rancho

stm32启动过程

了解stm32的启动过程,方便遇到问题时的调试; 主要是两个问题: STM32是如何启动的,如何执行到main函数; 如何保证编译后的代码可以烧录到正确的地址; 作为一个计算机系统的核心,CPU的实际工作就是取指令和计算。大体上它可以看做是三个部分组成的:寄存器组、算术逻辑单元(ALU) 、指令队列。 在Cortex-M4的寄存器中有一个特殊的寄存器PC(Program Counter,程序计数器), 用于控制程序的执行。在每个时钟周期中CPU都会根据PC中的值,从地址空间中取一条指令放到指令队列中, 同时从指令队列中取出一条指令进行解析和运算(实际上ARM采用的是一种流水线的指令处理方式,与这里所讲的内容还是有很大差异的,但大体思想差不多)。 并把上次的运算结果写到寄存器中。 CPU运算所用的指令和数据都来自地址空间。在Cortex-M4的内存系统中, CPU可以访问4G的地址空间,根据所映射的物理对象不同大体上被划分成了6块。我们烧写到芯片内部的程序一般都在其中的Code段中, 在STM32中这个段对应的是一块FLASH。上电的时候,基本上只有这块FLASH中的内容是确定的,其它地址空间以及CPU内部的寄存器中的值都是随机的。 当然为了防止上电的时候外设产生意外,片上外设(Peripheral)段中的值在上电的时候也会有初始值。 所以,从处理器的角度来看,启动过程实际上是给各个寄存器赋初值的过程,更具体的是给PC寄存器赋初值的过程。从MCU和系统的角度来看, 启动过程是初始化处理器和外设的过程。 以STM32F4为例: STM32是如何启动的: 上电后系统进行复位,等到时钟稳定后才可以正常工作,这个过程通常需要几个毫秒。 图1中描述了处理器的复位过程,Cortex-M内核会先从地址0x0000处读取栈地址,并写到CPU内部的SP寄存器中。 再从地址0x0004读取Reset Vector到PC寄存器中,进而跳转到Reset Vector所指的地址上开始执行程序。 图1 Cortex-M复位流程 栈空间是处理器实现函数调用和中断服务的工具。函数调用和中断服务有一个共同的特点就是,它们都需要先把当前正在处理的内容暂时保存下来,转而执行要调用的函数, 或者中断服务函数,等待新的函数执行完毕返回后,在从原来保存的内容恢复回来继续执行原来的函数。而函数的调用是支持嵌套的,也就是说一个函数中可以调用子函数, 在子函数中又可以调用其它子函数。那么从函数的调用和返回的顺序上来看,最后调用的函数一定先返回。栈这种数据结构的特点就是其中的数据是后进先出的, 与函数调用和返回的顺序是一致的。因而,人们就专门在内存空间中划分出来一块用作栈空间,并从CPU中拿出一个宝贵的寄存器用于指示栈顶, 该寄存器被记为SP (Stack Pointer)。 所以,前面所说的从CPU的角度看启动过程就是PC寄存器初始化的过程还不完善。虽然对PC寄存器进行初始化后,CPU就可以正常的取指令并进行运算了, 但这时所能完成的功能十分有限,并不能支持对我们很重要的函数和中断。因此,从CPU角度看启动过程是对PC和SP两个寄存器的初始化过程。 Cortex-M4中规定0x0000起始的地址存放的是系统向量表(vector table)。在STM32中0x0000本身并不对应什么物理设备, ==通过配置引脚BOOT[1:0]我们可以控制0x0000映射到地址空间中的其它地址中,也就实现了不同的启动方式。==一共有三种可选的启动方式如表1所示, 从主闪存或者系统存储器启动时,硬件上会把0x0000 0000映射到0x0800 0000或者0x1FFF F000上,这样我们从地址0x0000 0000访问的空间实际上就是主闪存或者系统存储器的空间。 从SRAM启动时,只能在0x2000 0000开始的地址访问SRAM。一般我都是从主闪存启动的,也就是说系统的向量表应当烧写在0x0800 0000的地址上。至于如何从SRAM启动需要查看其他资料todo。 启动模式选择引脚 启动模式 偏移地址 BOOT1 BOOT0 X 0 主闪存(Main Flash Memory) 0x0800 0000 0 1 系统存储器(system memory) 0x1FFF F000 1 1 内置SRAM(Embedded SRAM) 0x2000 0000 ...

May 8, 2022 · 2 min · Rancho

python向bin文件添加CRC32校验码

python向bin文件添加CRC32校验码; 1、keil添加配置,编译后调用脚本生成bin文件 D:\Keil_v5\ARM\ARMCC\bin\fromelf.exe --bin -o fromelf --bin -o "$L@L.bin" "#L" 2、keil添加配置,调用python脚本,向bin文件中插入crc32校验码 python ./crc32_bin.py python脚本如下: # -*- coding:utf-8 -*- import binascii import os import sys def crc2hex(crc): res='' for i in range(4): t=crc & 0xFF crc >>= 8 res='%02X%s' % (t, res) return res inputfile = "E:\IAP\APP\Project\OBJ\STM32F407.bin"#实际存放的bin文件路径 isfile = os.path.isfile(inputfile); print(inputfile); fp = open(inputfile, "r+b") #直接打开一个文件,如果文件不存在则创建文件 filesize = os.path.getsize(inputfile) print("ZI app firmware size:", filesize, "bytes.") #计算bin文件的CRC,首先清空CRC32区域的4个byte fp.seek(0x1c, 0)#从bin文件开始,偏移地址为0x1c的地方存放bin的CRC32 clear4bytes = '00000000' c4 =binascii.unhexlify(clear4bytes) fp.write(c4) #将CRC32存放的区域4bytes清零 fp.seek(0, 0)#从0开始读取整个bin file_content = fp.read()#读整个文件内容到 file_content crc = binascii.crc32(file_content) print('CRC32:', hex(crc)) fp.seek(0x1c, 0)#从bin文件开始,偏移地址为0x1c的地方存放bin的CRC32 #存放计算CRC32四个字节 crcstr_2 = crc2hex(crc) r=binascii.unhexlify(crcstr_2) fp.write(r) fp.close() sys.exit(0)##正常退出 这个脚本我是用在stm32 Bootloader中的,因为经串口或其他方式接收到的程序未必是正确的,而且有时候会加入版本信息什么的,所以加入CRC校验可以方便的检验文件完整性。 ...

May 7, 2022 · 1 min · Rancho

FreeRTOS基础知识

FreeRTOS学习记录; FreeRTOS学习记录 一、任务管理 任务函数: void ATaskFunction( void *pvParameters ); 每一个任务函数都有自己的栈空间、自动变量; 函数里通常是一个死循环; 一般函数有可能跳出死循环,则必须删除函数: void ATaskFunction( void *pvParameters ) { /* 可以像普通函数一样定义变量。用这个函数创建的每个任务实例都有一个属于自己的iVarialbleExample变 量。但如果iVariableExample被定义为static,这一点则不成立 – 这种情况下只存在一个变量,所有的任务实 例将会共享这个变量。 */ int iVariableExample = 0; /* 任务通常实现在一个死循环中。 */ for( ;; ) { /* 完成任务功能的代码将放在这里。 */ } /* 如果任务的具体实现会跳出上面的死循环,则此任务必须在函数运行完之前删除。传入NULL参数表示删除 的是当前任务 */ vTaskDelete( NULL ); } 任务创建函数: portBASE_TYPE xTaskCreate( pdTASK_CODE pvTaskCode, const signed portCHAR * const pcName, unsigned portSHORT usStackDepth, void *pvParameters, unsigned portBASE_TYPE uxPriority, xTaskHandle *pxCreatedTask ); pvTaskCode 任务只是永不退出的 C 函数,实现常通常是一个死循环。参数 pvTaskCode 是一个指向任务的实现函数的指针(效果上仅仅是函数 名)。 ...

May 6, 2022 · 18 min · Rancho