<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Posts on watermeko&#39;s blog</title>
    <link>https://blog.watermeko.icu/post/</link>
    <description>Recent content in Posts on watermeko&#39;s blog</description>
    <generator>Hugo -- 0.128.0</generator>
    <language>en</language>
    <copyright>watermeko</copyright>
    <lastBuildDate>Fri, 05 Jun 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://blog.watermeko.icu/post/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>FreeRTOS笔记</title>
      <link>https://blog.watermeko.icu/post/freertosnote/</link>
      <pubDate>Fri, 05 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://blog.watermeko.icu/post/freertosnote/</guid>
      <description>前言 基于ESP32S+ESP-IDF的FreeRTOS笔记。
FreeRTOS 里函数名前缀通常表示返回值类型或返回值含义，属于一种命名约定，常见如下：
前缀 典型返回类型 含义 v void 无返回值 x BaseType_t 基础有符号类型，常用于返回状态、布尔值或错误码 ux UBaseType_t 无符号基础类型，常用于返回数量、索引、优先级等 ul uint32_t / unsigned long 32 位无符号整数 us uint16_t / unsigned short 16 位无符号整数 uc uint8_t / unsigned char 8 位无符号整数 c char 字符类型 pc char * 指向字符的指针，常用于字符串 puc uint8_t * 指向无符号 8 位数据的指针 pv void * 通用指针 px 指向某种结构体的指针 常用于 FreeRTOS 内部结构体指针 e 枚举类型 返回枚举值 prv 通常是 static 私有函数 FreeRTOS 内部私有函数，不强调返回值类型 系统启动流程 flowchart LR entry[&amp;#34;ENTRY(call_start_cpu0)&amp;lt;br/&amp;gt;&amp;lt;small&amp;gt;esp_system/sections.</description>
    </item>
    <item>
      <title>嵌入式Linux-应用开发笔记</title>
      <link>https://blog.watermeko.icu/post/embeddedlinuxapplication/</link>
      <pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://blog.watermeko.icu/post/embeddedlinuxapplication/</guid>
      <description>进程 常用命令 pstree # 查看进程树状图 ps -aux # 查看所有用户的进程，-ux查看当前用户进程 进程状态 状态 说明 R 运行状态。严格来说，应该是&amp;quot;可运行状态&amp;quot;，即表示进程在运行队列中，处于正在执行或即将运行状态，只有在该状态的进程才可能在 CPU 上运行，而同一时刻可能有多个进程处于可运行状态。 S 可中断的睡眠状态。处于这个状态的进程因为等待某种事件的发生而被挂起，比如进程在等待信号。 D 不可中断的睡眠状态。通常是在等待输入或输出（I/O）完成，处于这种状态的进程不能响应异步信号。 T 停止状态。通常是被shell的工作信号控制，或因为它被追踪，进程正处于调试器的控制之下。 Z 退出状态。进程成为僵尸进程。 X 退出状态。进程即将被回收。 s 进程是会话其首进程。 l 进程是多线程的。 + 进程属于前台进程组。 &amp;lt; 高优先级任务。 创建进程 所有进程都基于一个父进程分叉出来，最初始的父进程为init。
system() system()用于在程序中调用 shell 执行一条命令，内部通常会通过 fork() 创建子进程，再由子进程调用 exec() 执行 /bin/sh -c command。调用者会等待命令执行结束，并获得其退出状态。
#include &amp;lt;sys/types.h&amp;gt; #include &amp;lt;unistd.h&amp;gt; #include &amp;lt;stdio.h&amp;gt; #include &amp;lt;stdlib.h&amp;gt; int main(void) { pid_t result; printf(&amp;#34;This is a system demo!\n\n&amp;#34;); /*调用 system()函数*/ result = system(&amp;#34;ls -l&amp;#34;); printf(&amp;#34;Done!</description>
    </item>
    <item>
      <title>移植FreeRTOS到我的SoC（一）基本移植</title>
      <link>https://blog.watermeko.icu/post/portfreertos1/</link>
      <pubDate>Wed, 25 Mar 2026 00:00:00 +0000</pubDate>
      <guid>https://blog.watermeko.icu/post/portfreertos1/</guid>
      <description>硬件准备 一、实现一个mtime外设作为RISC-V标准机器定时器中断源
src/rtl/peripheral/mtime_mmio.sv
`include &amp;#34;../lib/soc_pkg.sv&amp;#34; `include &amp;#34;../lib/bus_if.sv&amp;#34; module mtime_mmio ( input logic clk, input logic rst_n, output logic timer_irq, simple_bus_if.slave bus ); import soc_pkg::*; logic sel_mtime_lo, sel_mtime_hi, sel_mtimecmp_lo, sel_mtimecmp_hi; always_comb begin sel_mtime_lo = (align_word(bus.addr) == IO_MTIME_LO_ADDR); sel_mtime_hi = (align_word(bus.addr) == IO_MTIME_HI_ADDR); sel_mtimecmp_lo = (align_word(bus.addr) == IO_MTIMECMP_LO_ADDR); sel_mtimecmp_hi = (align_word(bus.addr) == IO_MTIMECMP_HI_ADDR); end logic [63:0] mtime; logic [63:0] mtimecmp; always_ff @(posedge clk or negedge rst_n) begin if (!rst_n) begin mtime &amp;lt;= 64&amp;#39;d0; mtimecmp &amp;lt;= 64&amp;#39;hFFFF_FFFF_FFFF_FFFF; end else begin mtime &amp;lt;= mtime + 64&amp;#39;d1; if (bus.</description>
    </item>
    <item>
      <title>tmux笔记</title>
      <link>https://blog.watermeko.icu/post/tmux/</link>
      <pubDate>Thu, 25 Dec 2025 00:00:00 +0000</pubDate>
      <guid>https://blog.watermeko.icu/post/tmux/</guid>
      <description>前言 tmux（Terminal Multiplexer）是一款功能强大的终端复用器。简单来说，它能让你在同一个屏幕上管理多个终端会话。除非手动终止tmux session，否则它会在SSH断开连接后继续运行。
安装 sudo apt install tmux -y 基本操作 Session操作 tmux # 创建新Session并进入 tmux new -s &amp;lt;name&amp;gt; # 创建具有名称 tmux rename-session &amp;lt;session name&amp;gt; &amp;lt;new name&amp;gt; # 更名session tmux ls # 查看正在运行的ession tmux attach -t &amp;lt;session name&amp;gt; # 重连seesion tmux kill-session -t &amp;lt;session name&amp;gt; # 使后台session停止活动 视窗操作 Ctrl+B D — 断开当前session Ctrl+B % — 水平分割视窗 Ctrl+B &amp;ldquo; — 垂直分割视窗 Ctrl+B 方向键 — 在视窗之间移动 Ctrl+B X — 关闭当前视窗 Ctrl+B C — 创建新视窗 Ctrl+B N or P — 在前/后一个视窗切换 Ctrl+B Q — 按下后再点击每个视窗上显示的数字对应按键即可跳转到视窗 Ctrl+B : — 输入指令 Ctrl+B ?</description>
    </item>
    <item>
      <title>CS61C笔记</title>
      <link>https://blog.watermeko.icu/post/cs61c-2020-fall-note/</link>
      <pubDate>Sat, 10 May 2025 00:00:00 +0000</pubDate>
      <guid>https://blog.watermeko.icu/post/cs61c-2020-fall-note/</guid>
      <description>前言 CS61C的笔记。以下描述的所有内容基于RV32（RISC-V的32位变体）。 CS61C的视频：Youtube
寄存器 RISC-V中有32个寄存器，每个寄存器的位宽为32。命名为x0-x31，其中x0永远存储0值（通过硬连线到地）。其中x10-x17又被命名为a0-a7，作为函数调用的参数寄存器。
汇编 关于RISC-V指令我发现一个很实用的项目，他将RV32I的指令和汇编伪指令做成了tldr库。安装后可以方便的查询指令的用法。
加减法 add 加法。
示例： add x1,x2,x3 （in RISC-V） 等同于：a = b + c （in C） sub 减法。
示例：sub x3,x4,x5 （in RISC-V） 等同于：d = e - f （in C） addi 立即数加法。
示例：addi x3,x4,10 （in RISC-V） 等同于： f = g + 10 （in C） 不存在立即数减法指令，将立即数设置为负数即可实现减法。 它可以用内存操作指令配合加法指令来替代，但我们仍然需要它。因为立即数加法非常常用。 内存操作 内存 内存按照字（words，4 bytes = 1 words）组织。数据遵循小端模式，高字节保存在内存的高地址中，而数据的低字节保存在内存的低地址中。 lw 从内存读取一个字到寄存器。 示例：
int A[100]; g = h + A[3]; lw x10, 12(x15) add x11, x12, x10 其中x15表示A所在的地址，12（字节）为偏移量。由于内存按照字（4字节）组织，故A[3]便是由A的地址偏移3个字得到的。偏移量必须是4的倍数。</description>
    </item>
    <item>
      <title>简单了解向量搜索</title>
      <link>https://blog.watermeko.icu/post/vectorsearch/</link>
      <pubDate>Sun, 08 Sep 2024 00:00:00 +0000</pubDate>
      <guid>https://blog.watermeko.icu/post/vectorsearch/</guid>
      <description>前言 最近看了篇文章，了解到了Vector Search。什么是Vector Search？来看看GPT的解释：
向量搜索是一种利用向量表示数据，并基于向量之间的相似性进行搜索的技术。它不同于传统的基于关键词的搜索，能够捕捉数据之间更深层次的语义关系，从而提高搜索的准确性和效率。
相较于基于关键词的搜索，将文本、图片等非数值型数据转换为向量形式，更能发现数据的特征，并发现数据之间的相似性。 至于如何将非数值型数据转换为向量，来开下面这个例子： 用“1”表示“是”，用“0”表示“否”
交通工具 轮子数量 有引擎吗 陆地上运动吗 最大承载人数 汽车 $4$ $1$ $1$ $4$ 自行车 $2$ $0$ $1$ $1$ 三轮车 $3$ $0$ $1$ $1$ 摩托车 $2$ $1$ $1$ $1$ 帆船 $0$ $0$ $0$ $20$ 轮船 $0$ $0$ $0$ $1000$ 把非数值型数据转换为向量，实际上就是把这些数据的特征用数值表示。比如对于汽车来说，它在这个例子中对应的向量就是$(4,1,1,4)$。
基础理论 向量之间的相似性 观察向量$\vec{a}$，$\vec{b}$和$\vec{p}$，哪个向量与$\vec{p}$更“相似”？向量$\vec{a}$与$\vec{p}$有着相同的方向，但模长更小；而$\vec{b}$与$\vec{p}$有着相同的模长，但方向只是相近。如果相似指的是方向上的相似，那么$\vec{a}$与$\vec{p}$更相似。而如果相似指的是模长，那么$\vec{b}$与$\vec{p}$更相似。 在矢量搜索中，很少单独根据模长来判断两个向量是否相似。相似性通常取决于方向，或者方向和模长。
相似性的评估方法 评估两个向量是否相似，通常有四种数学方法：
欧式距离：直接计算两个向量“箭头”的直线距离。 $d(\vec{a}, \vec{p}) = \sqrt{\sum_{i=1}^{n} (a_i - p_i)^2}$ $a_i$表示$\vec{a}$的第$i$个分量 曼哈顿距离：计算两个点之间沿着坐标轴行走的距离。 $d_1(\vec{a}, \vec{p}) = \sum_{i=1}^{n} |a_i - p_i|$ $a_i$表示$\vec{a}$的第$i$个分量 点积：将相同维度的分量相乘再相加。 $\vec{a} \cdot \vec{p} = \sum_{i=1}^{n}(a_i b_i)$ 余弦值：两个向量夹角的、余弦值。 $\cos(\theta) = \frac{\vec{a} \cdot \vec{b}}{|\vec{a}| |\vec{b}|}$ 这四种方法中，欧氏距离是最常用的，而曼哈顿距离与欧氏距离相似，应用这两种方法得到的结果通常也相似。而当需要评估相似性的向量模长一致时，使用余弦值是更好的方法，因为它只与夹角相关。点积与余弦值类似，但在需要大量计算的情况下，点积通常有着更好的性能。 简单的例子 由于在屏幕上呈现二维向量较为方便，因此选取前言中的“轮子数量”与“有引擎吗”作为特征。得到4个向量，分别是$(4,1),(2,0),(3,0),(2,1)$，它们在图上呈现为： 随后分别计算摩托车与自行车、三轮车、汽车的相似性：</description>
    </item>
    <item>
      <title>制作简单Magisk模块：音量阶数30阶</title>
      <link>https://blog.watermeko.icu/post/magiskmodulevolumesteps/</link>
      <pubDate>Fri, 06 Sep 2024 00:00:00 +0000</pubDate>
      <guid>https://blog.watermeko.icu/post/magiskmodulevolumesteps/</guid>
      <description>前言 手机默认音量阶数只有15阶，经常出现加一阶太大，减一阶又太小的情况。为了解决这个问题，可以制作一个Magisk模块，将音量阶数细分到30阶。以此来解决上述问题。
简单Magisk模块 Magisk模块的结构 为了将音量阶数修改为30阶，需要的东西其实非常少：
MoudleName.zip │ ├── 📁META-INF │ └── 📁com │ └── 📁google │ └── 📁android │ ├── 📄update-binary │ └── 📄updater-script │ ├── 📄module.prop ├── 📄system.prop │ META-INF：Magisk官方提供，无需更改。 module.prop：模块信息。 system.prop：用来修改系统的build.prop。
module.prop的作用 module.prop
id=&amp;lt;string&amp;gt; name=&amp;lt;string&amp;gt; version=&amp;lt;string&amp;gt; versionCode=&amp;lt;int&amp;gt; author=&amp;lt;string&amp;gt; description=&amp;lt;string&amp;gt; 名称 说明 id 模块唯一标识符 version 模块版本号 versionCode 模块版本标识，必须为整数 author 模块作者 description 模块描述 system.prop的作用 为了修改音量阶数为30阶，需要借由system.prop修改build.prop。system.prop中的配置会被替换到build.prop中的对应配置。 system.prop
ro.config.media_vol_steps=29 ro.config.vc_call_vol_steps=29 ro.config.alarm_vol_steps=29 ro.config.system_vol_steps=29 ro.config.media_vol_default=10 // 开机时默认的音量大小，10表示10阶 小结 将打包成zip的模块安装到手机后重启，音量阶数便会被修改为30阶，并且初始音量大小为最大音量的1/3。经过测试，本人的手机（Redmi Note 12 Turbo, HyperOS）最多将音量阶数调整为30阶，超过这个数，修改就会无效。
更多关于Magisk模块的信息 一个功能丰富的Magisk模块的结构 module.</description>
    </item>
    <item>
      <title>MSP430之时钟频率设置</title>
      <link>https://blog.watermeko.icu/post/msp430clocksetting/</link>
      <pubDate>Fri, 12 Jul 2024 00:00:00 +0000</pubDate>
      <guid>https://blog.watermeko.icu/post/msp430clocksetting/</guid>
      <description>写完这篇文章过了两天我才知道原来把官方例程下载下来可以用图形界面设置时钟。我是🐷
简介 MSP430有三个时钟，分别是MCLK、SMCLK、ACLK。
MCLK：主系统时钟。MCLK是CPU的运行时钟，MCLK频率越高，CPU运行越快，同时功耗越高。 SMCLK：子系统时钟。SMCLK可以提供给需要高速时钟的外设（如ADC、定时器。 ACLK：辅助时钟。辅助时钟的频率一般很低，功耗小，可以一直开启。 这三个时钟可由五个时钟源经过分频后提供，分别是XT1CLK、XT2CLK、VLOCLK、REFCLK、DCOCL。 XT1CLK（低频/高频时钟源）：XT1CLK可以作为内部FLL模块的参考时钟，且它是FLL模块（频锁环模块）的默认参考时钟。FLL时钟除锁频之外还可以对时钟进行倍频或分频。 XT2CLK（高频时钟源）：XT2CLK可以作为FLL的内部参考时钟。 VLOCLK（低频内部时钟源） REFCLK（低频时钟源，内部的参考时钟源）：可以作为内部FLL的参考时钟。 DCOCLK（片内数字控制时钟源）：通过内部FLL模块稳定，一般为1MHz。
其中XT1CLK、XT2CLK可由外部晶振提供。片内时钟源受温度影响大，频率一般不会很准确。 设置MSP430F5529的时钟 目的是将MCLK、SMCLK设置为XT2CLK（外部晶振 4Mhz）经过倍频后得到的24Mhz时钟；将ACLK设置为XT1CLK（外部晶振 32.768Khz）。 芯片的默认电压低，不足以支持高速的时钟频率，所以首先提升电压：
PMM_setVCore(PMM_CORE_LEVEL_3); 设置XT1CLK与XT2CLK引脚为复用功能：
GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P5,GPIO_PIN4 + GPIO_PIN5); GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P5,GPIO_PIN2 + GPIO_PIN3); 设置XT1CLK与XT2CLK外接晶振的频率：
UCS_setExternalClockSource(32768,4000000); 开启XT1CLK与XT2CLK：
UCS_turnOnLFXT1WithTimeout(UCS_XT1_DRIVE_0,UCS_XCAP_3,50000); UCS_turnOnXT2WithTimeout(UCS_XT2_DRIVE_4MHZ_8MHZ,50000); 将XT1CLK作为ACLK时钟源，XT2CLK作为FLL参考时钟源：
UCS_initClockSignal(UCS_ACLK,UCS_XT1CLK_SELECT,UCS_CLOCK_DIVIDER_1) UCS_initClockSignal(UCS_FLLREF,UCS_XT2CLK_SELECT,UCS_CLOCK_DIVIDER_1); 提升MCLK的频率：
UCS_initFLLSettle(24000, 6); // 6*4Mhz = 24Mhz 最后开启时钟源中断：
SFR_clearInterrupt(SFR_OSCILLATOR_FAULT_INTERRUPT); SFR_enableInterrupt(SFR_OSCILLATOR_FAULT_INTERRUPT); 但我还有一个疑问，就是为什么不用使用UCS_initClockSignal函数将XT2CLK作为MCLK和SMCLK的时钟源，或者说将FLL参考时钟源设置为MCLK和SMCLK的时钟。我不这么做MCLK和SMCLK照样会被设置为XT2CLK倍频后的时钟。如果有大神知道原因，希望能留评论告诉我。
完整代码 clock.h
#ifndef __CLOCK_H__ #define __CLOCK_H__ #include &amp;#34;driverlib.h&amp;#34; #define UCS_XT1_TIMEOUT 50000 #define UCS_XT2_TIMEOUT 50000 #define UCS_XT1_CRYSTAL_FREQUENCY 32768 #define UCS_XT2_CRYSTAL_FREQUENCY 4000000 #define UCS_MCLK_DESIRED_FREQUENCY_IN_KHZ 24000 #define UCS_MCLK_FLLREF_RATIO 6 void Clock_Init(void); #endif clock.</description>
    </item>
    <item>
      <title>PCA9685舵机驱动板的使用方法</title>
      <link>https://blog.watermeko.icu/post/pca9685motordriver/</link>
      <pubDate>Thu, 28 Sep 2023 00:00:00 +0000</pubDate>
      <guid>https://blog.watermeko.icu/post/pca9685motordriver/</guid>
      <description>简介 PCA9685驱动板支持控制16路舵机，采用i2c通信协议。
