0%

STM32实现W25Q16芯片的擦除、读和写

封装等待函数(通过读取状态寄存器)

void delayBusy()
{
uint8_t D05 = 0x05;
uint8_t result;
EnCS(0);
HAL_SPI_Transmit(&hspi1, &D05, 1, 1000);
HAL_SPI_Receive(&hspi1, &result, 1, 1000);
while((result&0x01) == 1)
{
// HAL_SPI_Transmit(&hspi1, &D05, 1, 1000);
HAL_SPI_Receive(&hspi1, &result, 1, 1000);
}
EnCS(1);
}

注意,手册上说明此处的状态寄存器可以连续读取,也就是在指令开始的时候发送一次0x05即可,之后循环读取,通过将片选信号CS置为高电平停止

擦除函数

void EraseChip()
{
uint8_t D60 = 0x60;
writeEnable();
EnCS(0);
HAL_SPI_Transmit(&hspi1, &D60, 1 ,1000);
EnCS(1);
delayBusy();
}

注意,将芯片内容擦除之前需要先发送一个使能写的命令,在下面的函数中会封装这个功能

然后在函数的最后增加等待芯片空闲的函数,该芯片的擦除时间大概1-2s,不同容量的芯片可能时间不同。

  • 擦除前的读取数据(第二行):
  • image-20220108130401292
  • 擦除后的读取数据
  • image-20220108130417022
  • 可见擦除成功

写使能函数

void writeEnable()
{
uint8_t D06 = 0x06;
EnCS(0);
HAL_SPI_Transmit(&hspi1, &D06, 1, 1000);
EnCS(1);
}

禁止写函数与写使能函数类似,将命令替换为0x04即可

读取函数

image-20220108125308657

void readChip(uint32_t addr, int32_t length)
{
uint8_t D03 = 0x03;
int32_t count = length-1;
uint8_t Haddr;
uint8_t Maddr;
uint8_t Laddr;
Haddr = addr/256/256;
Maddr = (addr/256)&0xff;
Laddr = addr&0xff;
EnCS(0);
HAL_SPI_Transmit(&hspi1, &D03, 1, 1000);
HAL_SPI_Transmit(&hspi1, &Haddr, 1, 1000);
HAL_SPI_Transmit(&hspi1, &Maddr, 1, 1000);
HAL_SPI_Transmit(&hspi1, &Laddr, 1, 1000);
for(count = length-1; count>=0;count--)
{
HAL_SPI_Receive(&hspi1, recBuff+count, 1, 1000);
}
EnCS(1);
}

其中的recBuff是一个大小与length相同的byte数组

注意,与上面类似的是,此处在发送一次读指令之后,芯片会从指定的地址开始自动向前串行发送往后的数据直到CS被置为高电平终止指令,严格的讲从发送的地址开始,可以只发送一次读取指令便读取到芯片的末尾存储空间

写数据函数

image-20220108133158219

写函数的执行逻辑是:

  • 发送一个写使能信号
  • CS置为低电平
  • 发送0x02
  • 发送24位地址
  • 连续发送写入的数据(1-256个,一次最多不超过256个,否则超出的会从头部开始覆盖)
  • CS置为高电平
  • 等待芯片写完
  • 发送禁止写命令
void writeChip(uint32_t addr, uint16_t length)
{
uint8_t D02 = 0x02;
int32_t count = 0;
uint8_t Haddr;
uint8_t Maddr;
uint8_t Laddr;
Haddr = addr/256/256;
Maddr = (addr/256)&0xff;
Laddr = addr&0xff;
writeEnable();
EnCS(0);
HAL_SPI_Transmit(&hspi1, &D02, 1, 1000);
HAL_SPI_Transmit(&hspi1, &Haddr, 1, 1000);
HAL_SPI_Transmit(&hspi1, &Maddr, 1, 1000);
HAL_SPI_Transmit(&hspi1, &Laddr, 1, 1000);
for(count = 0; count<length;count++)
{
HAL_SPI_Transmit(&hspi1, writeBuff+count, 1, 1000);
}
EnCS(1);
delayBusy();
writeDisable();
}

writeBuff是一个长度为length的字节数组

注意:

  • 假如一次写256个字节的话,地址的最后8位务必是0,假如不是的话会导致超出的部分从头部开始覆盖
  • 写入的字节数不能超出从地址位置开始剩余的字节量(比如地址后8位是0的话,剩余的字节就是256)
  • 注意在写完最后一个数据之后,比如将片选信号CS置为高电平,此时芯片会开始写入数据,写入的时候查询BUSY寄存器会得到1,所以使用等待函数等待

执行效果

  • 发送buff定义为uint8_t writeBuff[] = {1,2,3,4,5,6,7,8};注意数组初始化不要漏写[]
  • 发送函数和读取函数
writeChip(0x01,8);
readChip(0x01, 8);
  • 结果
  • image-20220108133835422
  • 可见已经成功写入

使用STM32读取Winbond_W25Q16_Flash芯片的ID和序列号

Cube配置

配置为全双工主设备

image-20220108111641258

image-20220108111113092

使用的是SPI1(根据开发板的接线),Prescaler配置多少都可以,不关键,2和256都行

image-20220108111433160

DMA也可以开,不影响

配置CS引脚,原理图接线为

image-20220108111515938

图中的F_CS是片选引脚,注意低电平有效

程序编写

封装一个片选函数

void EnCS(uint8_t flag)
{
if(flag==1)
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
else
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
}

读取chipID的函数根据手册编写

image-20220108111759707

可见是先把CS置为低电平,然后SPI发送0x90,然后发送3byte的0x00地址,然后接收即可

void ReadChipID(void)
{

uint8_t D00 = 0x00;
uint8_t DFF = 0xFF;
uint8_t D90 = 0x90;
uint8_t D06 = 0x06;
uint8_t count = 0;
uint8_t D60 = 0x60;

EnCS(0);
HAL_SPI_Transmit(&hspi1, &D90, 1, 1000);
HAL_SPI_Transmit(&hspi1, &D00, 1, 1000);
HAL_SPI_Transmit(&hspi1, &D00, 1, 1000);
HAL_SPI_Transmit(&hspi1, &D00, 1, 1000);
HAL_SPI_TransmitReceive(&hspi1,&DFF, recBuff, 1, 1000);
HAL_SPI_TransmitReceive(&hspi1, &DFF, recBuff+1, 1, 1000);
EnCS(1);
}

