- 博客(33)
- 收藏
- 关注
原创 基于Linux系统和ncurses库的贪吃蛇小游戏
ncurses库是什么我并没有深入了解,本文的重点也不是ncurses的使用,至于为什么要使用ncurses库,我们先来看一个问题:我们要通过方向键来控制蛇的走向,用c的库函数scanf和getchar都需要我们输入字符后再按回车,这显然不合理,而ncurses库就能解决这些问题,我们不需要知道他是怎么实现的,我们只需要会用就行了,因为这节主要是巩固链表的知识。最后有源码展示。最终效果:蛇活动的范围为 20x20 ,地图的左右边界用 "|" 符号表示,上下边界用 "--" ,蛇身用 "[]"表示,食物用##
2024-08-26 15:11:15
1006
1
原创 链表(静态/动态创建,遍历,计数,查找,在节点的前/后方插入新节点,头插法,尾插法)
链表本质是一个结构体,结构体里不仅包含不同类型的数据,最重要的是定义了一个指向相同结构体类型的指针(必须是相同类型的结构体),通过这个指针我们就可以将一个又一个的结构体链接(指针存放下一个结构体的地址)起来,每一个结构体就是链表的其中一个节点。我们使用链表的时候可以和数组类比,但两者还是有区别,数组是内存中连续的空间;而链表不一定在内存中连续。使用malloc函数来为每一个新节点分配内存,如果输入的节点id为0代表结束创建,free函数释放最后建立的节点,并返回head头节点的地址。贪吃蛇游戏下篇博客更新。
2024-08-25 03:49:49
656
原创 freeRTOS任务通知(Task Notifications)
所谓"任务通知",你可以反过来读"通知任务"。我们使用队列、信号量、事件组等等方法时,并不知道对方是谁。使用任务通知时,可以明确指定:通知哪个任务。使用队列、信号量、事件组时,我们都要事先创建对应的结构体,双方通过中间的结构体通信:效率更高:使用任务通知来发送事件、数据给某个任务时,效率更高。比队列、信号量、事件组 都有大的优势。更节省内存:使用其他方法时都要先创建对应的结构体,使用任务通知时无需额外创建结构体。
2024-08-13 16:59:17
826
原创 freeRTOS事件组(event group)
学校组织秋游,组长在等待:张三:我到了李四:我到了王五:我到了组长说:好,大家都到齐了,出发!张三、李四、王五谁先写好就交谁的。在这个日常生活场景中:出发:要等待这3个人都到齐,他们是"与"的关系交报告:只需等待这3人中的任何一个,他们是"或"的关系在FreeRTOS中,可以使用事件组(event group)来解决这些问题。事件组可以简单地认为就是一个整数:事件组的每一位表示一个事件。
2024-08-13 14:44:49
641
原创 freeRTOS互斥量(mutex)
可以看到,使用信号量确实也可以实现互斥访问,但是不完美。使用互斥量可以解决这个问题,互斥量的名字取得很好:量:值为0、1互斥:用来实现互斥访问它的核心在于:谁上锁,就只能由谁开锁。即使任务A获得了互斥锁,任务B竟然也可以释放互斥锁。谁上锁、谁释放:只是约定。互斥锁解决的核心问题其实是优先级反转和优先级继承。互斥量其实就是一种特殊的二进制信号量,只不过它能解决优先级反转和实现优先级继承。二、互斥量函数。
2024-08-12 00:22:54
1105
原创 FreeRTOS信号量(semaphore)
前面介绍的队列(queue)可以用于传输数据:在任务之间、任务和中断之间。有时候我们只需要传递状态,并不需要传递具体的信息,比如:我的事做完了,通知一下你卖包子了、卖包子了,做好了1个包子!做好了2个包子!做好了3个包子!这个停车位我占了,你们只能等着在这种情况下我们可以使用信号量(semaphore),它更节省内存。信号量(Semaphore),是在多任务环境下使用的一种机制,是可以用来保证两个或多个关键代 码段不被并发调用。信号量这个名字,我们可以把它拆分来看,
2024-08-10 19:23:36
647
原创 FreeRTOS队列(queue)
队列又称消息队列,是一种常用于任务间通信的数据结构,队列可以在任务与任务间、中断和任 务间传递信息。为什么不使用全局变量?如果使用全局变量,兔子(任务1)修改了变量 a ,等待树獭(任务3)处理,但树獭处理速度很 慢,在处理数据的过程中,狐狸(任务2)有可能又修改了变量 a ,导致树獭有可能得到的不是正确的数据。在这种情况下,就可以使用队列。兔子和狐狸产生的数据放在流水线上,树獭可以慢慢一个个依次处理。关于队列的几个名词::队列中的每一个数据;:队列能够存储队列项目的最大数量;
2024-08-09 16:01:07
1207
原创 Linux线程编程(创建&退出&等待&互斥锁&条件变量)
典型的UNIX/Linux进程可以看成只有一个控制线程:一个进程在同一时刻只做一件事情。有了多个控制线程后,在程序设计时可以把进程设计成在同一时刻做不止一件事,每个线程各自处理独立的任务。所谓线程,就是操作系统所能调度的最小单位。普通的进程,只有一个线程在执行对应的逻辑。我们可以通过多线程编程,使一个进程可以去执行多个不同的任务。相比多进程编程而言,线程享有共享资源,即在进程中出现的全局变量, 每个线程都可以去访问它,与进程共享“4G”内存空间,使得系统资源消耗减少。
2024-07-28 18:19:02
592
原创 Linux进程间通信(共享内存&信号&信号量)
共享内存一般都结合信号量来使用,假设我们写了一个无限循环的程序,想要让它停止运行,我们会按Ctrl+C键来终止进程,其实就是向进程发送了第2个信号SIGINT,kill是发送信号的指令,-9表示发送第9个信号,也就是发送SIGKILL信号给对应ID的进程,信号的处理有三种方法,分别是:忽略、捕捉和默认动作,信号量(semaphore)与已经介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。信号量无法实现进程间收发数据。
2024-07-23 09:39:30
767
原创 Linux进程间通信(管道,命名管道/FIFO,消息队列)
它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不 是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。创建管道后,数据不知道要怎么走,管道要怎么连接?所以我们要把路“打通”,告诉水要怎么流。由于管道是半双工的,所以两条路不能同时选,避免干扰,要把不用的路给断开。当传入一个数组名字时,sizeof返回整个数组在内存中占用的字节数,而不是数组中存储的实际数据的长度,而strlen会返回字符串"\0"之前的字符串的长度。按照定义的消息结构体接受处理消息
2024-07-21 02:25:04
776
2
原创 Linux进程编程(exec族函数,system函数,popen函数)
一个进程要执行一个不同的程序。这对shell是常见的情况。在这种情况下,子进程从fork返回后立即调用exec函数。在调用进程内部执行一个可执行文件。path:可执行文件的绝对路径arg:可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束。我们之前执行可执行文件都是用 ./+可执行文件名的方式,前面加上sh -c 则是另外一种方式,不过两者实现功能一样。
2024-07-16 08:41:11
937
原创 Linux进程编程(vfork函数,进程退出,父进程等待子进程退出及其相关函数)
vfork函数也可以创建进程,它与fork函数有两个关键的区别在我的上一篇文章中讲到,子进程会拷贝父进程的代码,并且开辟一个新的存储空间,父子进程之间的变量互不干扰,而vfork与之相反。这里先用fork函数创建进程,运行结果为:我们来修改代码,将fork改成vfork,其运行结果为:可见子进程一直在执行,只有子进程退出后才轮到父进程执行。
2024-07-14 00:01:50
915
原创 Linux进程编程(使用fork函数创建进程以及fork函数的实际引用场景)
每个进程都有一个非负整数表示的唯一ID,叫做pid,它就像进程的身份证一样。Linux操作系统中,默认pid=0的进程为交换进程(swapper),作用是进程的调度;默认pid=1的进程为init进程,作用是系统的初始化。编程调用getpid函数获取自身的进程标识符,getppid获取父进程的进程标识符。 由fork创建的新进程被称为子进程(child process)。fork函数被调用一次,但返回两次。两次返回的唯一区别是子进程的返回值是0,而父进程的返回值则是新子进程的进程ID。
2024-07-13 01:17:56
838
原创 Linux文件编程(标准C库)
前面讲到的open函数都是基于linux内核的,也就是说在Windows系统上无法运行,移植性比较差,但标准C库同样有一套对文件进行操作的函数。
2024-07-11 22:18:20
893
原创 Linux文件编程应用
我们学习了文件编程的常用指令以及了解文件编程的基本步骤后,试着来写一些程序实现某些功能。我们假设我们已经把程序写好了,那么在终端上我们会输入“./a.out demo4.c demo7.c”以实现和cp指令同样的操作,那么这个argc就是从第一个文件名开始数,一共有多少个文件,argc就是文件的个数;argv是一个二级指针,可以理解成指针里的每一项都是一个数组。1.文件光标位置,在进行读写操作之前,确认光标处于正确的位置。2.打开文件后必须关闭文件,避免造成数据损坏。
2024-07-11 02:36:14
665
原创 Linux文件编程(打开/创建&写入&读取&移动光标)
发现读到了0个字节,内容也是空的,这里就要提到文件光标了,我们写入了内容后,光标是在我们写入内容的后面,这时候去读文件,自然是从光标位置往后读,所以读到是空。
2024-07-09 20:52:18
934
原创 FreeRTOS任务调度
上个文章概述了FreeRTOS的功能以及简单的移植,详情看,本章概述FreeRTOS任务之间是如何切换和任务内部运行等细节。
2024-05-20 19:30:29
740
5
原创 FreeRTOS入门(如何移植&任务创建与删除)
RTOS的全称是Real time operating system,中文就是实时操作系统。这里的RTOS不是指某一个确定的系统,而是指一类操作系统。比如:uc/OS,FreeRTOS,RTX, RT-Thread等这些都是RTOS类操作系统。我们之所以选择FreeRTOS有以下几点原因;·FreeRTOS是免费的;·很多半导体厂商产品的SDK(Software Development Kit)软件开发工具包,就使用FreeRTOS 作为其操作系统,尤其是WIFI、蓝牙这些带有协议栈的芯片或模块。
2024-05-17 23:05:02
929
原创 蓝桥杯嵌入式模板(cubemx&keil5)
引脚PC8~PC15,默认高电平(灭)。选择PB0~PB2,PA0为输入模式,配置成上拉输入。我们按键用的是定时器轮询检测按键状态(还能实现长按短按的功能)。时钟源选择内部时钟,设置成100Hz,也就是10ms进一次中断,别忘了在NVIC Settings打勾key_proc()丢while里。板子从左往右数第一个是PB15引脚(对应ADC2的通道15),第二个是PB12引脚(对应ADC1的通道11),采样周期选到最大,一定程度上能防止adc一直抖动。
2024-04-08 22:12:10
815
3
原创 IWDG&WWDG(STM32学习笔记)
这里与独立看门狗不懂,独立看门狗在超时时间之前喂狗就不会引起复位;窗口看门狗要在窗口时间和超时时间之间喂狗,由PCLK1作为时钟源,需要调用RCC库函数开启,并且进来后会有一个4096的分频,图中没标出来。模板代码配置了窗口时间30ms,超时时间50ms的窗口看门狗,所以在喂第一次狗后(代码最后一行),30ms之后、50ms之前要喂狗,否则会引起复位。看门狗能在停机和待机模式下工作,从上节我们能得知,IWDG复位也能唤醒待机模式下的主机。只要在超时时间之内完成喂狗,独立看门狗就不会复位。
2024-02-10 22:42:38
323
3
原创 PWR(低功耗模式)(STM32学习笔记)
进入待机模式后,只能由特定的方式唤醒STM32:如设定RTC闹钟,使用RTC_SetAlarm();只需要一句代码,程序在执行到这句后立刻进入睡眠模式,只要有任一中断信号产生,就会停止睡眠,并从暂停的地方开始执行下一步代码。进入停止模式,里面有两个参数选择,第一个选择电压调节器是打开还是处于低功耗模式,低功耗模式下唤醒会有一定的延时;唤醒条件表中也给出了,WFI表示中断唤醒,WFE表示事件唤醒(本节采用中断唤醒)。本节学习如何使用PWR的低功耗模式来降低STM32的功耗,以及各种模式对应的使用条件。
2024-02-09 00:59:10
465
1
原创 BKP&RTC(STM32学习笔记)
我们先使能LSE外部低速时钟,然后选择LSE作为RTC的时钟源,再配置RTC各个参数就可以了,本节不使用闹钟和中断的功能。本节使用的STM32F103C8T6是中容量单片机,所以只能存储20字节的数据,共有10个DR数据寄存器,每个寄存器能存储两个字节的数据。可以对秒计数器进行读写操作,想要将秒计数器里的值转换成想要的年月日时分秒,需要用到<time.h>库里的函数。我们一般选择LSE作为RTC的时钟源,再经过分频器输出1Hz的信号给RTC的秒计数器。注意事项里的第二点和第四点,用两个无参的函数就能完成。
2024-02-07 03:02:00
726
1
原创 SPI(STM32学习笔记)
见上图,主机想要给从机发送0xAA,交换一个字节后,主机接收到的是0x55,从机则接收到0xAA,我们如果只想接收数据,就随便发送一个数据给从机;SCK默认低电平,当SS第一个下降沿的时候,MOSI和MISO移出数据,在SCK的上升沿移入数据,如此循环八次,便交换一个字节,所以SPI通信其实就是交换数据。配置SPI结构体一般都是按照上面的参数来配置,SPI不同的模式差别也就在于时钟的相位,是第几个边沿采样的区别,本质上没有区别,这里配置成模式0;MISO是输入端,所以配置为普通的上拉输入;
2024-02-03 17:44:13
1003
3
原创 I2C(STM32学习笔记)
虽然是输出,但仍然可以输入,应为这里默认高电平是一种弱上拉,从机要发送数据只需要进行拉低电平或释放这一种操作,“拉”或“不拉”,主机读取电平就能接收从机发送的数据。附:在主发送和主接收的序列图中,发送从机地址后,我们会判断EV6事件,可以看到后面还接了一个EV8_1事件,这个事件是提醒我们该写入数据发送出去了,因此我们不需要判断EV8_1事件。指定地址读的前三步和指定地址写一样,起始条件后发送从机地址,写寄存器地址,然后重复起始条件,发送从机地址选择“读”,这里判断EV6事件就不同于指定地址写。
2024-01-31 18:16:23
1922
原创 USART(串口协议&通信)(STM32学习笔记)
数据,首先开启RXNE到中断的通道,用USART_ITConfig函数,再配置NVIC优先级分组等参数;见手册描述,我们对DR进行读操作,硬件会将标志位清零,所以这里不需要我们像收数据一样手动清除标志位。为1时标志可以继续发了,和RXNE对应,为1时表示接收到了数据,可以读了,以免继续发数据被覆盖了。本节展示串口收发的功能,通常使用波特率为9600,8位数据位,即无校验位,停止位长度为1的时序。有数据时会触发中断,中断函数从启动文件里找。
2024-01-26 03:41:19
1250
1
原创 DMA(STM32学习笔记)
上一节我们用的是ADC的非扫描模式,主要是因为,ADC多通道扫描时,会不断把数据运到ADC的DR寄存器,但又没有人将数据运出去,就会导致前面的数据被覆盖,所以DMA就派上用场了。,这里的外设只是个名字,只是为了区分,要怎么配置可以按照实际需要选择,我们要把数组A的元素传递给数组B,又把数组A的首地址放在了外设站点,所以选择外设地址为源地址。,DMA是从这里转运数据的,我们定义一个数组用来存放转换值,那么存储器地址(目的地址)就是数组名,函数,ADC就会硬件触发DMA,不需要我们手动检测标志位等操作。
2024-01-24 05:04:24
1887
1
原创 ADC(STM32学习笔记)
所以我们只需要在初始化函数最后触发一次就可以了,不需要一直在while里触发,读取转换值时也不用判断标志位,因为此时数据寄存器会不断刷新最新的转换结果,我们只需要读出来就行了。由手册可知,我们调用 ADC_GetConversionValue函数时,就是读取ADC_DR的值,所以不用再手动对EOC清零。由上图可知,ADCCLK最大频率为14MHz,所以这里选择6分频,即72MHz/6=12MHz。将该函数放在while循环里,就会不断进行软件触发,进而进行ADC转换,当转换完成时,EOC为1。
2024-01-22 11:25:43
373
原创 TIM(编码器接口)(STM32学习笔记)
可见之前的定时器内容,在配置时基单元之前,我们都需要选择时钟,内部时钟或者外部输入时钟等,但是我们这节内容不需要,直接初始化GPIO后配置时基单元,参考手册里有这样一句话:“编码器接口模式基本上相当于使用了一个带有方向选择的外部时钟”,因为编码器接口会托管时钟,可以理解为编码器接口就是一个。PSC选择不分频,ARR给最大值,当编码器反转、计数器自减小于0时,会从65535开始继续减小,但强制转换成int16_t类型时,会转换成从-1开始减小,这样就能用OLED屏幕显示出来。
2024-01-21 06:26:34
825
1
原创 TIM(输入捕获)(STM32学习笔记)
输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用 于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数。TIM_ICSelection配置的是通道连接选择,即直连还是交叉连接,交叉连接一般用于PWMI模式,即可以读出两个CCR的值,用于计算占空比。主要搞懂CNT的变化,明白每一段的CCR代表的是什么,通过主从触发模式实现硬件的自动清零,外部每来一个。输入捕获用于测量外部输入的波形,所以照常初始化GPIO和定时器内部时钟,上节课的代码我们利用了。
2024-01-20 09:03:29
1131
原创 TIM(输出比较&PWM波形)(STM32学习笔记)
(对于普通的开漏/推挽输出,引脚的控制权是来自于输出数据寄存器的,如果想让定时器控制引脚,就需要使用复用开漏/推挽输出,这时输出数据寄存器被断开,输出控制权转移给片上外设,这里片上外设的引脚连接的是TIM2的CH1通道)可见还是要用到定时器的功能,通过不断比较CCR和CNT的值,在PWM模式1下就能输出PWM波形,通过修改CCR的值,就能产生不同占空比的PWM波形。这里的ARR=99,PSC=719,CCR=50,那么根据公式可知,输出的是一个频率为1KHz,占空比为50%,分辨率为1%的PWM波形。
2024-01-19 21:24:10
2359
1
原创 TIM(定时中断&外部时钟)(STM32学习笔记)
关于外部时钟模式1和2,我还没搞懂区别,如果你想在ETR外部引脚提供时钟,或者想对ETR时钟进行计数,把这个定时器当做。·不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接 口、主从触发模式等多种功能。TIM _ETRClockMode1Config,选择ETR通过外部时钟模式1输入的时钟。TIM _ETRClockMode2Config,选择ETR通过外部时钟模式2输入的时钟。TIM_ITRxExternalClockConfig,选择ITRx其他定时器的时钟。
2024-01-17 15:26:38
1744
6
原创 EXTI(STM32学习笔记)
去启动文件里的startup_stm32f10x_md.s文件找到函数名为EXTIx_IRQHandler的中断函数x与选中的端口对应(STM32的10到15号端口是共用的一个中断函数 EXTI15_10_IRQHandler)·EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向 NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序。·支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断。
2024-01-15 21:34:15
386
2
原创 GPIO (STM32学习笔记)
GPIO_WriteBit前面两个参数选择引脚,第三个参数填写Bit_RESET或Bit_SET来设置电平。(7)GPIO_Mode_AF_OD 复用开漏输出(第二功能IO口)(8)GPIO_Mode_AF_PP 复用推挽输出(第二功能IO口)(2)GPIO_Mode_IN_FLOATING 浮空输入。(5)GPIO_Mode_Out_OD 开漏输出。(6)GPIO_Mode_Out_PP 推挽输出。(1)GPIO_Mode_AIN 模拟输入。(3)GPIO_Mode_IPD 下拉输入。
2024-01-14 06:29:10
438
2
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人