STM32F4 | SYSTEM文件夹介绍 | delay文件夹 | sys文件夹 | usart文件夹
文章目录
- 一、delay 文件夹代码介绍
- 1.delay_init 函数
- 2.delay_us 函数
- 3.delay_ms函数
- 二、sys 文件夹代码介绍
- 1.IO 口的位操作实现
- 三、usart 文件夹代码介绍
- 1.printf 函数支持
在 新建工程模板——库函数版本中,我们用到了一个 SYSTEM
文件夹里面的代码,此文件夹里面的代码由 ALIENTEK
提供,是 STM32F4xx
系列的底层核心驱动函数,可以用在 STM32F4xx
系列的各个型号上面,方便大家快速构建自己的工程。 SYSTEM
文件夹下包含了 delay
、 sys
、 usart
等三个文件夹。分别包含了 delay.c
、 sys.c
、 usart.c
及其头文件。
一、delay 文件夹代码介绍
delay
文件夹内包含了 delay.c
和 delay.h
两个文件,这两个文件用来实现系统的延时功能,其中包含 7 个函数:
void delay_osschedlock(void);
void delay_osschedunlock(void);
void delay_ostimedly(u32 ticks);
void SysTick_Handler(void);
void delay_init(u8 SYSCLK);
void delay_ms(u16 nms);
void delay_us(u32 nus);
在介绍这些函数之前,我们先了解一下编程思想:CM4
内核的处理和 CM3
一样,内部都包含了一个 SysTick
定时器,SysTick
是一个 24 位的倒计数定时器,当计到 0 时,将从 RELOAD
寄存器中自动重装载定时初值。只要不把它在 SysTick
控制及状态寄存器中的使能位清除,就永不停息。我们就是利用 STM32
的内部 SysTick
来实现延时的,这样既不占用中断,也不占用系统定时器。
这里,我们以 UCOSII
为例,介绍如何实现操作系统和我们的 delay
函数共用 SysTick
定时器。首先,我们简单介绍下 UCOSII
的时钟:ucos
运行需要一个系统时钟节拍(类似 “心跳”),而这个节拍是固定的(由 OS_TICKS_PER_SEC
宏定义设置),比如要求 5ms
一次(即可设置:OS_TICKS_PER_SEC=200
),在 STM32
上面,一般是由 SysTick
来提供这个节拍,也就是 SysTick
要设置为 5ms
中断一次,为 ucos
提供时钟节拍,而且这个时钟一般是不能被打断的(否则就不准了)。
因为在 ucos
下 systick
不能再被随意更改,如果我们还想利用 systick
来做 delay_us
或者delay_ms
的延时,就必须想点办法了,这里我们利用的是时钟摘取法。以 delay_us
为例,比如:delay_us(50)
,在刚进入 delay_us
的时候先计算好这段延时需要等待的 systick
计数次数,这里为 50*180
(假设系统时钟为 180Mhz
,因为我们设置 systick
的频率为系统时钟频率,那么 systick
每增加 1,就是 1/180us
),然后我们就一直统计 systick
的计数变化,直到这个值变化了 50*180
,一旦检测到变化达到或者超过这个值,就说明延时 50us
时间到了。这样,我们只是抓取 SysTick
计数器的变化,并不需要修改 SysTick
的任何状态,完全不影响 SysTick
作为 UCOS
时钟节拍的功能,这就是实现 delay
和操作系统共用 SysTick
定时器的原理。
1.delay_init 函数
该函数用来初始化 2 个重要参数:fac_us
以及 fac_ms
;同时把 SysTick
的时钟源选择为外部时钟,如果需要支持操作系统(OS
),只需要在 sys.h
里面,设置 SYSTEM_SUPPORT_OS
宏的值为 1 即可,然后,该函数会根据delay_ostickspersec
宏的设置,来配置 SysTick
的中断时间,并开启 SysTick
中断。具体代码如下:
//初始化延迟函数
//当使用ucos的时候,此函数会初始化ucos的时钟节拍
//SYSTICK的时钟固定为AHB时钟
//SYSCLK:系统时钟频率
void delay_init(u8 SYSCLK)
{#if SYSTEM_SUPPORT_OS //如果需要支持OS.u32 reload;
#endifHAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);//SysTick频率为HCLKfac_us=SYSCLK; //不论是否使用OS,fac_us都需要使用
#if SYSTEM_SUPPORT_OS //如果需要支持OS.reload=SYSCLK; //每秒钟的计数次数 单位为K reload*=1000000/delay_ostickspersec; //根据delay_ostickspersec设定溢出时间//reload为24位寄存器,最大值:16777216,在180M下,约合0.745s左右 fac_ms=1000/delay_ostickspersec; //代表OS可以延时的最少单位 SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//开启SYSTICK中断SysTick->LOAD=reload; //每1/OS_TICKS_PER_SEC秒中断一次 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
#else
#endif
}
可以看到,delay_init
函数使用了条件编译,来选择不同的初始化过程,如果不使用 OS
的时候,只是设置一下 SysTick
的时钟源以及确定 fac_us
值。而如果使用 OS
的时候,则会进行一些不同的配置,这里的条件编译是根据SYSTEM_SUPPORT_OS
这个宏来确定的,该宏在sys.h
里面定义。SysTick
是 MDK
定义了的一个结构体,里面包含 CTRL
、LOAD
、VAL
、CALIB
等 4 个寄存器,
/**\brief Structure type to access the System Timer (SysTick).*/
typedef struct
{__IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */__IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */__IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */__IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */
} SysTick_Type;
CTRL
是SysTick
控制和状态寄存器。SysTick->CTRL
的各位定义如图所示:
LOAD
是SysTick
自动重装载除值寄存器。SysTick-> LOAD
的定义如图所示:
VAL
是SysTick
当前值寄存器。SysTick-> VAL
的定义如图所示:
CALIB
是SysTick
校准值寄存器。SysTick-> CALIB
不常用,在这里我们也用不到,故不介绍了。
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
这句代码把 SysTick
的时钟选择为内核时钟,这里需要注意的是:SysTick
的时钟源自 HCLK
,假设我们外部晶振为 25M
,然后倍频到 180MHZ
,那么 SysTick
的时钟即为 180Mhz
,也就是 SysTick
的计数器 VAL
每减 1,就代表时间过了 1/180us
。所以fac_us=SYSCLK;
这句话就是计算在 SYSCLK
时钟频率下延时 1us
需要多少个 SysTick
时钟周期。
在不使用 OS
的时候:fac_us
为 us
延时的基数,也就是延时 1us
,Systick
定时器需要走过的时钟周期数。 当使用 OS
的时候,fac_us
,还是 us
延时的基数,不过这个值不会被写到SysTick->LOAD
寄存器来实现延时,而是通过时钟摘取的办法实现的。而fac_ms
则代表 ucos
自带的延时函数所能实现的最小延时时间(如 delay_ostickspersec=200
,那么 fac_ms
就是 5ms
)。
2.delay_us 函数
该函数用来延时指定的 us
,其参数 nus
为要延时的微秒数。该函数有使用 OS
和不使用 OS
两个版本,这里我们首先介绍不使用 OS
的时候,实现函数如下:
//延时nus
//nus为要延时的us数.
//nus:0~190887435(最大值即2^32/fac_us@fac_us=22.5)
void delay_us(u32 nus)
{ u32 ticks;u32 told,tnow,tcnt=0;u32 reload=SysTick->LOAD; //LOAD的值 ticks=nus*fac_us; //需要的节拍数 told=SysTick->VAL; //刚进入时的计数器值while(1){tnow=SysTick->VAL; if(tnow!=told){ if(tnow<told)tcnt+=told-tnow; //这里注意一下SYSTICK是一个递减的计数器就可以了.else tcnt+=reload-tnow+told; told=tnow;if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出.} }
}
这里就正是利用了我们前面提到的时钟摘取法,ticks
是延时 nus
需要等待的 SysTick
计数次数(也就是延时时间),told
用于记录最近一次的 SysTick->VAL
值,然后 tnow
则是当前的SysTick->VAL
值,通过他们的对比累加,实现 SysTick
计数次数的统计,统计值存放在 tcnt
里面,然后通过对比 tcnt
和 ticks
,来判断延时是否到达,从而达到不修改 SysTick
实现 nus
的延时。
对于使用 OS
的时候,delay_us
的实现函数和不使用 OS
的时候方法类似,都是使用的时钟摘取法,只不过使用 delay_osschedlock
和delay_osschedunlock
两个函数,用于调度上锁和解锁,这是为了防止 OS
在 delay_us
的时候打断延时,可能导致的延时不准,所以我们利用这两个函数来实现免打断,从而保证延时精度。
//延时nus
//nus:要延时的us数.
//nus:0~190887435(最大值即2^32/fac_us@fac_us=22.5)
void delay_us(u32 nus)
{ u32 ticks;u32 told,tnow,tcnt=0;u32 reload=SysTick->LOAD; //LOAD的值 ticks=nus*fac_us; //需要的节拍数 delay_osschedlock(); //阻止OS调度,防止打断us延时told=SysTick->VAL; //刚进入时的计数器值while(1){tnow=SysTick->VAL; if(tnow!=told){ if(tnow<told)tcnt+=told-tnow; //这里注意一下SYSTICK是一个递减的计数器就可以了.else tcnt+=reload-tnow+told; told=tnow;if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出.} };delay_osschedunlock(); //恢复OS调度
}
3.delay_ms函数
该函数是用来延时指定的 ms
的,其参数 nms
为要延时的毫秒数。该函数有使用 OS
和不使用 OS
两个版本,这里我们分别介绍,首先是不使用 OS
的时候,实现函数如下:
//延时nms
//nms:要延时的ms数
void delay_ms(u16 nms)
{u32 i;for(i=0;i<nms;i++) delay_us(1000);
}
该函数其实就是多次调用前面所讲的 delay_us
函数,来实现毫秒级延时的。
再来看看使用 OS
的时候,delay_ms
的实现函数如下:
//延时nms
//nms:要延时的ms数
//nms:0~65535
void delay_ms(u16 nms)
{ if(delay_osrunning&&delay_osintnesting==0)//如果OS已经在跑了,并且不是在中断里面(中断里面不能任务调度) { if(nms>=fac_ms) //延时的时间大于OS的最少时间周期 { delay_ostimedly(nms/fac_ms); //OS延时}nms%=fac_ms; //OS已经无法提供这么小的延时了,采用普通方式延时 }delay_us((u32)(nms*1000)); //普通方式延时
}
该函数中,delay_osrunning
是 OS
正在运行的标志,delay_osintnesting
则是 OS
中断嵌套次数,必须 delay_osrunning
为真,且 delay_osintnesting
为 0 的时候,才可以调用 OS
自带的延时函数进行延时(可以进行任务调度),delay_ostimedly
函数就是利用 OS
自带的延时函数,实现任务级延时的,其参数代表延时的时钟节拍数(假设 delay_ostickspersec=200
,那么delay_ostimedly (1)
,就代表延时 5ms
)。
当 OS
还未运行的时候,我们的 delay_ms
就是直接由 delay_us
实现的,OS
下的 delay_us
可以实现很长的延时(达到 204 秒)而不溢出!,所以放心的使用 delay_us
来实现 delay_ms
,不过由于 delay_us
的时候,任务调度被上锁了,所以还是建议不要用 delay_us
来延时很长的时间,否则影响整个系统的性能。
当 OS
运行的时候,我们的 delay_ms
函数将先判断延时时长是否大于等于 1 个 OS
时钟节拍fac_ms
,当大于这个值的时候,我们就通过调用 OS
的延时函数来实现(此时任务可以调度),不足 1 个时钟节拍的时候,直接调用 delay_us
函数实现(此时任务无法调度)。
二、sys 文件夹代码介绍
sys
文件夹内包含了 sys.c
和 sys.h
两个文件。在 sys.h
里面定义了 STM32F4
的 IO
口位操作输入读取宏定义和输出宏定义以及类型别名。sys.c
里面除了定义时钟系统配置函数Stm32_Clock_Init
外主要是一些汇编函数。本小节我们主要向大家介绍 sys.h
头文件里面的 IO
口位操作。
1.IO 口的位操作实现
该部分代码在 sys.h
文件中,实现对STM32F4
各个 IO
口的位操作,包括读入和输出。当然在这些函数调用之前,必须先进行 IO
口时钟的使能和 IO
口功能定义。此部分仅仅对IO
口进行输入输出读取和控制。
位带操作简单的说,就是把每个比特膨胀为一个 32 位的字,当访问这些字的时候就达到了访问比特的目的,比如说 GPIO
的 ODR
寄存器有 32 个位,那么可以映射到 32 个地址上,我们去访问这 32 个地址就达到访问 32 个比特的目的。这样我们往某个地址写 1 就达到往对应比特位写 1 的目的,同样往某个地址写 0 就达到往对应的比特位写 0 的目的。
对于上图,我们往 Address0
地址写入 1,那么就可以达到往寄存器的第 0 位 Bit0
赋值1 的目的。下面我们看看 sys.h 中位带操作的定义,代码如下:
//位带操作,实现51类似的GPIO控制功能
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).M4同M3类似,只是寄存器地址变了.
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
//IO口地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+20) //0x40020014
#define GPIOB_ODR_Addr (GPIOB_BASE+20) //0x40020414
#define GPIOC_ODR_Addr (GPIOC_BASE+20) //0x40020814
#define GPIOD_ODR_Addr (GPIOD_BASE+20) //0x40020C14
#define GPIOE_ODR_Addr (GPIOE_BASE+20) //0x40021014
#define GPIOF_ODR_Addr (GPIOF_BASE+20) //0x40021414
#define GPIOG_ODR_Addr (GPIOG_BASE+20) //0x40021814
#define GPIOH_ODR_Addr (GPIOH_BASE+20) //0x40021C14
#define GPIOI_ODR_Addr (GPIOI_BASE+20) //0x40022014
#define GPIOJ_ODR_ADDr (GPIOJ_BASE+20) //0x40022414
#define GPIOK_ODR_ADDr (GPIOK_BASE+20) //0x40022814#define GPIOA_IDR_Addr (GPIOA_BASE+16) //0x40020010
#define GPIOB_IDR_Addr (GPIOB_BASE+16) //0x40020410
#define GPIOC_IDR_Addr (GPIOC_BASE+16) //0x40020810
#define GPIOD_IDR_Addr (GPIOD_BASE+16) //0x40020C10
#define GPIOE_IDR_Addr (GPIOE_BASE+16) //0x40021010
#define GPIOF_IDR_Addr (GPIOF_BASE+16) //0x40021410
#define GPIOG_IDR_Addr (GPIOG_BASE+16) //0x40021810
#define GPIOH_IDR_Addr (GPIOH_BASE+16) //0x40021C10
#define GPIOI_IDR_Addr (GPIOI_BASE+16) //0x40022010
#define GPIOJ_IDR_Addr (GPIOJ_BASE+16) //0x40022410
#define GPIOK_IDR_Addr (GPIOK_BASE+16) //0x40022810 //IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入 #define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入 #define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //输出
#define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //输入 #define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出
#define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入 #define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //输出
#define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //输入#define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出
#define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入#define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //输出
#define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //输入#define PHout(n) BIT_ADDR(GPIOH_ODR_Addr,n) //输出
#define PHin(n) BIT_ADDR(GPIOH_IDR_Addr,n) //输入#define PIout(n) BIT_ADDR(GPIOI_ODR_Addr,n) //输出
#define PIin(n) BIT_ADDR(GPIOI_IDR_Addr,n) //输入#define PJout(n) BIT_ADDR(GPIOJ_ODR_Addr,n) //输出
#define PJin(n) BIT_ADDR(GPIOJ_IDR_Addr,n) //输入#define PKout(n) BIT_ADDR(GPIOK_ODR_Addr,n) //输出
#define PKin(n) BIT_ADDR(GPIOK_IDR_Addr,n) //输入
以上代码的便是 GPIO
位带操作的具体实现。比如说,我们调用 PAout(1)=1
是设置了 GPIOA
的第一个管脚 GPIOA.1
为 1,实际是设置了寄存器的某个位,但是我们的定义中可以跟踪过去看到却是通过计算访问了一个地址。上面一系列公式也就是计算GPIO
的某个 IO
口对应的位带区的地址了。
有了上面的代码,我们就可以像51一样操作STM32
的IO
口了。比如,我要PORTA
的第七个 IO
口输出 1,则可以使用 PAout(6)=1;
即可实现。我要判断 PORTA
的第 15个位是否等于 1,则可以使用 if(PAin(14)==1)…;
就可以了。
这里顺便说一下,在 sys.h
中的还有个全局宏定义:
//0,不支持os
//1,支持os
#define SYSTEM_SUPPORT_OS 0 //定义系统文件夹是否支持OS
SYSTEM_SUPPORT_OS
,这个宏定义用来定义 SYSTEM
文件夹是否支持 ucos
,如果在 ucos
下面使用 SYSTEM
文件夹,那么设置这个值为 1 即可,否则设置为 0(默认)。
三、usart 文件夹代码介绍
该文件夹下面有 usart.c
和 usarts.h
两个文件。串口相关知识将在串口实验中讲解。
1.printf 函数支持
printf
函数支持的代码在 usart.c
文件的最上方,在我们初始化和使能串口 1 之后,然后把这段代码加入到工程,便可以通过 printf
函数向串口 1 发送我们需要的内容,方便开发过程中查看代码执行情况以及一些变量值。这段代码如果要修改一般也只是用来改变printf
函数针对的串口号,大多情况我们都不需要修改。代码如下:
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
//#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{ int handle;
}; FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{ x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{ while((USART1->SR&0X40)==0);//循环发送,直到发送完毕 USART1->DR = (u8) ch; return ch;
}
#endif
STM32F4 | SYSTEM文件夹介绍 | delay文件夹 | sys文件夹 | usart文件夹相关推荐
- 【正点原子sys、delay、usart文件夹介绍】
正点原子sys.delay.usart文件夹介绍 文章目录 正点原子sys.delay.usart文件夹介绍 一.sys文件夹函数 二.delay文件夹函数 2.1文件夹函数介绍 2.2 测试程序运行 ...
- 批处理命令html文件合并,cmd命令行无损合并批处理TS视频文件
有时,当我们从Internet下载一些视频文件时,我们发现某些视频格式为.ts格式,并且被分成N个以上的小文件. 特别是,某些视频以.ts格式(MPEG2-TS流)播放. 因此,如果要下载此视频,则需 ...
- 【STM32】SYSTEM文件夹介绍,delay,sys,usart
文章目录 delay 文件夹 sys 文件夹 usart 文件夹 uart_init() USART1_IRQHandler 函数 usart.h 对串口 printf 的支持代码 delay 文件夹 ...
- Android 文件夹介绍
目录列表详细对照表:1..android_secure 是官方app2sd的产物,删了之后装到sd卡中的软件就无法使用了.2..Bluetooth 顾名思义,用蓝牙之后就会有这个.3..mobo 顾名 ...
- postman怎么导出测试用例_postman---postman文件夹介绍以及批量执行用例
我们在做测试的过程中,都会多次请求接口,都会把接口保存下来,方便下次直接请求,节省时间不用每次都重新输入,我们一起看下Postman如何保存接口会话 保存请求作用 Postman可以将各个请求组合保存 ...
- win10电脑服务器在哪个文件夹下,Win10桌面背景在哪个文件夹?Win10桌面背景所在文件夹介绍...
最近有Win10用户反映,之前电脑有设了张很好看的桌面背景,但后来不小心给换成了别的,现在想换回来,却不知道要在哪个文件夹找那张桌面背景,用户为此非常困恼.那么,Win10桌面背景在哪个文件夹呢?下面 ...
- linux复制文件夹 实例,linux复制文件夹与文件实例介绍linux操作系统 -电脑资料
本文章来介绍一下关于linux复制文件夹与文件的一些基础用法,有需要学习的朋友可参考一下本文章, CP命令 格式: CP [选项] 源文件或目录 目的文件或目录 选项说明:-b 同名,备分原来的文件 ...
- php遍历目录与文件夹,介绍几种php遍历目录与文件夹的方法
介绍几个php遍历目录的方法,可以遍历目录及目录中的文件,供大家参考 遍历目录或遍历目录下指定类型的文件,这是每一个童鞋在写程序的时候难免会用到的.PHP本身也提供了很多灰常有用的函数,正确地使用它们 ...
- linux的重要文件,Linux中重要文件夹介绍
Linux中重要的文件夹介绍 嵌入式助教苗沛2014 01 08 知识讲解 对于linux新手来说 最感到迷惑的问题之一就是文件都存在哪里呢 特别是对于那些从windows转过来的新手来说 linux ...
最新文章
- 计算机名称改变之后,HOUDINI Server 连接不上的解决办法
- sap 教学视频网址
- PHP-Curl模拟HTTP请求
- CSS基础「六」元素的显示与隐藏
- linux 内核同步--理解原子操作、自旋锁、信号量(可睡眠)、读写锁、RCU锁、PER_CPU变量、内存屏障
- Linux中让普通用户拥有超级用户的权限
- CUDA编程之:Stream(流)
- mysql cnf参数_系统运维|MySQL my.cnf参数配置优化详解
- 趣学 C 语言(六)—— 结构和联合
- 白化(Whitening): PCA白化 ZCA白化
- 空气投影+生物识别,支付宝的“如影计划”还有多少硬仗要打?
- 页面编码和js文件不同导致的IE6下脚本错误
- does not point to a valid jvm installation
- magicbook的linux是哪个版本,MagicBook 2019 linux版来了!
- 大学英语精读第二版(第五册)复习笔记——文章内容摘要
- 正雅齿科运用新数字技术为正畸行业开辟新空间
- excel 锁定第一行
- 【软件测试】稳定性和可靠性测试在软件开发中的重要性
- OpenCV图像特征提取学习三,LBP图像特征检测算法
- C++工程编译链接错误汇总VisualStudio
热门文章
- flowable会签时, act_run_task 表内有数据但是 assignee字段没值
- 31岁程序员的苦恼:妻子总让我想办法考体制,铁饭碗总比码农强
- 【TouchDesigner】生成艺术
- html5微信拍照后自动刷新,html5 调用微信jssdk 没有调用相机,只显示最近的图片文件。...
- 爬虫:JSON文件存储
- 面试题--cpu高解决方案以及火焰图学习
- windbg输出SXS.DLL的调试信息
- 一个事件订阅和发布的库(onfire.js)的源码浅析
- 微信小程序开发支付流程
- 电视剧 | 黑镜S5E2 碎片