VCC：PCA9685芯片的电源 V+：舵机的电源 舵机数量较少时电源可以通过V+引脚输入，较多时通过绿色电源输入端输入 Arduino驱动示例 #include &amp;lt;Wire.h&amp;gt; #include &amp;lt;Adafruit_PWMServoDriverh&amp;gt; #define SERVO_45 187 Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(); void setup() { pwm.begin(); pwm.setPWMFreq(50); pwm.setPWM(0,0,SERVO_45); } 本示例使用了Adafruit PWM Servo Driver Library。 该程序可以使连接在PCA9685舵机驱动板上0号位的舵机转动45°。 setPWMFreq()设置了IIC通信的更新频率（50HZ对应脉冲周期20ms），setPWM()`的三个参数依次为舵机编号、脉冲起始时间、脉冲结束时间，其中脉冲时间要以寄存器的值来表示（187对应1.0ms）。
舵机控制逻辑 上述示例使用舵机型号为SG90，运动范围0°-180°，舵机的控制周期信号为20ms，工作脉冲在0.5ms-2.5ms之间，舵机的转动角度由PCA9685的12位寄存器中的值来决定，而寄存器的值由脉冲时间决定。故可以得到脉冲时间-寄存器值-转动角度的关系如下：
&amp;#39;0.5ms-0°&amp;#39;:0.5*204=102 &amp;#39;1.0ms-45°&amp;#39;:1*204=204 &amp;#39;1.5ms-90°&amp;#39;:1.5*204=306 &amp;#39;2.0ms-135°&amp;#39;:2*204=408 &amp;#39;2.5ms-180°&amp;#39;:2.5*204=510 然而在实际使用时会有误差出现，故除了0°与180°的情况，要将寄存器值乘以0.915的系数。
&amp;#39;0.5ms-0°&amp;#39;:0.5*204=102 &amp;#39;1.0ms-45°&amp;#39;:1*204=204*0.915=187 &amp;#39;1.5ms-90°&amp;#39;:1.5*204=306*0.915=280 &amp;#39;2.0ms-135°&amp;#39;:2*204=408*0.915=373 &amp;#39;2.5ms-180°&amp;#39;:2.5*204 </description>
    </item>
    <item>
      <title>ESP8266-NodeMCU食用指南</title>
      <link>https://blog.watermeko.icu/post/esp8266-nodemcu-guide/</link>
      <pubDate>Wed, 20 Sep 2023 00:00:00 +0000</pubDate>
      <guid>https://blog.watermeko.icu/post/esp8266-nodemcu-guide/</guid>
      <description>基础知识 WIFI模式 接入点模式 esp8266作为接入点，其他设备通过wifi与esp8266连接。 示例：