//然后将接收到的字符串格式化
sprintf(stringToSend, "0x%2x%2x%2x%2x%2x%2x%2x%2x",recBuff[7],recBuff[6],recBuff[5],recBuff[4],recBuff[3],recBuff[2],recBuff[1],recBuff[0]);

注意此处因为函数的接收参数是接受数据存放位置的指针,所以必须使用recBuff + X的形式(recBuff是一个uint8_t数组)而不能用recBuff[X]的形式。

结果如图

image-20220108112619532

同样根据手册编写读取芯片唯一序列号的程序

image-20220108112138116

也是先把CS置为低电平,然后发送指令,然后等待三个数据传输周期(可以向芯片发送0xFF模拟等待延时),然后接收,注意此时必须手动等待,而不能直接使用HAL库的SPI接收函数的接收超时等待功能,因为会接收到错误的结果(比如全是0xFF)。

void ReadUniqueID(void)
{
uint8_t D4B = 0x4B;
int8_t count = 7;
uint8_t DFF = 0xFF;
EnCS(0);
HAL_SPI_Transmit(&hspi1, &D4B, 1, 1000);
HAL_SPI_Transmit(&hspi1, &DFF, 1, 1000);
HAL_SPI_Transmit(&hspi1, &DFF, 1, 1000);
HAL_SPI_Transmit(&hspi1, &DFF, 1, 1000);
HAL_SPI_Transmit(&hspi1, &DFF, 1, 1000);
for(count=7;count>=0;count--)
{
HAL_SPI_Receive(&hspi1, recBuff+count, 1, 1000);
}
EnCS(1);
}

//然后将接收到的字符串格式化
sprintf(stringToSend, "0x%2x%2x%2x%2x%2x%2x%2x%2x",recBuff[7],recBuff[6],recBuff[5],recBuff[4],recBuff[3],recBuff[2],recBuff[1],recBuff[0]);

image-20220108112807141

假如去掉四行HAL_SPI_Transmit(&hspi1, &DFF, 1, 1000);,接收到的序列号将会变成

image-20220108112925406

注意到开头出现了很多ff,因为芯片在此时的4个数据周期中是没有响应的,对应接收到4byte的ff,也就是图上的8个ff

解决Cube新建项目使用UART编译出错的问题

注意stm32f4xx_hal_conf.h中有一些宏定义

image-20220106220834007

此处可能是系统没有将#define HAL_UART_MODULE_ENABLED 取消注释,此处自己删除注释即可正常编译

image-20220106220934356

Linux系统知识

apt命令

apt(Advanced Packaging Tool)是一个在 Debian 和 Ubuntu 中的 Shell 前端软件包管理器。

apt 命令提供了查找、安装、升级、删除某一个、一组甚至全部软件包的命令,而且命令简洁而又好记。

apt 命令执行需要超级管理员权限(root)。

apt [options] [command] [package ...]
  • options:可选,选项包括 -h(帮助),-y(当安装过程提示选择全部为”yes”),-q(不显示安装的过程)等等。
  • command:要进行的操作。
  • package:安装的包名。

用法:

  • 列出所有可更新的软件清单命令:sudo apt update

  • 升级软件包:sudo apt upgrade

    列出可更新的软件包及版本信息:apt list –upgradeable

    升级软件包,升级前先删除需要更新软件包:sudo apt full-upgrade

  • 安装指定的软件命令:sudo apt install

    安装多个软件包:sudo apt install

  • 更新指定的软件命令:sudo apt update

  • 显示软件包具体信息,例如:版本号,安装大小,依赖关系等等:sudo apt show

  • 删除软件包命令:sudo apt remove

  • 清理不再使用的依赖和库文件: sudo apt autoremove

  • 移除软件包及配置文件: sudo apt purge

  • 查找软件包命令: sudo apt search

  • 列出所有已安装的包:apt list –installed

  • 列出所有已安装的包的版本信息:apt list –all-versions

目录结构

