主题
本手册视频讲解链接指路 👇
【电赛速通教程! 不翻页! 一张图看懂!】
手册说明
对于不翻页速通手册Wiki的说明:
每页图片均有对应的B站视频碎碎念讲解和可复制的伪代码,或许可以帮你更快了解。如有问题欢迎讨论,该系列仍在更新,或许你还有想要补充的内容,欢迎在视频下评论,UP应该会一点一点填坑的 🥺
该系列中的伪代码都不可直接运行,但是你可以发给AI,再告诉他你的环境,可以帮你快速生成可执行的代码并且可以更细致地教会你 😊
祝各位学的开心、代码一编译就能运行~ 🤓
目录如下:
视频版本说明: https://www.bilibili.com/video/BV1Xp3Bz3EH8
功能篇
GPIO
https://www.bilibili.com/video/BV1uU3qzvEAr
json
// 示例伪代码
// 1. 设置GPIO为输出模式
pinMode(GPIO_PIN, OUTPUT); // 设置为输出
// 2. 更改输出电平
digitalWrite(GPIO_PIN, HIGH); // 输出高电平
delay(1000); // 延时1000ms
digitalWrite(GPIO_PIN, LOW); // 输出底电平go
// 示例伪代码
// 1. 设置GPIO为输入模式,并启用内部上拉电阻
setPinMode(GPIO_PIN, INPUT); // 设为输入模式
setPullUp(GPIO_PIN); // 启用上拉电阻
// 2. 读取引脚状态(默认HIGH,按下按钮接地变LOW)
if (readDigitalPin(GPIO_PIN) == LOW) {
print("Valid Press!"); // 触发
}
}PWM
https://www.bilibili.com/video/BV1Dy3qzTEvi
java
// 示例伪代码
// 1. 初始化PWM(选择引脚、频率、分辨率)
pwmPin = GPIO_PIN_X; // 选择支持PWM的引脚(如PA8/PIN9)
pwmFrequency = 1000; // 设置PWM频率(Hz,如1kHz)
pwmResolution = 8; // 设置分辨率(比特数,如8位=0~255)
setPinMode(pwmPin, PWM_OUTPUT); // 设置引脚为PWM输出模式
initPWM(pwmPin, pwmFrequency, pwmResolution); // 初始化PWM硬件
// 2. 固定占空比(0%~100%对应0~最大值)
setPWMDutyCycle(pwmPin, 50); // 设置占空比为50%
// 3. 动态占空比
while (1) {
for (duty = 0; duty <= 100; duty += 10) {
setPWMDutyCycle(pwmPin, duty); // 渐变占空比(呼吸灯效果)
delay(100);
}
}ADC
https://www.bilibili.com/video/BV1rP3BzWErg
go
// 示例伪代码
// 1. 配置ADC引脚为模拟输入
adc_configure_pin(PIN_A0); // 设置A0为模拟输入
// 2. 初始化ADC硬件(自动使用默认设置)
adc_init(); // 启用ADC
// 3. 读取一次原始值
raw_value = adc_read(PIN_A0); // 获取0-4095原始值
print("ADC值: %d", raw_value); // 直接输出结果IRQ
https://www.bilibili.com/video/BV14mGEzjEan
javascript
// 示例伪代码
// 中断服务函数
void IRQ_Handler() {
digitalToggle(LED); // 中断触发时翻转LED
}
int main() {
// 初始化部分
pinMode(IRQ_PIN, INPUT); // 设置中断引脚为输入
setInterrupt(IRQ_PIN, RISING_EDGE, IRQ_Handler); // 配置上升沿触发
while(1) { /* 其他任务 */ } // 主循环
}Timer
https://www.bilibili.com/video/BV16FGJzMEiE
javascript
// 示例伪代码
// 中断服务函数
void Timer_IRQHandler() {
digitalToggle(LED); // 中断触发时翻转LED
}
// 主函数初始化
int main() {
// 1. 初始化周期定时器(1ms中断)
Timer_Init(TIM1, PERIODIC, 1000Hz); // 1ms周期
NVIC_SetIRQHandler(TIM1_IRQn, Timer_IRQHandler);
// 或者2. 初始化超时定时器(3s单次中断)
Timer_Init(TIM2, ONESHOT, 3000ms);
NVIC_SetIRQHandler(TIM2_IRQn, Timer_IRQHandler);
while(1); // 所有操作由中断完成
}UART
https://www.bilibili.com/video/BV1itGEzFEVY
javascript
// 示例伪代码
// 1.初始化
void UART_Init() {
// 1. 配置TX/RX引脚(伪代码)
pinMode(UART_TX_PIN, ALT_FUNC); // 设为复用功能
pinMode(UART_RX_PIN, ALT_FUNC);
// 2. 核心参数配置
uart_setup(port:1, baud:115200, data:8, parity:none, stop:1);
}
// 2.主函数,发送或接收UART数据
int main() {
UART_Init(); // 初始化(含引脚)
UART_Send("Hello"); // 发送
char c = UART_Read(); // 接收
}I2C
https://www.bilibili.com/video/BV1BpGEzYEm6
javascript
// 示例伪代码
void I2C_Init() {
// 1. 配置I2C引脚(SCL/SDA)
pinMode(I2C_SCL_PIN, ALT_FUNC_OPEN_DRAIN); // 开漏输出
pinMode(I2C_SDA_PIN, ALT_FUNC_OPEN_DRAIN);
// 2. I2C参数配置
i2c_setup(port:0, speed:400kHz); // 标准模式(100kHz)或快速模式(400kHz)
}
int main() {
I2C_Init();
// 1. 向0x27地址的设备发送指令0xFE(示例:LCD清屏)
I2C_WriteCommand(dev_addr:0x27, cmd:0xFE);
// 2. 向0x68地址的设备读取6字节(示例:LCD清屏)
I2C_Read_Reg(dev_addr:0x68, reg:0x3B, buf, len:6);
}SPI
https://www.bilibili.com/video/BV1bkGEzSEMj
javascript
// 示例伪代码
void SPI_Init() {
// 1. 配置SPI引脚(SCK/MOSI/MISO)
pinMode(SPI_SCK_PIN, ALT_FUNC); // 时钟线
pinMode(SPI_MOSI_PIN, ALT_FUNC); // 主机输出
pinMode(SPI_MISO_PIN, ALT_FUNC); // 主机输入
// 2. 配置片选引脚(两个设备)
pinMode(CS1_PIN, OUTPUT); // 设备1片选
pinMode(CS2_PIN, OUTPUT); // 设备2片选
digitalWrite(CS1_PIN, HIGH); // 初始不选中
digitalWrite(CS2_PIN, HIGH);
// 3. SPI参数配置
spi_setup(port:0, mode:0, speed:1MHz); // 模式0,1MHz时钟
}
// ===== 发送指令 ===== //
void SPI_WriteCommand(uint8_t cs_pin, uint8_tcmd) {
digitalWrite(cs_pin, LOW); // 选中设备
spi_write(cmd); // 发送指令
digitalWrite(cs_pin, HIGH); // 释放设备
}
// ===== 读取数据 ===== //
void SPI_ReadData(uint8_t cs_pin, uint8_t reg, uint8_t *buf, uint8_tlen) {
digitalWrite(cs_pin, LOW);
spi_write(reg); // 先发送寄存器地址
spi_read(buf, len); // 连续读取数据
digitalWrite(cs_pin, HIGH);
}
int main() {
SPI_Init();
uint8_t data[6];
// 1. 向设备1(CS1)发送指令0xFE
SPI_WriteCommand(CS1_PIN, 0xFE);
// 2. 从设备2(CS2)的0x3B寄存器读取6字节
SPI_ReadData(CS2_PIN, 0x3B, data, 6);
}模块篇
舵机
https://www.bilibili.com/video/BV1qCuMztET5
电机
https://www.bilibili.com/video/BV1jsuuz8EC6
电机编码器
https://www.bilibili.com/video/BV1h9uMzDEXP
javascript
// 示例伪代码
// 全局变量
volatile int count = 0; // 编码器计数
volatile int speed = 0; // 当前速度
// 中断服务函数:计数器更新
void IRQ_Handler() {
// 根据A和B的状态进行计数
if (A_edge == FALLING) {
if (B == 0) {
count++; // B=0,加一
} elseif (B == 1) {
count--; // B=1,减一
}
}
else if (A_edge == RISING) {
if (B == 1) {
count++; // B=1,加一
} else if (B == 0) {
count--; // B=0,减一
}
}
else if (B_edge == RISING) {
if (A == 0) {
count++; // A=0,加一
} else if (A == 1) {
count--; // A=1,减一
}
}
else if (B_edge == FALLING) {
if (A == 1) {
count++; // A=1,加一
} else if (A == 0) {
count--; // A=0,减一
}
}
}
// 定时器中断服务函数:更新速度
void Timer_IRQHandler() {
speed = count; // 计算当前速度(单位为计数值)
count = 0; // 每次定时器中断时清零计数器
}
int main() {
// 初始化部分
pinMode(A_PIN, INPUT); // 设置A引脚为输入
pinMode(B_PIN, INPUT); // 设置B引脚为输入
// 配置A引脚为双沿触发
setInterrupt(A_PIN, BOTH_EDGES, IRQ_Handler);
// 配置B引脚为双沿触发
setInterrupt(B_PIN, BOTH_EDGES, IRQ_Handler);
Timer_Init(TIM1, PERIODIC, 100); // 100ms周期定时器
NVIC_SetIRQHandler(TIM1_IRQn, Timer_IRQHandler); // 配置定时器中断处理函数
while(1) {
// 主循环,处理其他任务 }
}TB6612电机驱动
https://www.bilibili.com/video/BV1R2uMzxEQm
L298N电机驱动
https://www.bilibili.com/video/BV1xKuuzkENh
带稳压的TB6612电机驱动
https://www.bilibili.com/video/BV1tFuuz2Ezs
TCRT5000 循迹传感器
https://www.bilibili.com/video/BV1bCuFzxEqK
HC-SR04 超声波测距传感器
https://www.bilibili.com/video/BV1LGucziEsL
SSD1306 OLED屏幕
https://www.bilibili.com/video/BV1PtuMzzEL8

c++
// 示例库函数代码
#include <Wire.h> // I2C 库
#include <Adafruit_GFX.h> // GFX 库
#include <Adafruit_SSD1306.h> // SSD1306 显示器库
#define SCREEN_WIDTH 128 // OLED 显示宽度
#define SCREEN_HEIGHT 64 // OLED 显示高度
#define OLED_ADDR 0x3C // I2C 地址,默认是 0x3C
#define OLED_RESET -1 // 重置引脚
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // 创建 SSD1306 对象,使用 I2C
intmain(void) {
// 初始化硬件
Wire.begin(); // 初始化 I2C 总线
display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR); // 初始化 SSD1306
display.clearDisplay(); // 清空显示器
display.setTextSize(1); // 设置文字大小
display.setTextColor(SSD1306_WHITE); // 设置文字颜色为白色
delay(1000); // 延时 1 秒
while (1) {
display.clearDisplay(); // 清空显示器
display.setCursor(20, 10); // 设置显示位置
display.println("- LEO -"); // 显示文本
display.display(); // 更新显示
delay(200); // 延时 200 毫秒
}
}MPU6050 六轴姿态传感器
https://www.bilibili.com/video/BV14WuMzGEh3
javascript
// 示例伪代码
// 1. 初始化部分
voidI2C_Init() {
// 配置I2C引脚
pinMode(I2C_SCL_PIN, ALT_FUNC_OPEN_DRAIN);
pinMode(I2C_SDA_PIN, ALT_FUNC_OPEN_DRAIN);
// I2C参数配置
i2c_setup(port:0, speed:400kHz); // 使用快速模式
}
// 2. 主程序
intmain() {
I2C_Init();
// 数据缓冲区
uint8_traw_data[14]; // 14字节原始数据(6轴+温度)
int16_t accel[3], gyro[3], temp;
while(1) {
// 从0x3B寄存器开始读取14字节数据
I2C_Read_Reg(dev_addr:0x68, reg:0x3B, buf:raw_data, len:14);
// 解析原始数据 (大端格式)
accel[0] = (raw_data[0]<<8) | raw_data[1]; // X轴加速度
accel[1] = (raw_data[2]<<8) | raw_data[3]; // Y轴加速度
accel[2] = (raw_data[4]<<8) | raw_data[5]; // Z轴加速度
temp = (raw_data[6]<<8) | raw_data[7]; // 温度
gyro[0] = (raw_data[8]<<8) | raw_data[9]; // X轴角速度
gyro[1] = (raw_data[10]<<8)| raw_data[11]; // Y轴角速度
gyro[2] = (raw_data[12]<<8)| raw_data[13]; // Z轴角速度
// 保存数据 (伪代码)
save_to_storage(accel, gyro, temp);
delay(10); // 10ms采样间隔
}
}HC-05 蓝牙模块
https://www.bilibili.com/video/BV1byuFzRETX