/* 此程序用于演示如何将NodeMCU以接入点模式工作。通过此程序，您可以使用 电脑或者手机连接NodeMCU所建立WiFi网络。 */ #include &amp;lt;ESP8266WiFi.h&amp;gt; // 本程序使用ESP8266WiFi库 const char* ssid = &amp;#34;wifi_name&amp;#34;; const char* password = &amp;#34;12345678&amp;#34;; // 如果建立的WiFi不要密码，则在双引号内不要填入任何信息 void setup() { Serial.begin(9600); // 启动串口通讯 WiFi.softAP(ssid, password); // 此语句是重点。WiFi.softAP用于启动NodeMCU的AP模式。 Serial.print(&amp;#34;Access Point: &amp;#34;); // 通过串口监视器输出信息 Serial.println(ssid); // 告知用户NodeMCU所建立的WiFi名 Serial.print(&amp;#34;IP address: &amp;#34;); // 以及NodeMCU的IP地址 Serial.println(WiFi.softAPIP()); // 通过调用WiFi.softAPIP()可以得到NodeMCU的IP地址 } void loop() { } 无线终端模式 esp8266与路由器连接。
/* 此程序用于演示如何将NodeMcu以接入点模式工作 */ #include &amp;lt;ESP8266WiFi.h&amp;gt; const char* ssid = &amp;#34;wifi_name&amp;#34;; const char* password = &amp;#34;pswd&amp;#34;; void setup(){ Serial.</description>
    </item>
  </channel>
</rss>