image-20220105113244292

  • /bin
    bin 是 Binaries (二进制文件) 的缩写, 这个目录存放着最经常使用的命令

  • /boot:
    这里存放的是启动 Linux 时使用的一些核心文件,包括一些连接文件以及镜像文件。

  • /dev :
    dev 是 Device(设备) 的缩写, 该目录下存放的是 Linux 的外部设备,在 Linux 中**访问设备的方式和访问文件的方式是相同的**。

  • /etc:
    etc 是 Etcetera(等等) 的缩写,这个目录用来存放所有的系统管理所需要的配置文件和子目录

  • /home
    用户的主目录,在 Linux 中,每个用户都有一个自己的目录,一般该目录名是以用户的账号命名的,如上图中的 alice、bob 和 eve。

  • /lib
    lib 是 Library(库) 的缩写这个目录里存放着系统最基本的动态连接共享库,其作用类似于 Windows 里的 DLL 文件。几乎所有的应用程序都需要用到这些共享库。

  • /lost+found
    这个目录一般情况下是空的,当系统非法关机后,这里就存放了一些文件。

  • /media
    linux 系统会自动识别一些设备,例如U盘、光驱等等,当识别后,Linux 会把识别的设备挂载到这个目录下

  • /mnt
    系统提供该目录是为了让用户临时挂载别的文件系统的,我们可以将光驱挂载在 /mnt/ 上,然后进入该目录就可以查看光驱里的内容了。

  • /opt
    opt 是 optional(可选) 的缩写,这是给主机额外安装软件所摆放的目录。比如你安装一个ORACLE数据库则就可以放到这个目录下。默认是空的。

  • /proc
    proc 是 Processes(进程) 的缩写,/proc 是一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,这个目录是一个虚拟的目录,它是系统内存的映射,我们可以通过直接访问这个目录来获取系统信息
    这个目录的内容不在硬盘上而是在内存里,我们也可以直接修改里面的某些文件,比如可以通过下面的命令来屏蔽主机的ping命令,使别人无法ping你的机器:

    echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
  • /root
    该目录为系统管理员,也称作超级权限者的用户主目录

  • /sbin
    s 就是 Super User 的意思,是 Superuser Binaries (超级用户的二进制文件) 的缩写,这里存放的是系统管理员使用的系统管理程序

  • /selinux
    这个目录是 Redhat/CentOS 所特有的目录,Selinux 是一个安全机制,类似于 windows 的防火墙,但是这套机制比较复杂,这个目录就是存放selinux相关的文件的。

  • /srv
    该目录存放一些服务启动之后需要提取的数据。

  • /sys

    这是 Linux2.6 内核的一个很大的变化。该目录下安装了 2.6 内核中新出现的一个文件系统 sysfs 。

    sysfs 文件系统集成了下面3种文件系统的信息:针对进程信息的 proc 文件系统、针对设备的 devfs 文件系统以及针对伪终端的 devpts 文件系统

    该文件系统是内核设备树的一个直观反映。

    当一个内核对象被创建的时候,对应的文件和目录也在内核对象子系统中被创建。

  • /tmp
    tmp 是 temporary(临时) 的缩写这个目录是用来存放一些临时文件的。

  • /usr
    usr 是 unix shared resources(共享资源) 的缩写,这是一个非常重要的目录,用户的很多应用程序和文件都放在这个目录下,类似于 windows 下的 program files 目录

  • /usr/bin:
    系统用户使用的应用程序

  • /usr/sbin:
    超级用户使用的比较高级的管理程序和系统守护程序

  • /usr/src:
    内核源代码默认的放置目录。

  • /var
    var 是 variable(变量) 的缩写,这个目录中存放着在不断扩充着的东西,我们习惯将那些经常被修改的目录放在这个目录下。包括各种日志文件。

  • /run
    是一个临时文件系统,存储系统启动以来的信息。当系统重启时,这个目录下的文件应该被删掉或清除。如果你的系统上有 /var/run 目录,应该让它指向 run。

  • .或者./代表当前目录,..或者../代表上一级目录

  • 如果一个目录或文件名以一个点 . 开始,表示这个目录或文件是一个隐藏目录或文件(如:.bashrc)。即以默认方式查找时,不显示该目录或文件。

  • ~/的含义:主目录

    • image-20220105211153744

文件属性

  • chown (change owner) : 修改所属用户与组。
  • chmod (change mode) : 修改用户的权限。
  • 使用 ll 或者 ls –l 命令来显示一个文件的属性以及文件所属的用户和组,如:
[root@www /]# ls -l
total 64
dr-xr-xr-x 2 root root 4096 Dec 14 2012 bin
dr-xr-xr-x 4 root root 4096 Apr 19 2012 boot
……
  • 文件属性的字符表示:

  • 当为 d 则是目录

  • 当为 - 则是文件;

  • 若是 l 则表示为链接文档(link file);

  • 若是 b 则表示为装置文件里面的可供储存的接口设备(可随机存取装置);

  • 若是 c 则表示为装置文件里面的串行端口设备,例如键盘、鼠标(一次性读取装置)。

  • 接下来的字符中,以三个为一组,且均为 rwx 的三个参数的组合。其中, r 代表可读(read)、 w 代表可写(write)、 x 代表可执行(execute)。 要注意的是,这三个权限的位置不会改变,如果没有权限,就会出现减号 - 而已。

  • image-20220105115020261

  • 文件类型的表示:
    image-20220105115040437

  • 更改属性:

    • chgrp:更改文件属组:chgrp [-R] 属组名 文件名

    • -R:递归更改文件属组,就是在更改某个目录文件的属组时,如果加上-R的参数,那么该目录下的所有文件的属组都会更改。

    • chown:更改文件属主,也可以同时更改文件属组

    • chown [–R] 属主名 文件名chown [-R] 属主名:属组名 文件名

    • Linux文件属性有两种设置方法,一种是数字,一种是符号。

      Linux 文件的基本权限就有九个,分别是 owner/group/others(拥有者/组/其他) 三种身份各有自己的 read/write/execute 权限。

      先复习一下刚刚上面提到的数据:文件的权限字符为: -rwxrwxrwx , 这九个权限是三个三个一组的!其中,我们可以使用数字来代表各个权限,各权限的分数对照表如下:

      • r:4
      • w:2
      • x:1
    • 语法: chmod [-R] xyz 文件或目录

      • xyz : 就是刚刚提到的数字类型的权限属性,为 rwx 属性数值的相加。
      • -R : 进行递归(recursive)的持续变更,亦即连同次目录下的所有文件都会变更
    • 比如chmod 777 .bashrc就是启用.bashrc文件的所有权限

    • 还有一个改变权限的方法,从之前的介绍中我们可以发现,基本上就九个权限分别是:

      • user:用户
      • group:组
      • others:其他
      • 此外, a 则代表 all,即全部的身份。读写的权限可以写成 r, w, x
      • +代表添加,-代表除去,=代表指定(对应的权限)
    • 方法为chmod u=rwx,g=rx,o=r 文件名来指定,其中的rwx等是对应的用户的权限

    • 去掉权限:

      • 例如要拿掉全部人的可执行权限chmod a-x test1
    • 比如

    • image-20220105202406126

    • 此处在chmod a+x test.txt之后,权限可以看见全部增加了x

处理目录

  • ls(英文全拼:list files): 列出目录及文件名
  • cd(英文全拼:change directory):切换目录
  • pwd(英文全拼:print work directory):显示目前的目录
  • mkdir(英文全拼:make directory):创建一个新的目录
  • rmdir(英文全拼:remove directory):删除一个空的目录
  • cp(英文全拼:copy file): 复制文件或目录
  • rm(英文全拼:remove): 删除文件或目录
  • mv(英文全拼:move file): 移动文件与目录,或修改文件与目录的名称

ls列出目录

  • -a :全部的文件,连同隐藏文件( 开头为 . 的文件) 一起列出来(常用)
  • -d :仅列出目录本身,而不是列出目录内的文件数据(常用)
  • -l :长数据串列出,包含文件的属性与权限等等数据;(常用)

cd变换路径

  • cd [相对路径或绝对路径]

pwd(显示当前所在的目录)

  • -P :显示出确实的路径,而非使用连结 (link) 路径。
  • image-20220105203806376