算法篇
PID
https://www.bilibili.com/video/BV1yGuFz2EFA
java
// 示例伪代码
// 定义PID计算所需变量
float setpoint = 0; // 目标设定值
float measured_value = 0; // 当前测量值
float error = 0; // PID输入的误差值 = 目标值 - 测量值
float integral = 0; // 误差积分项
float last_error = 0; // 上次误差值
float Compensation = 0; // PID输出的补偿值
float output = 0; // 控制器输出
// PID系数(需根据实际系统调整)
float Kp = 1.0; // 比例系数
float Ki = 0.01; // 积分系数
float Kd = 0.1; // 微分系数
// PID计算函数(需周期性调用)
void PID_Compute() {
// 1. 计算当前误差
error = setpoint - measured_value;
// 2. 计算PID三项
float proportional = Kp * error; // 比例项:立即响应误差
integral += Ki * error; // 积分项:消除稳态误差
float derivative = Kd * (error - last_error); // 微分项:抑制超调
// 3. 合成输出
output = proportional + integral + derivative;
// 4. 保存当前误差(用于下次微分计算)
last_error = error;
// 5. (可选)输出限幅
if(output > max_output) output = max_output;
if(output < min_output) output = min_output;
}
// 主函数
int main() {
while(1) {
measured_value = read_sensor(); // 获取传感器数据
PID_Compute(); // 计算控制量
set_actuator(output,Compensation); // 输出控制信号
delay(20); // 20ms控制周期
}
}互补滤波
https://www.bilibili.com/video/BV1fHuFz6EQX
java
// 示例伪代码
// 初始化
float angle = 0.0f; // 初始角度估计值
constfloat alpha = 0.98f; // 滤波系数
constfloat RAD_TO_DEG = 57.29578f; // 弧度转角度
while(1) {
// 1. 获取传感器数据(伪函数)
floataccelY = read_accelY(); // Y轴加速度值
floataccelZ = read_accelZ(); // Z轴加速度值
floatgyroX = read_gyroX(); // X轴角速度值
float dt = 0.02f; // 采样周期20ms
// 2. 加速度计计算角度(低频)
floataccelAngle = atan2f(accelY, accelZ) * RAD_TO_DEG;
// 3. 陀螺仪计算角度变化(高频)
floatgyroDelta = gyroX * dt;
// 4. 互补滤波融合(核心计算)
angle = alpha * (angle + gyroDelta) + (1.0f - alpha) * accelAngle;
// 5. 使用最终角度(示例)
set_actuator(angle);
delay_ms(20); // 保持固定采样周期
}平滑滤波
www.bilibili.com/video/BV1nSf8BqESw
c++
// 示例伪代码
// 初始化
float filteredValue = 0.0f; // 初始滤波值 u(t-1)
const float alpha = 0.2f; // 滤波系数 α (0~1之间)
while(1) {
// 1. 获取当前时刻的原始采样数据(伪函数)
float currentSample = read_sensor(); // 当前采样值 x(t)
float dt = 0.02f; // 采样周期20ms
// 2. EMA滤波核心计算
// u(t) = α × x(t) + (1 - α) × u(t-1)
filteredValue = alpha * currentSample + (1.0f - alpha) * filteredValue;
// 3. 使用滤波后的值(示例)
process_data(filteredValue);
// 4. 为下一次迭代准备:u(t) 自动成为下一次的 u(t-1)
delay_ms(20);
// 保持固定采样周期
}滑动窗口
https://www.bilibili.com/video/BV1HSf8BqEUj
c++
// 示例伪代码
// 循环数组定义
#define BUFFER_SIZE 6
int dataBuffer[BUFFER_SIZE]; // 数据缓冲区
int writeIndex = 0; // 写入位置索引
int count = 0; // 当前数据数量
// 每一次向循环数组存入新数据
void AddDataToBuffer(int newData) {
// 将新数据存入当前写入位置
dataBuffer[writeIndex] = newData;
// 更新写入索引(循环)
writeIndex = (writeIndex + 1) % BUFFER_SIZE;
// 更新数据计数(不超过缓冲区大小)
if (count < BUFFER_SIZE) {
count++;
}
}状态机
https://www.bilibili.com/video/BV1kSf8BqEAd/
c++
// 示例伪代码
// 状态定义:
// 0=初始化(S0), 1=第一次按下(S1), 2=第一次释放(S2), 3=第二次按下(S3)
int state = 0; // 事件处理函数
// 输入参数:eventType - 0表示按下事件,1表示释放事件 。
// 定时中断50ms触发
void KeyFSM_HandleEvent(int eventType) {
switch(state) {
case 0: // S0状态 - 初始化
if (eventType == 0) { // 按下事件
printf("已按下\n");
state = 1;
}
break;
case 1: // S1状态 - 第一次按下
if (eventType == 1) { // 释放事件
state = 2; // 此时开始记录施放时间 releaseTime
}
break;
case 2: // S2状态 - 第一次释放
if (eventType == 0) { // 按下事件
printf("双击\n");
state = 3;
}
else if (releaseTime >= 500) { // 超过规定间隔500ms
printf("单击\n");
state = 0;
}
break;
case 3: // S3状态 - 第二次按下
if (eventType == 1) { // 释放事件
state = 0;
}
break;
}
}