目录3
一、 关于STM32库函数运用结构体赋予寄存器地址的方式 2
七、 链表在newnode后面记得加上newnode->next=NULL 6
十、 OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL() 6
十八、 关于TI的板子移植程序中各个功能设定在什么文件中 10
二十七、 二维数组\双重指针\数组指针\指针数组的关系 16
6
-
关于STM32库函数运用结构体赋予寄存器地址的方式
-
在库函数运用中
两个全局变量的输入,一个是GPIOA类型的地址,另外一个是初始化配置的枚举定义。初始化的枚举定义是没有地址赋予的,而GPIO是已经在H文件中赋予地址。
-
- 最终变成GPIOA有一个首地址,GPIOA->MODER赋予了一个地址。而后进行的初始化GPIO函数就是用前面定义的初始化结构体先进行赋值然后再送入函数中,在函数中进行地址的查找和对寄存器赋值从而实现了GPIO口的初始化。
-
-
关于GPIO口上下拉代表的意义
- 在输出模式下,引脚受ODR寄存器影响。ODR寄存器对应引脚初始化后默认值为0,引脚输出低电平。所以在输出模式下无论上下拉都不会改变引脚输出低电平这个模式。但如果配置上拉能小幅提高电流输出能力。
-
Static和extern
- Static: 它只在定义它的源文件内有效,其他源文件无法访问它。所以一般有这个的还需要在加一步源文件中的封装,然后再在main中调用这个封装。当定义成普通静态变量时,它只能在或者函数中使用不能在其他的函数中调用。静态局部变量如果没有被用户初始化,则会被编译器自动赋值为0,以后每次调用静态局部变量的时候都用上次调用后的值。
- Extern:它其实就相当于引用,是用在这个函数将要放在的这个文件中的,比如main函数要用其他文件的函数,但是这个时候没有include这个函数所在的文件,那么就要在main中加入extern 这个函数,这样就能调用到没有include的函数了,所以加了include就不用extern了。
-
ARM处理器的工作状态
- 在ARM的体系结构中,可以工作在三种不同的状态,一是ARM状态,二是Thumb状态及Thumb-2状态,三是调试状态。
- ARM状态:处理器工作于32位指令的状态,所有指令均为32位。
- Thumb状态:执行16位指令的状态。
- 两个状态的切换R0[0] = 0 R0指令将进入ARM状态
- Cortex-M3只有thumb-2状态
-
STM32F4中的时钟源
-
HCLK:液晶、内存的相关频率
PCLK:串口等外部速度较慢的设备频率
FLCK:内核频率 三者关系是:FLCK = n * (HCLK = n * PCLK)
AHB分频因子(决定HCLK等于多少),APB2分频因子(决定PCLK2等于的多少),APB1分频因子(决定PCLK1等于多少)
在我们的F407中HCLK = SYSCLK = PLLCLK = 168MHz
PCLK2 = HLCK/2 = 84MHz
PCLK1 = HCLK/4 = 42MHz
- HSE是高速外部时钟信号,由我们的25MHz无源晶振提供,HIS是高速内部时钟,其频率为16MHz,一般SYSCLK都是HSE或者HIS通过PLL倍频后拿来使用的。
-
GPIO和USART1和SPI1等高速外设使用的是AHB,所以函数用的是AHB。
I2C,SPI23和UART2345等用的是APB,所以函数用的hi是APB。
-
Void和void * 的意义
- Void是表示对返回类型的限定和函数参数的限定,void a 是错误的
- Void *指的是定义一个"不确定类型指针" 这个指针可以接受任意变量的赋值
-
链表在newnode后面记得加上newnode->next=NULL
- 删除列表头节点指向的指针时要注意头节点指针要指向下下个节点,尤其是在循环列表中
- 要搞清楚什么时候是空头节点什么时候是头节点有数据
-
*p 和 p 的区别
- *p 表示 p这个指针指向的变量的值
- P 表示p这个指针指向的变量的地址
- Int *p = &n; 表示创建一个指针并且把n的地址赋予p及*p指向n;
-
什么是SP指针、PC指针、R1、R2、PSW
- CPU 按照 PC 指针,到存储器去取指令代码。
CPU 按照 SP 指针,到存储器存取地址或数据。 - R1\R2就是通用寄存器
- PSW就是程序状态寄存器
- CPU 按照 PC 指针,到存储器去取指令代码。
-
OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()
- OS_ENTER_CRITICAL()是关中断,意思是进入关键临界区,绝不能被中断,OS_EXIT_CRITICAL()是开中断
-
什么是DOS,什么是BIOS
- BIOS指(Basic Input & Output System,基本输入输出系统 ),相当于一个与硬件直接打交道的原始级操作系统。电脑的架构是这样的:
硬件——BIOS——操作系统——应用程序,应用程序的指令是通过这几个层次来控制最终的硬件执行。 - DOS是英文Disk Operating System的缩写,意思是"磁盘操作系统"。应该指的就是我们用的Win10
- BIOS指(Basic Input & Output System,基本输入输出系统 ),相当于一个与硬件直接打交道的原始级操作系统。电脑的架构是这样的:
-
uC/OS是如何切换任务的
-
我们先来看一下我们自己创建任务的一般结构。如下所示:
void Task_USER_A(void *pdata)
{
pdata = pdata;
for(;;){
//用户代码
..............
//用户代码
OSTimeDly(); //(1)
OSTimeDlyHMSM(); //(2)
}
}
在每个已经建立的任务(idel除外)中都会看到(1)或(2)这样的延时函数,(1)/(2)实现任务延时,将当前的任务挂起一段时间,延时函数中会去查询任务就续表中优先级最高的任务,通过调用任务调度OSSched()完成任务切换。挂起当前任务,才能让比当前任务优先级低的任务得到CPU而执行。
-
-
带Hook的钩子函数
- 钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。对每种类型的钩子由系统来维护一个钩子链,最近安装的钩子放在链的开始,而最先安装的钩子放在最后,也就是后加入的先获得控制权。
-
uC/OS中任务调用
- 在延时函数中将当前任务挂起,OSTCBCur中的OSTCBBitX记录的是任务在就绪表数组中的位置,取反然后相与就能在就绪表中去除任务。
-
那么延时是怎么实现的呢?
延时1s,在该函数中计算出ticks = 100,然后赋值给OSTCBCur->OSTCBDly
,然后开启中断,在中断大概是10ms进入一次,所以是先延时然后开始寻找下一个任务,而延时中断任务在os_core,c中,延时完要把这个任务重新放到任务就绪表中,以下是函数截图
-
单片机中一些英文符号的释义
- OSC:振荡器
- XTAL:晶振
-
ARM中的指令集
-
MSR,MRS指令用于将程序状态寄存器的内容传送到通用寄存器中。
- 当需要改变程序状态寄存器的内容时,可用MRS将程序状态寄存器的内容读入通用寄存器,修改后再写回程序状态寄存器。
MRS R0,CPSR @传送CPSR的内容到R0
- 当在异常处理或进程切换时,需要保存程序状态寄存器的值,可先用该指令读出程序状态寄存器的值,然后保存。
- MSR指令用亍将操作数的内容传送到程序状态寄存器的特定域中。
- 该指令通常用于恢复或改变程序状态寄存器的内容,在使用时,一般要在MSR指令中指明将要操作的域。
指令示例:
MSR CPSR,R0 @传送R0的内容到CPSR
MSR SPSR,R0 @传送R0的内容到SPSR
MSR CPSR_c,R0 @传送R0的内容到SPSR,但仅仅修改CPSR中的控制位域
-
- 时钟树
-
关于TI的板子移植程序中各个功能设定在什么文件中
- 晶振的频率设定在这个文件中
- 配置晶振的数组在这个文件中
- 获取系统时钟源也在这个文件中
-
将ucos改成1024个任务需要改写哪些文件
-
什么是BSP和CSP
- BSP:板级支持包
- CSP:芯片支持包
-
KMP中的next[i]是干什么用的
- 当某一个字符与主串不匹配时,我们应该知道j指针要移动到哪
-
最前面的k个字符和j之前的最后k个字符是一样的。
-
如果用数学公式来表示是这样的
P[0 ~ k-1] == P[j-k ~ j-1]
一个基本事实是,当空格与D不匹配时,你其实知道前面六个字符是"ABCDAB"。KMP算法的想法是,设法利用这个已知信息,不要把"搜索位置"移回已经比较过的位置,继续把它向后移,这样就提高了效率。
KMP代码实现
void makeNext(const char P[],int next[])
{
int q,k;//q:模版字符串下标;k:最大前后缀长度
int m = strlen(P);//模版字符串长度
next[0] = 0;//模版字符串的第一个字符的最大前后缀长度为0
for (q = 1,k = 0; q < m; ++q)//for循环,从第二个字符开始,依次计算每一个字符对应的next值
{
while(k > 0 && P[q] != P[k])//递归的求出P[0]···P[q]的最大的相同的前后缀长度k
k = next[k-1]; //不理解没关系看下面的分析,这个while循环是整段代码的精髓所在,确实不好理解
if (P[q] == P[k])//如果相等,那么最大相同前后缀长度加1
{
k++;
}
next[q] = k;
}
}
- BM算法
BM字符主要是从右边开始比较,也就是从后面开始,匹配的叫好后缀,遇到不匹配的叫坏字符
-
坏字符:
我们现在将匹配过程中,主串失配位置的字符称之为"坏字符"。根据这个坏字符,我们可以"总结出一些教训",进而改进以上的算法
-
坏字符的几种情况:
-
坏字符不存在我的模式串中
这种情况就直接移动到坏字符后面一格就可以了
-
坏字符存在我的模式串中
坏字符存在于还未比对的字符中,那么久把存在的那个坏字符串和模式串中的坏字符对上
-
每一次不匹配导致的移动都应该是移动到坏字符(如果存在)在模式床中最右端的一个位置
-
具体怎么移动到坏字符那里呢?
- 首先构造一个hash表,从左到右bc[pattern[i]] = i赋值,因为字符就是256的ACKII码,由于是从左往右遍历,所以在后面的相同字符会覆盖掉前面已经赋值的字符.
- 这是只有符合坏字符条件的情况下的代码,可以看出从右到左 应该是不要包含好后缀的而且实际上所谓的hash表不就是坏字符在模式串中的最后面的位数吗.
int bmMatch(const string & text, const string & pat)
{
int *bc = getBc(pat);
//patAt指向了当前pat和text对齐的位置
int patAt = 0;
//cmp指向了当前比较的位置
int cmp;
const size_t PATLASTID = pat.length() - 1;
const size_t patLen = pat.length();
const size_t textLen = text.length();
while (patAt + patLen <= textLen)
{
//如果匹配成功,cmp就会来到-1的位置上
//patAt + cmp 指向了text上当前比较的字符
for (cmp = PATLASTID; cmp >= 0 && pat[cmp] == text[patAt + cmp]; --cmp);
if (cmp == -1)//找到匹配的信息了 直接退出
break;
else
{
int span = cmp - bc[text[patAt + cmp]];
patAt += (span > 0)? span : 1;
}
}
delete[] bc;
return (patAt + patLen <= textLen)? patAt : -1;
}
-
好后缀:
好后缀就是从右到左匹配的时候,匹配成功的字符,这个字符很有可能也存在于前面未匹配的字符中,如果存在那么就可以直接移动到那边。
-
好后缀的几种移动情况:
- 首先是前缀中不存在
- 其次是前缀中存在一整个好后缀
- 最后是前缀中有好后缀的后缀
- 建立gs方便移动
-
int * getGs(const string & pat)
{
const int len = pat.length();
const int lastIndex = len - 1;
int *suffix = suffixes(pat);
int *gs = new int[len];
//找不到对应的子串和前缀
for (int i = 0; i < len; ++i)
gs[i] = len;
//找前缀
for (int i = lastIndex; i >= 0; --i)
{
//存在我们想要的前缀
if (suffix[i] == i + 1)
{
for (int j = 0; j < lastIndex - i; ++j)
{
if (gs[j] == len)
gs[j] = lastIndex - i;
}
}
}
//找中间的匹配子串
for (int i = 0; i < lastIndex; ++i)
{
gs[lastIndex - suffix[i]] = lastIndex - i;
}
delete[] suffix;
return gs;
}
-
ODR和IDR
ODR是配置GPIOx的输出状态寄存器,IDR是配置GPIOx的输入状态寄存器。
-
BOOT0和BOOT1
BOOT0是不能拿来用的,是复位用的,而BOOT1在启动后就变成了普通io口了可以使用。
-
C语言指针和字符串
- 首先数组对应指针,二维数组就对应双重指针吗?错。二维数组就是二维数组还是一维指针。
-
字符串的一些用法:
- Strcat:字符串连接,并不返回连接的字符串而是直接把前面一个串变成前+后。strcat_s(PaddC, 1024, P);
- Strcpy:字符串赋值strcpy_s(P, PaddC);
- Memset:用于字符串清空。memset(str, 0, sizeof(str));
-
DRAM和SRAM
内存条是dram,而SRAM因为缺点是同容量相比DRAM需要非常多的晶体管,发热量也非常大。因此SRAM难以成为大容量的主存储器,通常只用在CPU、GPU中作为缓存,容量也只有几十K至几十M,故采用DRAM。
Cache存储器:电脑中为高速缓冲存储器,是位于CPU和主存储器DRAM(DynamicRandomAccessMemory)之间,规模较小,但速度很高的存储器,通常由SRAM(StaticRandomAccessMemory静态存储器)组成。它是位于CPU与内存间的一种容量较小但速度很高的存储器。CPU的速度远高于内存,当CPU直接从内存中存取数据时要等待一定时间周期,而Cache则可以保存CPU刚用过或循环使用的一部分数据,如果CPU需要再次使用该部分数据时可从Cache中直接调用,这样就避免了重复存取数据,减少了CPU的等待时间,因而提高了系统的效率。Cache又分为L1Cache(一级缓存)和L2Cache(二级缓存),L1Cache主要是集成在CPU内部,而L2Cache集成在主板上或是CPU上。
- 二维数组\双重指针\数组指针\指针数组的关系
首先,二维数组和双重指针表示的是不一样的意思,但是在使用二维数组的形式上都可以表示,但是由于存储空间的形式不同,导致不能相互赋值.
左图为二维数组(数组指针char *c[10];)的形式,右图为双重指针(指针数组char (*c)[10];)的形式.
指针是不能指向指针的,只有定义的二级指针才能指向指针.这应该并不是因为指针的原因,应该是语法的关系,否则怎么都是地址,指针不能指向指针的地址.
指针是能指向结构体的
二维数组的定义必须要固定的空间,而二重指针的话就可以动态分配空间给他。这样方便了不定长度的使用,也能更好地利用空间而不用像二维数组那样浪费。
Void指针可以转换为任何类型, 感觉应该是一个开辟最大空间的指针所以能够强制转换成任何指针.
如果使用*p来给指针赋值,会改变指针指向的那个内容的值,而p = &I;这样带来的是指针指向内容的改变.
-
Short、long、int
百度上说short一般16位,long一般32位,这个和编译器有关,vc就说short最大16,int和long最大32.keil5中 int应该和这个mcu的最大位数一样,short固定16,long感觉应该可以达到64的在64位芯片中。这个方面还没有非常详细地了解。
- 看门狗
在程序中一直定时喂狗,然后如果程序跑飞了,就表示一段时间没有喂狗了,看门狗定时器就发送一个信号到reset中重启程序
- 人工智能
基本概念:训练集、测试集、特征值、监督学习、非监督学习、半监督学习、分类、回归
概念学习:人类学习概念