mkdir (创建新目录)

  • mkdir [-mp] 目录名称
  • -m :配置文件的权限喔!直接配置,不需要看默认权限 (umask) 的脸色~
    • 比如mkdir -m 711 test2
  • -p :帮助你直接将所需要的目录(包含上一级目录)递归创建起来!
    • 比如mkdir test1/test2/test3/test4不能创建的时候,使用-p选项即可
    • image-20220105204407469

rmdir (删除空的目录)

  • rmdir [-p] 目录名称
  • -p :从该目录起,一次删除多级空目录
    • image-20220105205146656

cp (复制文件或目录)

  • cp [-adfilprsu] 来源档(source) 目标档(destination)
  • cp [options] source1 source2 source3 .... directory
  • -a:相当於 -pdr 的意思,至於 pdr 请参考下列说明;(常用)
  • -d:若来源档为连结档的属性(link file),则复制连结档属性而非文件本身;
  • -f:为强制(force)的意思,若目标文件已经存在且无法开启,则移除后再尝试一次;
  • -i:若目标档(destination)已经存在时,在覆盖时会先询问动作的进行(常用)
  • -l:进行硬式连结(hard link)的连结档创建,而非复制文件本身;
  • -p:连同文件的属性一起复制过去,而非使用默认属性(备份常用);
  • -r:递归持续复制,用於目录的复制行为;(常用)
  • -s:复制成为符号连结档 (symbolic link),亦即『捷径』文件;
  • -u:若 destination 比 source 旧才升级 destination !

rm (移除文件或目录)

  • rm [-fir] 文件或目录
  • -f :就是 force 的意思,忽略不存在的文件,不会出现警告信息;
  • -i :互动模式,在删除前会询问使用者是否动作
  • -r :递归删除啊!最常用在目录的删除了!这是非常危险的选项!!!

mv (移动文件与目录,或修改名称)

  • mv [-fiu] source destination
  • mv [options] source1 source2 source3 .... directory
  • -f :force 强制的意思,如果目标文件已经存在,不会询问而直接覆盖;
  • -i :若目标文件 (destination) 已经存在时,就会询问是否覆盖!
  • -u :若目标文件已经存在,且 source 比较新,才会升级 (update)
  • 更改文件夹名称image-20220105212045584
    • 也就是mv 原名称 现名称

Linux 文件内容查看

  • cat 由第一行开始显示文件内容
  • tac 从最后一行开始显示,可以看出 tac 是 cat 的倒着写!
  • nl 显示的时候,顺道输出行号!
  • more 一页一页的显示文件内容
  • less 与 more 类似,但是比 more 更好的是,他可以往前翻页!
  • head 只看头几行
  • tail 只看尾巴几行

Linux 文件链接

硬连接

硬连接指通过索引节点来进行连接。在 Linux 的文件系统中,保存在磁盘分区中的文件不管是什么类型都给它分配一个编号,称为索引节点号(Inode Index)。在 Linux 中,多个文件名指向同一索引节点是存在的。比如:A 是 B 的硬链接(A 和 B 都是文件名),则 A 的目录项中的 inode 节点号与 B 的目录项中的 inode 节点号相同,即一个 inode 节点对应两个不同的文件名两个文件名指向同一个文件,A 和 B 对文件系统来说是完全平等的。删除其中任何一个都不会影响另外一个的访问

硬连接的作用是允许一个文件拥有多个有效路径名,这样用户就可以建立硬连接到重要文件,以防止“误删”的功能。其原因如上所述,因为对应该目录的索引节点有一个以上的连接。只删除一个连接并不影响索引节点本身和其它的连接,只有当最后一个连接被删除后,文件的数据块及目录的连接才会被释放。也就是说,文件真正删除的条件是与之相关的所有硬连接文件均被删除

软链接

另外一种连接称之为符号连接(Symbolic Link),也叫软连接。软链接文件有类似于 Windows 的快捷方式。它实际上是一个特殊的文件。在符号连接中,文件实际上是一个文本文件,其中包含的有另一文件的位置信息。比如:A 是 B 的软链接(A 和 B 都是文件名),A 的目录项中的 inode 节点号与 B 的目录项中的 inode 节点号不相同,A 和 B 指向的是两个不同的 inode,继而指向两块不同的数据块。但是 A 的数据块中存放的只是 B 的路径名(可以根据这个找到 B 的目录项)。A 和 B 之间是“主从”关系,如果 B 被删除了,A 仍然存在(因为两个是不同的文件),但指向的是一个无效的链接

[oracle@Linux]$ touch f1          #创建一个测试文件f1
[oracle@Linux]$ ln f1 f2 #创建f1的一个硬连接文件f2
[oracle@Linux]$ ln -s f1 f3 #创建f1的一个符号连接文件f3
[oracle@Linux]$ ls -li # -i参数显示文件的inode节点信息
total 0
9797648 -rw-r--r-- 2 oracle oinstall 0 Apr 21 08:11 f1
9797648 -rw-r--r-- 2 oracle oinstall 0 Apr 21 08:11 f2
9797649 lrwxrwxrwx 1 oracle oinstall 2 Apr 21 08:11 f3 -> f1

从上面的结果中可以看出,硬连接文件 f2 与原文件 f1 的 inode 节点相同,均为 9797648,然而符号连接文件的 inode 节点不同。

  • 注意创建硬链接文件的语法为ln 文件 硬链接
  • 创建符号链接文件的语法为ln -s 文件 符号链接
  • ls -li可以显示文件的inode节点信息
[oracle@Linux]$ echo "I am f1 file" >>f1
[oracle@Linux]$ cat f1
I am f1 file
[oracle@Linux]$ cat f2
I am f1 file
[oracle@Linux]$ cat f3
I am f1 file
[oracle@Linux]$ rm -f f1
[oracle@Linux]$ cat f2
I am f1 file
[oracle@Linux]$ cat f3
cat: f3: No such file or directory
  • 通过上面的测试可以看出:当删除原始文件 f1 后,硬连接 f2 不受影响,但是符号连接 f3 文件无效

Linux 磁盘管理

df

