- 博客(38)
- 收藏
- 关注
原创 芯片启动原理 与 如何跳转到主程序
ARM 汇编伪指令,分配 4 字节(32 位)内存,并用 __initial_sp 的值初始化该内存。中断向量表 __vectors (相当于一个数组 用于储存图片内的中断向量的地址值)包括msp (主栈指针针 用于中断触发) 与 psp(进程指针 用于app使用)(地址值)__initial_sp = 0x 20 00 04 00。1.芯片上电后将sp(r13) 跟pc(r15)加载到对应寄存器。(地址)第二个字 reset_handler 复位处理。这里存放的是sp(栈指针)的地址值。
2025-11-28 12:18:30
280
原创 滴答时钟延时
define TICKS_PER_MS (SystemCoreClock / 1000) // 1ms 对应的时钟周期数#define TICKS_PER_US (SystemCoreClock / 1000000) // 1us 对应的时钟周期数。
2025-10-13 17:17:20
806
原创 st7789中文字库
该代码实现了在ST7789 LCD屏幕上显示ASCII字符和GB2312编码中文字符的功能,支持自定义字体设置和前景/背景颜色配置。
2025-10-08 12:58:14
1070
原创 显示英文以及字符
利用软件进行取模后,对字库数据(16个点为一行,0x00形式的数据)进行检索。本代码处理的是ascii码的字符显示。提取像素值:检查特定位是否被设置。计算当前像素所在的字节和位位置。显示像素:如果位被设置则显示。一下代码从如上代码改编而来。计算当前行数据的起始地址。处理当前行的每个像素。
2025-10-06 16:11:30
387
原创 for(int t =0;;)里面的变量一定为啥一定要初始化(⊙_⊙)?
今天用keil写代码的时候,没有实现功能,但是keil一直没有报错,最后发现是for里面的值没有初始化,导致里面的值是一个随机数。的好习惯,尤其是在嵌入式系统开发中。这意味着该变量所在的内存地址里是什么值,它就是什么值。在C语言中,在函数内部(包括循环内)定义的局部变量,如果。未初始化 for (uint32_t t;没有显式地赋予初始值,那么它的值是“未定义”的。你图片中的代码是正确的,因为它在。不可预测,依赖于内存垃圾值。可预测,严格循环20次。极其常见且重要的陷阱。
2025-09-18 15:54:29
500
原创 栈传递要注意的事项
你可以这样理解:普通局部变量就像沙滩上的画(栈内存)。函数调用像是潮水上来,在沙滩上作画(创建变量)。函数返回像是潮水退去,画作立刻被抹掉(变量销毁)。你把这幅画的位置告诉别人(传递地址),等潮水退去后别人再来看,画早就没了。静态局部变量就像博物馆里永久的展品(静态存储区)。它在博物馆开张时(程序启动)就被放在一个固定的位置。你告诉别人这个展品的位置(传递地址),无论你是否离开博物馆,这个展品都会一直在那里,别人随时来看都能找到它。所以,改为static。
2025-09-18 10:10:01
900
原创 指针 结构体 二级指针
最近写指针跟结构体晕晕的,写了个小总结}_led1;结构体本身就是在一段连续的内存上写数据&_led1 //获得结构体地址正常对结构体赋值;//定义一个结构体指针//led_desc_t是一个指针,指针内指向的数据是一个地址//将结构体的地址给了指针此时&led1获得的是led1这个指针的地址*led1获得的是led1所指向的那个地址 ,即&_led1(也就是结构体_led1的地址)此时如果要赋值,可以通过结构体指针进行赋值。
2025-09-18 09:22:15
402
原创 Vtaskdelay任务阻塞深入了解
动作结果调用任务主动放弃CPU,自我阻塞。链表操作从就绪链表->延时链表。状态变化就绪态 (Ready)->阻塞态 (Blocked)。CPU资源立即释放,调度器选择下一个最高优先级的就绪任务运行。恢复条件指定的延时时间到期(由SysTick中断服务程序检查)。恢复后从延时链表->就绪链表阻塞态->就绪态,等待调度器再次分配CPU。因此,使用delay是任务间协作的一种非常高效的方式,它让低优先级的任务可以主动让出CPU,使得系统能够及时响应高优先级任务,从而充分利用CPU资源。
2025-09-16 20:31:30
305
原创 面向对象点亮LED stm32407VEt6
/ GPIO端口// 引脚编号// 点亮时的电平状态// 熄灭时的电平状态// 类型别名// 静态实例定义// 全局访问指针。
2025-09-16 17:41:57
707
原创 深入了解一下栈的概念
所以,准确的回答是:栈是随着进程或线程的创建而由系统自动创建和初始化的。当你双击一个程序图标,操作系统在加载它的代码和数据的同时,也为其创建了栈(和堆)空间,为它的运行准备好环境。计算机里有一个叫 「栈指针」 的寄存器,它就相当于一个尺子,永远指着当前这摞盘子的最顶部那个的位置。线程的栈:在一个程序内部,如果你创建了新的线程,操作系统或运行时库也会为这个新线程创建一块属于它自己的独立栈空间。主程序的栈:当你启动一个程序(进程)时,操作系统会为它创建一块独立的、大小固定的内存区域作为主栈。
2025-09-09 15:56:58
389
原创 让出CPU 跟 时间片轮询的区别
但开启了时间片轮询后,Task_A运行5ms后会被强制切走,换Task_B运行5ms,再换Task_C运行5ms,如此循环,使得三个计算任务“同时”在向前推进。当一个任务持续运行了整整一个时间片后,系统时钟滴答中断(Tick Interrupt)会触发,调度器在中断服务程序中进行处理,强制暂停当前任务,并切换到下一个相同优先级的就绪态任务。任务需要等待某个未来才会发生的事件(如信号量、消息队列、延时等),此时它会阻塞(Block),阻塞操作本身就会引发任务切换,让出CPU。这是RTOS实时性的保证。
2025-09-09 15:28:56
699
原创 注释掉 vTaskDelay(3000) 反而导致耗时增加的原因
后耗时反而增加的现象看似违反直觉,但实际上是 FreeRTOS 调度机制和硬件资源竞争共同作用的结果。LCD 任务进入阻塞状态 → 调度器立即切换到高优先级 CalcTask → CalcTask 独占 CPU。LCD 任务直接进入忙等待 → 与 CalcTask 竞争 CPU → 频繁任务切换增加开销。section 有 vTaskDelay。section 无 vTaskDelay。// 任务主动阻塞 3 秒。立即切换(高优先级)// 立即开始忙等待。
2025-09-08 23:41:51
218
原创 队列的相关函数
如果队列已满,则自动覆盖队列中最早的数据(即队尾的数据),然后放入新数据。作用:从队列的头部****复制一个数据项到提供的缓冲区,但不会将数据项从队列中移除。:如果发送操作解除了某个任务的阻塞,并且该任务的优先级高于当前运行的任务,则此参数会被设置为。队列长度必须为 1。:指向一个缓冲区的指针,用于保存接收到的数据。说明:此函数会从 FreeRTOS 的堆中分配创建队列所需的内存。作用:动态创建一个新的队列,并返回队列的句柄(Handle)。作用:从队列的头部接收一个数据项,并将该数据项从队列中移除。
2025-09-08 23:40:08
388
原创 一个模块接受的数据,使用多个队列的处理方式
此笔记描述了一个在 FreeRTOS 中使用 “参数化任务” 和 “广播队列” 模式的经典设计,***源自于韦东山老师的rtos教学用于处理多个相似对象(如三辆小车)的并发控制。🔧 系统设计模式详解这种设计的核心思想是:“一套代码,多份数据,消息广播,各自过滤”。核心组件:一个通用的任务函数 (CarTask):包含所有汽车的控制逻辑。多个汽车对象 (g_cars[0], g_cars[1], ...):每个对象存储一辆汽车的状态(如位置、控制键值)。
2025-09-08 23:38:13
148
原创 队列 信号量 互斥量的互斥操作实现
这种分层设计使得 FreeRTOS 能够在保持小巧代码体积的同时,提供灵活且高效的同步机制。FreeRTOS 中的队列、信号量和互斥量虽然用途不同,但它们的互斥操作都基于一些共同的底层机制。队列是 FreeRTOS 中最基础的同步机制,信号量和互斥量都是基于队列实现的。// 计数信号量是项目大小为0的队列,队列长度就是最大计数值。// 二进制信号量实际上是一个长度为1、项目大小为0的队列。// 内部调用队列接收函数,但项目大小为0(只操作计数)// 内部调用队列发送函数,但项目大小为0(只操作计数)
2025-09-08 23:25:35
626
原创 FreeRTOS xTimerChangePeriod() 周期设置机制详解
从时间轴可以看出,不是在原来的 t=4 基础上加4秒到 t=8,而是从改变的时刻 t=3 开始加4秒,到 t=7 触发。设置的新周期不是在原有周期基础上叠加的,而是完全替换旧的周期值,并从函数调用成功的那一刻开始重新计算下一次到期时间。时间轴: 0-----1-----2-----3-----4-----5-----6-----7-----8 (秒)// - 新模式:每4秒触发一次(从改变时刻开始计算)// - 如果尚未触发:将在改变后的4秒触发。// - 旧模式:每2秒触发一次。
2025-09-03 12:10:59
431
原创 多任务系统为什么会有栈?
a的sp(执行到最后切换前的那一步)会保存在a的结构体当中,用于切换。找到a结构体的sp,恢复栈里面保存的值,先恢复寄存器,再回复pc。会提前保留切换前的任务现场,保留在栈当中(a的栈)然后回复要执行的任务的现场,继续执行。rtos在调度其他任务b的时候。多任务系统为什么会有栈?记录两个时间分别为a b。保存调用关系跟局部变量。
2025-07-23 21:38:27
132
原创 vtaskdelay 与vtaskdelayuntil
└───────────────────────────────────────────────────┘ → 唤醒在 200ms。└───────────────────────────────────────────────────┘ → 下次唤醒严格在 100ms。├─ 工作耗时 30ms ────── vTaskDelayUntil(&t, 100) ──┐。├─ 工作耗时 20ms ────── vTaskDelayUntil(&t, 100) ──┐。3. 核心区别对比表。
2025-07-23 14:55:35
306
原创 SW IIC
功能:由主设备(如MCU)发出的同步时钟信号,用于协调数据传输的时序。这两条线需通过上拉电阻连接至正电源,确保信号稳定性。功能:双向数据传输线,用于主从设备之间的。实际数据通信(如传感器、存储器等)。主机向从机发送一位起始位。
2025-04-12 21:22:06
587
原创 NVIC的主要作用
在STM32(如STM32F103C8T6)中,是ARM Cortex-M内核的一部分,负责管理所有中断和异常的优先级、使能状态及响应流程。
2025-04-10 21:49:49
594
原创 为什么用DMA与串口发送数据,原地址需要自增而目的地地址不用自增?
串口发送数据时,数据是通过写入**串口的发送数据寄存器(如`USART_DR`或`UART_TXD`)**来完成的。在使用DMA(直接内存访问)与串口发送数据时,**目的地地址(即串口数据寄存器地址)通常不需要自增**,而**源地址(内存中的数据缓冲区地址)需要自增**。- **源地址(内存缓冲区)**:通常是数组或变量,数据是连续存储的,DMA需要逐个读取每个字节,因此源地址必须自增。| **方向** | **源地址** | **目的地地址** | **地址自增** |
2025-04-07 13:55:27
603
原创 AFIO引脚复用功能
需要开启外设时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);1.复用引脚重映射//必须开启了复用功能才能这样子,让一个引脚有多个功能开启后一个管脚可以有多个功能,但是需要进行工作mode的切换这里要注意的是 引脚功能时不能同时的一个引脚在(普通GPIO或某个特定的复用功能),但可以在不同场景下为不同功能。例如,PA9引脚可以配置为:普通GPIO输出(控制LED)USART1_TX(串口发送)TIM1_CH2(定时器通道)
2025-04-05 12:45:14
1287
原创 函数指针与回调函数
/ p就是一个函数指针。//类比数组的写法,其实可以不用*//传入函数名字func1。而对于函数而言 ex : int add【int a, int b】arr代表的是首个元素的地址 &arr代表的好似数组的地址。) //函数指针 传入的参数为函数的名字,也是地址。add 与 &add代表的均为这个函数的地址。//&add也是一样的,因为函数名就是地址。最终将会输出 me he。
2025-04-05 12:42:13
235
原创 #define 与 typedef
point //结构体struct point 的别名。宏定义可以放在.h文件里,当包含该头文件时,该宏定义生效。此时的pfun_f 就可以代表一个返回值任意的函数指针。#define 你想要的名字 代表的值。typedef 已有的类型 新名字。等于 void * 函数名 (int)此时key_press 的值就代表1。指针类命名要注意,*要放进括号内。类型不转换,只是改了名字。
2025-04-05 12:40:02
200
原创 #c语言 关键字static
改全局变量将会只能在当前源文件中使用,不能被extern(用于限制该全局变量的使用范围)* 局部变量出函数后将不会被销毁,下一次进入函数将会保留上一次的值。在不同的源文件中,使用了相同名字的函数与变量名,方便模块化。* 仅能在当前源文件使用。2与3主要应用在如下情况。
2025-04-05 12:39:24
140
原创 BKP备份备份寄存器与unix时间戳
复位后,对备份寄存器和RTC的访问被禁止,并且备份域被保护以防止可能存在的意外的写操 作。执行以下操作可以使能对备份寄存器和RTC。即由Vbat 单独供电 受其控制,不受主控Vdd电源的影响,但Vbat掉电,则该功能无效,一般Vbat独立外接备用电源【电池】(1)temper引脚 防入侵(当拆机时会导致该引脚电平变化,触发防入侵功能,使得bkp内容被清除)当系统在待机模式下被唤醒,或系统复位或。电源复位时,他们也不会被复位。控制寄存器用来管理侵入检测和。电源被切断,他们仍然由。
2024-06-25 17:29:29
217
原创 对于stm32 F1系列的时钟树的认识
单片机需要信号时序来维持其功能,其时序脉冲由晶振产生,经过各种倍频以及分频器的作用,以产生对应设备所需的时序。
2024-05-21 14:14:08
185
原创 DMA数据转移
DMA,全称Direct Memory Access,即直接存储器访问。DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。DMA无须CPU的干预,通过DMA数据可以快速地移动。这就节省了CPU的资源来做其他操作。DMA的触发方式软件触发:用于寄存器之间的数据搬移,入flash搬到SRAM中硬件触发:外设的数据产生需要一定时机,故需要在的硬件反应后const 常量也存放在flash中该表为各个参数及寄存器的地址。
2024-05-21 14:13:56
780
原创 adc模数转换
注意:在使用adc的时候gpio模式需要选择 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//复位校准是否完成。实际问题中,数据抖动严重,可以采用均值滤波的方法,即,去多个值求平均,作为最后的输出值,减小抖动。
2024-05-21 14:13:42
187
原创 stm32 usart
(hex 模式只显示原始数据,即16进制或者2进制数)如若使用usart的接收功能,则需要开启中断。(文本模式 数据编码之后显示 可以显示字符)1. 开启gpio和usart时钟。2.配置对应引脚为接收与发送模式。3.配置usart的结构体参数。传输仅以十六进制数进行传输。发送功能不需要用到中断。
2024-05-21 14:13:25
218
原创 stm32 定时器IC输入捕获的相关知识
1首先考虑测量频率:测频法更适合测量高频(T可以自己设定);while测周法更适合低频。ps:捕获输入的一般为下图的方波,若要检测正弦波则需要进行转换。方波的高电平一般为3.3v 低电平为0v。测频法:有上升沿,记录值N就+1。若需要测量的f>fm,采用测频法。上拉输入:无信号输入时引脚高电平。下拉输入:无信号输入时引脚低电平。关于二者测量方法,我们可知道。(图片与学习内容来自江科大)如下是三种频率的测量方法。
2024-04-28 15:10:01
296
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅
5