检查文件系统的磁盘空间占用情况。可以利用该命令来获取硬盘被占用了多少空间,目前还剩下多少空间等信息。

  • df [-ahikHTm] [目录或文件名]
  • -a :列出所有的文件系统,包括系统特有的 /proc 等文件系统;
  • -k :以 KBytes 的容量显示各文件系统;
  • -m :以 MBytes 的容量显示各文件系统;
  • -h :以人们较易阅读的 GBytes, MBytes, KBytes 等格式自行显示;
  • -H :以 M=1000K 取代 M=1024K 的进位方式;
  • -T :显示文件系统类型, 连同该 partition 的 filesystem 名称 (例如 ext3) 也列出;
  • -i :不用硬盘容量,而以 inode 的数量来显示
[root@www ~]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/hdc2 9.5G 3.7G 5.4G 41% /
/dev/hdc3 4.8G 139M 4.4G 4% /home
/dev/hdc1 99M 11M 83M 12% /boot
tmpfs 363M 0 363M 0% /dev/shm

du

Linux du 命令也是查看使用空间的,但是与 df 命令不同的是 Linux du 命令是对文件和目录磁盘使用的空间的查看,还是和df命令有一些区别的,这里介绍 Linux du 命令。

  • du [-ahskm] 文件或目录名称

  • -a :列出所有的文件与目录容量,因为默认仅统计目录底下的文件量而已。

  • -h :以人们较易读的容量格式 (G/M) 显示;

  • -s :列出总量而已,而不列出每个各别的目录占用容量;

  • -S :不包括子目录下的总计,与 -s 有点差别。

  • -k :以 KBytes 列出容量显示;

  • -m :以 MBytes 列出容量显示;

fdisk

fdisk 是 Linux 的磁盘分区表操作工具。

  • fdisk [-l] 装置名称需要超级用户(sudo)权限

  • -l :输出后面接的装置所有的分区内容。若仅有 fdisk -l 时, 则系统将会把整个系统内能够搜寻到的装置的分区均列出来。

  • image-20220105221448829

磁盘格式化

  • mkfs [-t 文件系统格式] 装置文件名
  • -t :可以接文件系统格式,例如 ext3, ext2, vfat 等(系统有支持才会生效)

磁盘检验

fsck(file system check)用来检查和维护不一致的文件系统。

若系统掉电或磁盘发生问题,可利用fsck命令对文件系统进行检查。

  • fsck [-t 文件系统] [-ACay] 装置名称
  • -t : 给定档案系统的型式,若在 /etc/fstab 中已有定义或 kernel 本身已支援的则不需加上此参数
  • -s : 依序一个一个地执行 fsck 的指令来检查
  • -A : 对/etc/fstab 中所有列出来的 分区(partition)做检查
  • -C : 显示完整的检查进度
  • -d : 打印出 e2fsck 的 debug 结果
  • -p : 同时有 -A 条件时,同时有多个 fsck 的检查一起执行
  • -R : 同时有 -A 条件时,省略 / 不检查
  • -V : 详细显示模式
  • -a : 如果检查有错则自动修复
  • -r : 如果检查有错则由使用者回答是否修复
  • -y : 选项指定检测每个文件是自动输入yes,在不确定那些是不正常的时候,可以执行 # fsck -y 全部检查修复。

磁盘挂载和删除

  • Linux 的磁盘挂载使用 mount 命令,卸载使用 umount 命令。
  • mount [-t 文件系统] [-L Label名] [-o 额外选项] [-n] 装置文件名 挂载点
  • umount [-fn] 装置文件名或挂载点

参考https://www.runoob.com/linux/linux-file-content-manage.html

Linux初步学习(四)

函数

[ function ] funname [()]

{

action;

[return int;]

}
  • 1、可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。
  • 2、参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return后跟数值n(0-255)
#!/bin/bash
# author:菜鸟教程
# url:www.runoob.com

demoFun(){
echo "这是我的第一个 shell 函数!"
}
echo "-----函数开始执行-----"
demoFun
echo "-----函数执行完毕-----"

传递参数

  • 在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数等等
#!/bin/bash
# author:菜鸟教程
# url:www.runoob.com

funWithParam(){
echo "第一个参数为 $1 !"
echo "第二个参数为 $2 !"
echo "第十个参数为 $10 !"
echo "第十个参数为 ${10} !"
echo "第十一个参数为 ${11} !"
echo "参数总数有 $# 个!"
echo "作为一个字符串输出所有参数 $* !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73
  • 注意,$10 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。
参数处理 说明
$# 传递到脚本或函数的参数个数
$* 以一个单字符串显示所有向脚本传递的参数
$$ 脚本运行的当前进程ID号
$! 后台运行的最后一个进程的ID号
$@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。
$- 显示Shell使用的当前选项,与set命令功能相同。
$? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

Linux初步学习(三)

shell

test

  • Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。

数值

参数 说明
-eq 等于则为真
-ne 不等于则为真
-gt 大于则为真
-ge 大于等于则为真
-lt 小于则为真
-le 小于等于则为真
num1=100
num2=100
if test $[num1] -eq $[num2]
then
echo '两个数相等!'
else
echo '两个数不相等!'
fi
  • []中可以执行算术运算比如加减等

字符串

  • 参数 说明
    = 等于则为真
    != 不相等则为真
    -z 字符串 字符串的长度为零则为真
    -n 字符串 字符串的长度不为零则为真
  • 实例

num1="ru1noob"
num2="runoob"
if test $num1 = $num2
then
echo '两个字符串相等!'
else
echo '两个字符串不相等!'
fi

文件

  • 参数 说明
    -e 文件名 如果文件存在则为真
    -r 文件名 如果文件存在且可读则为真
    -w 文件名 如果文件存在且可写则为真
    -x 文件名 如果文件存在且可执行则为真
    -s 文件名 如果文件存在且至少有一个字符则为真
    -d 文件名 如果文件存在且为目录则为真
    -f 文件名 如果文件存在且为普通文件则为真
    -c 文件名 如果文件存在且为字符型特殊文件则为真
    -b 文件名 如果文件存在且为块特殊文件则为真
  • 使用例

cd /bin
if test -e ./bash
then
echo '文件已存在!'
else
echo '文件不存在!'
fi

Shell 还提供了与( -a )、或( -o )、非( ! )三个逻辑操作符用于将测试条件连接起来,其优先级为: ! 最高, -a 次之, -o 最低。

  • 比如
cd /bin
if test -e ./notFile -o -e ./bash
then
echo '至少有一个文件存在!'
else
echo '两个文件都不存在'
fi

分支和循环

if-else

if condition1
then
command1
elif condition2
then
command2
else
commandN
fi

for循环

for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done
  • 针对字符串的特殊用法(不加引号)
for str in This is a string
do
echo $str
done
  • 输出
This
is
a
string

while 语句

while condition
do
command
done
  • while读取键盘信息
echo '按下 <CTRL-D> 退出'
echo -n '输入你最喜欢的网站名: '
while read FILM
do
echo "是的!$FILM 是一个好网站"
done

无限循环

while :
do
command
done
while true
do
command
done
for (( ; ; ))

until循环

until 循环执行一系列命令直至条件为 true 时停止。

until condition
do
command
done

case和easc

类似于switch case语句

casein
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac
  • 其他写法
#!/bin/bash
while :
do
echo -n "输入 1 到 5 之间的数字:"
read aNum
case $aNum in
1|2|3|4|5) echo "你输入的数字为 $aNum!"
;;
*) echo "你输入的数字不是 1 到 5 之间的! 游戏结束"
break
;;
esac
done
  • 此处注意多个输入对应一个case的时候,使用|分隔,蒋多个输入列为一种情况

break和continue

类似于其他语言,略

Linux初步学习(二)

shell

向shell脚本传参

echo "Shell 传递参数实例!";
echo "执行的文件名:$0";
echo "第一个参数为:$1";
echo "第二个参数为:$2";
echo "第三个参数为:$3";
参数处理 说明
$# 传递到脚本的参数个数
$* 以一个单字符串显示所有向脚本传递的参数。 如”$*”用「”」括起来的情况、以”$1 $2 … $n”的形式输出所有参数。
$$ 脚本运行的当前进程ID号
$! 后台运行的最后一个进程的ID号
$@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。 如”$@”用「”」括起来的情况、以”$1” “$2” … “$n” 的形式输出所有参数。
$- 显示Shell使用的当前选项,与set命令功能相同。
$? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

实例

image-20220102172744910

$ 与 $@ 区别:*

  • 相同点:都是引用所有参数。
  • 不同点:只有在双引号中体现出来。假设在脚本运行时写了三个参数 1、2、3,,则 “ * “ 等价于 “1 2 3”(传递了一个参数),而 “@” 等价于 “1” “2” “3”(传递了三个参数)。

演示

#!/bin/bash
# author:菜鸟教程
# url:www.runoob.com

echo "-- \$* 演示 ---"
for i in "$*"; do
echo $i
done

echo "-- \$@ 演示 ---"
for i in "$@"; do
echo $i
done

输出

image-20220102173644362


shell 数组

获取元素个数

$ chmod +x test.sh 
$ ./test.sh
数组元素个数为: 4
数组元素个数为: 4

数组长度

echo "数组元素个数为: ${#my_array[*]}"
echo "数组元素个数为: ${#my_array[@]}"

数组的值可以是变量

A=1
my_array=($A B C D)
echo "第一个元素为: ${my_array[0]}"
echo "第二个元素为: ${my_array[1]}"
echo "第三个元素为: ${my_array[2]}"
echo "第四个元素为: ${my_array[3]}"

shell 运算符

expr:expr是计算表达式的工具,比如

val = `expr 2 + 2`
  • 表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2,这与我们熟悉的大多数编程语言不一样。
  • 完整的表达式要被反引号 包含,注意这个字符不是常用的单引号,在 Esc 键下边。

if表达式

if [ $a == $b ]
then
echo "a 等于 b"
fi
if [ $a != $b ]
then
echo "a 不等于 b"
fi

if [ $a -le $b ]
then
echo "$a -le $b: a 小于或等于 b"
else
echo "$a -le $b: a 大于 b"
fi

关系运算符

-eq 检测两个数是否相等,相等返回 true。 [ $a -eq $b ] 返回 false。
-ne 检测两个数是否不相等,不相等返回 true。 [ $a -ne $b ] 返回 true。
-gt 检测左边的数是否大于右边的,如果是,则返回 true。 [ $a -gt $b ] 返回 false。
-lt 检测左边的数是否小于右边的,如果是,则返回 true。 [ $a -lt $b ] 返回 true。
-ge 检测左边的数是否大于等于右边的,如果是,则返回 true。 [ $a -ge $b ] 返回 false。
-le 检测左边的数是否小于等于右边的,如果是,则返回 true。 [ $a -le $b ] 返回 true。

布尔运算符

! 非运算,表达式为 true 则返回 false,否则返回 true。 [ ! false ] 返回 true。
-o 或运算,有一个表达式为 true 则返回 true。 [ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a 与运算,两个表达式都为 true 才返回 true。 [ $a -lt 20 -a $b -gt 100 ] 返回 false。

逻辑运算符

运算符 说明 举例
&& 逻辑的 AND [[ $a -lt 100 && $b -gt 100 ]] 返回 false
|| 逻辑的 OR [[ $a -lt 100 || $b -gt 100 ]] 返回 true

字符串运算符

运算符 说明 举例
= 检测两个字符串是否相等,相等返回 true。 [ $a = $b ] 返回 false。
!= 检测两个字符串是否不相等,不相等返回 true。 [ $a != $b ] 返回 true。
-z 检测字符串长度是否为0,为0返回 true。 [ -z $a ] 返回 false。
-n 检测字符串长度是否不为 0,不为 0 返回 true。 [ -n “$a” ] 返回 true。
$ 检测字符串是否为空,不为空返回 true。 [ $a ] 返回 true。

使用例

#!/bin/bash
# author:菜鸟教程
# url:www.runoob.com

a="abc"
b="efg"

if [ $a = $b ]
then
echo "$a = $b : a 等于 b"
else
echo "$a = $b: a 不等于 b"
fi
if [ $a != $b ]
then
echo "$a != $b : a 不等于 b"
else
echo "$a != $b: a 等于 b"
fi
if [ -z $a ]
then
echo "-z $a : 字符串长度为 0"
else
echo "-z $a : 字符串长度不为 0"
fi
if [ -n "$a" ]
then
echo "-n $a : 字符串长度不为 0"
else
echo "-n $a : 字符串长度为 0"
fi
if [ $a ]
then
echo "$a : 字符串不为空"
else
echo "$a : 字符串为空"
fi

文件测试运算符

操作符 说明 举例
-b file 检测文件是否是块设备文件,如果是,则返回 true。 [ -b $file ] 返回 false。
-c file 检测文件是否是字符设备文件,如果是,则返回 true。 [ -c $file ] 返回 false。
-d file 检测文件是否是目录,如果是,则返回 true。 [ -d $file ] 返回 false。
-f file 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 [ -f $file ] 返回 true。
-g file 检测文件是否设置了 SGID 位,如果是,则返回 true。 [ -g $file ] 返回 false。
-k file 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 [ -k $file ] 返回 false。
-p file 检测文件是否是有名管道,如果是,则返回 true。 [ -p $file ] 返回 false。
-u file 检测文件是否设置了 SUID 位,如果是,则返回 true。 [ -u $file ] 返回 false。
-r file 检测文件是否可读,如果是,则返回 true。 [ -r $file ] 返回 true。
-w file 检测文件是否可写,如果是,则返回 true。 [ -w $file ] 返回 true。
-x file 检测文件是否可执行,如果是,则返回 true。 [ -x $file ] 返回 true。
-s file 检测文件是否为空(文件大小是否大于0),不为空返回 true。 [ -s $file ] 返回 true。
-e file 检测文件(包括目录)是否存在,如果是,则返回 true。 [ -e $file ] 返回 true。
  • -S: 判断某文件是否 socket。
  • -L: 检测文件是否存在并且是一个符号链接。

使用例

#!/bin/bash
# author:菜鸟教程
# url:www.runoob.com

file="/var/www/runoob/test.sh"
if [ -r $file ]
then
echo "文件可读"
else
echo "文件不可读"
fi
if [ -w $file ]
then
echo "文件可写"
else
echo "文件不可写"
fi
if [ -x $file ]
then
echo "文件可执行"
else
echo "文件不可执行"
fi
if [ -f $file ]
then
echo "文件为普通文件"
else
echo "文件为特殊文件"
fi
if [ -d $file ]
then
echo "文件是个目录"
else
echo "文件不是个目录"
fi
if [ -s $file ]
then
echo "文件不为空"
else
echo "文件为空"
fi
if [ -e $file ]
then
echo "文件存在"
else
echo "文件不存在"
fi

命令行输入和输出

echo,显示转义字符使用\

read,从标准输入获取一行,并且将其赋给变量

比如read name将一行内容赋值给name

  • echo开启转义字符:-e

比如

echo -e "OK! \n" # -e 开启转义
echo "It is a test"

会在OK后换行

  • 不换行
#!/bin/sh
echo -e "OK! \c" # -e 开启转义 \c 不换行
echo "It is a test"
  • 结果定向至文件
echo "It is a test" > myfile
  • 原样输出字符串,不考虑变量和转义:
echo '$name\"'
  • 显示对应的命令执行的结果(反引号)
echo `date`

输出

Thu Jul 24 10:08:46 CST 2014

printf

printf  format-string  [arguments...]
  • 使用例
printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg  
printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234
printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543
printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876
  • 输出

  • 姓名     性别   体重kg
    郭靖     男      66.12
    杨过     男      48.65
    郭芙     女      47.99
    

%s %c %d %f 都是格式替代符,%s 输出一个字符串,%d 整型输出,%c 输出一个字符,%f 输出实数,以小数形式输出。

%-10s 指一个宽度为 10 个字符(- 表示左对齐,没有则表示右对齐),任何字符都会被显示在 10 个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。

%-4.2f 指格式化为小数,其中 .2 指保留2位小数。

  • 单引号与双引号效果一样

  • 没有引号也可以输出

  • 格式只指定的参数比传入的需要格式化输出的参数少的时候,多出的参数仍然会按照该格式输出,format-string 被重用,类似于printf被重复使用多次直到所有参数都被格式化输出为止

  • 转义序列

  • 序列 说明
    \a 警告字符,通常为ASCII的BEL字符
    \b 后退
    \c 抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略
    \f 换页(formfeed)
    \n 换行
    \r 回车(Carriage return)
    \t 水平制表符
    \v 垂直制表符
    \ 一个字面上的反斜杠字符
    \ddd 表示1到3位数八进制值的字符。仅在格式字符串中有效
    \0ddd 表示1到3位的八进制值字符

Linux初步学习(一)

菜鸟教程入口:
https://www.runoob.com/linux/linux-tutorial.html

shell

https://www.runoob.com/linux/linux-shell-variable.html

  • Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。

  • Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。

  • Ken Thompson 的 sh 是第一种 Unix Shell,Windows Explorer 是一个典型的图形界面 Shell。

shell脚本的扩展名为.sh

  • 指定shell 解释器

#!/bin/bash

#!符号后面指定

  • 运行脚本

    chmod +x ./test.sh  #使脚本具有执行权限
    ./test.sh #执行脚本
  • 注意此处运行的时候必须写成./文件名的形式,直接写的话Linux会去Path里面寻找这个文件,而只有 /bin, /sbin, /usr/bin,/usr/sbin 等在 PATH 里,你的当前目录通常不在 PATH 里,所以写成 test.sh 是会找不到命令的,要用 ./test.sh 告诉系统说,就在当前目录找。

  • 或者作为解释器参数传给解释器以进行运行

    /bin/sh test.sh

第一个shell程序

#!/bin/bash
echo "Hello World !"

echo是控制台输出

image-20220102115757607

shell 变量

  • 变量名不能有$

  • 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。

  • 中间不能有空格,可以使用下划线 **_**。

  • 不能使用标点符号。

  • 不能使用bash里的关键字(可用help命令查看保留关键字)。

变量赋值

显式=,或者

for file in `ls /etc`
或者
for file in $(ls /etc)

以上语句将 /etc 下目录的文件名循环出来。

变量使用

  • 使用一个定义过的变量,只要在变量名前面加美元符号即可
your_name="qinjx"
echo $your_name
echo ${your_name}

花括号的作用是提示编译器这是个变量名

比如

for skill in Ada Coffe Action Java; do
echo "I am good at ${skill}Script"
done

如果不给skill变量加花括号,写成echo “I am good at $skillScript”,解释器就会把$skillScript当成一个变量(其值为空),代码执行结果就不是我们期望的样子了。

  • 加上:image-20220102121030726

  • 不加但是加了空格:

  • image-20220102121227839

花括号加不加都行,不加的时候注意用空格区分开

变量只读

  • 使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。
#!/bin/bash
myUrl="https://www.google.com"
readonly myUrl
myUrl="https://www.runoob.com"

变量删除

unset variable_name

变量类型

  • 1) 局部变量 局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
  • 2) 环境变量 所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
  • 3) shell变量 shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行

字符串

单引号

  • 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
  • 单引号字串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,作为字符串拼接使用。
str='this is a string'

双引号

  • 双引号里可以有变量
  • 双引号里可以出现转义字符
your_name="runoob"
str="Hello, I know you are \"$your_name\"! \n"
echo -e $str

字符串拼接

your_name="runoob"
# 使用双引号拼接
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"
echo $greeting $greeting_1
# 使用单引号拼接
greeting_2='hello, '$your_name' !'
greeting_3='hello, ${your_name} !'
echo $greeting_2 $greeting_3
  • 直接拼接,不需要逗号或者加号等

子字符串

string="runoob is a great site"
echo ${string:1:4} # 输出 unoo

查找字符串

string="runoob is a great site"
echo `expr index "$string" io` # 输出 4
  • 注意上面的expr…是反引号,不是单引号

字符串长度

string="abcd"
echo ${#string} #输出 4

字符串长度的其他方法

string="hello,everyone my name is xiaoming"
expr length "$string"

image-20220102142331661

输出6

数组

数组名=(值1 值2 ... 值n)
array_name=(value0 value1 value2 value3)
array_name=(
value0
value1
value2
value3
)

单独定义变量

array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen

读数组内容

${数组名[下标]}
valuen=${array_name[n]}

取得数组全部内容

echo ${array_name[@]}

取得数组长度

# 取得数组元素的个数
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得数组单个元素的长度
lengthn=${#array_name[n]}

注意,可能数组会出现image-20220102141720725这是因为 ubuntu 模式使用的是 dash 模式,如果不想报错使用 bash 模式

的报错,此时使用命令 bash xxx.sh 或者 将脚本第一行改为 *#!/bin/bash,执行./example.sh也可以

输出数组长度的时候可能不对,此时输出的是1

image-20220102142013809

此时也是1

image-20220102142050921

此代码的输出为

array=(0,1,2,3,4,5,6)
echo ${#array[@]}
echo $array
echo ${array[*]}

image-20220102142905800

  • 可以通过给数组设置下标范围以外的单元的内容来增加数组的长度

注意,使用指定下标修改数组比如array_name[0]=value0会丢弃该下标后面的其他内容

注意,代码不能随意包含空格,比如等号两边,可能会引起错误

字符串截取

参考 https://www.runoob.com/linux/linux-shell-variable.html 第一篇笔记

读取用户输入

read [-options] [variable...]
read -p "input a val:" a    #获取键盘输入的 a 变量数字
read -p "input b val:" b #获取键盘输入的 b 变量数字
r=$[a+b] #计算a+b的结果 赋值给r 不能有空格
echo "result = ${r}" #输出显示结果 r

shell注释

# 开头的行就是注释,会被解释器忽略。

  • 多行注释
:<<EOF
注释内容...
注释内容...
注释内容...
EOF

或者任何符号

:<<'
注释内容...
注释内容...
注释内容...
'

:<<!
注释内容...
注释内容...
注释内容...
!

freeRTOS中断和信号量

使用freeRTOS中断与使用正常的中断区别不大(不使用FreeRTOS自带的API),直接使用系统给的中断函数即可

  • 手动启动这两个

  • image-20211220221301553

  • 注意,使用EXTI的时候(外部中断),需要自己手动定义中断处理函数

    void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
    {

    }

    系统没有给出这个函数的定义,可以自己在任意文件中定义

  • 设置的优先级比系统(RTOS)的话,中断处理函数就不会被系统打断执行

  • 注意,在中断函数中调用FreeRTOS的API的时候,需要采用Fron_ISR的API。FreeRTOS对于一些系统API函数提供两种版本,一种是供任务调用的,一种是供中断调用的(Interrupt Safe API)。由中断调用的API函数后缀上会有“FromISR”。

信号量

  • 简单而言就是一个进程通过一个信号变量指示另一个线程是阻塞还是运行,详见https://zhuanlan.zhihu.com/p/139469342

  • 有两种调用方式,一种是使用CMSIS_OS中的osSemaphoreId osSemaphoreCreate (const osSemaphoreDef_t *semaphore_def, int32_t count);int32_t osSemaphoreWait (osSemaphoreId semaphore_id, uint32_t millisec);osStatus osSemaphoreRelease (osSemaphoreId semaphore_id);函数

  • 或者是手动#include "semphr.h",然后使用知乎网页上的SemaphoreHandle_t xSemaphoreCreateBinary( void )BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait )BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );以及中断函数中的BaseType_t xSemaphoreGiveFromISR( SemaphoreHandle_t xSemaphore, BaseType_t *pxHigherPriorityTaskWoken )

  • 注意,xSemaphoreGiveFromISR函数的第二个参数是可选的,不需要的时候可以给NULL,具体作用详见https://blog.csdn.net/weixin_45045399/article/details/103238514

  • xTicksToWait 如果信号量不可用的话任务处于阻塞状态的最长时间,设置为 portMAX_DELAY的话任务会一直处于阻塞状态直到信号量可用,设置为0的话如果信号量不可用的话会直接返回

  • 假如准备在对应的中断中调用系统的API的话,注意勾选这个选项并且调整相应的中断优先级否则会卡死

  • image-20211220234334003

  • 经过测试,虽然osSemaphoreId与FreeRTOS原生的API要求的SemaphoreHandle_t不同,但是两种API都可以运行,直接替换即可。

优先级倒置

参考知乎文章https://zhuanlan.zhihu.com/p/139469342

互斥量

同上

关键区

同上