c语言笔记

snprintf

	// char *p = "hello";
	// char *p2 = "world";
	char buf[15];
	int n = snprintf(buf, sizeof(buf), "%s%s%d", "hello", "world", 123);
	printf("n = %d, buf = %s\n", n, buf);
	
	/* n = 13, buf = helloworld123 */

typedef

C99标准中定义了这些数据类型,具体定义在stdint.h

【C语言】uint8_t、uint16_t、uint32_t、uint64_t是什么?_雪 风的博客-优快云博客

/* exact-width signed integer types */
typedef   signed          char int8_t;
typedef   signed short     int int16_t;
typedef   signed           int int32_t;
typedef   signed       __INT64 int64_t;

/* exact-width unsigned integer types */
typedef unsigned          char uint8_t;
typedef unsigned short     int uint16_t;
typedef unsigned           int uint32_t;
typedef unsigned       __INT64 uint64_t;

 串口中断接收完成判断的方法

  1. 在接收数据时启动一个定时器,在指定时间间隔内没有接收到新数据,认为数据接收完成。
  2. 在数据中加入帧头、帧尾,通过在程序中判断是否接收到帧尾来确定数据接收完毕。
  3. 使用空闲帧中断,用户可以通过获取该中断标志位来判断数据是否接收完成。

串口接收数据完成的几种判断方式-电子工程专辑点击下方【一起学嵌入式】关注,一起学习,一起成长说起通信,首先想到的肯定是串口,日常中232和485的使用比比皆是,数据的发送、接收是串口通信最基础的内容。这篇文章主要讨论串口接收数据的断帧操作。空闲icon-default.png?t=O83Ahttps://www.eet-china.com/mp/a212231.html

字符数组和字符串有什么区别?

在c语言中,并不存在字符串这个数据类型,而是使用字符数组来保存字符串,但是字符数组和字符串是完全不相同的两个概念。

#include<stdio.h>
#include<string.h>

int main()
{
    //字符数组
    char arr[] = {'H','E','L','L','O'};
    //字符串
    char str[] = "HELLO";

    printf("sizeof(arr) = %ld\n", sizeof(arr));
    printf("sizeof(str) = %ld\n", sizeof(str));

    printf("strlen(arr) = %ld\n", strlen(arr));
    printf("strlen(str) = %ld\n", strlen(str));

    printf("arr = %s\n", arr);
    printf("str = %s\n", str);
}

/*
sizeof(arr) = 5
sizeof(str) = 6
strlen(arr) = 10
strlen(str) = 5
arr = HELLOHELLO
str = HELLO
*/

字符数组:H E L L O 未知内容。对于字符数组则不会自动增加任何的内容。

字符串:H E L L O \0。对于字符串,c语言在编译的时候会自动在末尾加一个null字符,也就是'\0',用16进制表示就是0x00。

计算字符数组的长度,sizeof(arr) = 5。

计算字符串的长度,sizeof(str) = 6,之所以为6,是因为str是一个字符串,编译的时候在后面增加一个null字符。

为什么strlen(arr) = 10?实际上strlen会一直计算字符数组的长度,直到遇到'\0',在定义arr的时候,在内容里面开辟5个char大小的空间给arr使用,但这内容之后,到底是不是'\0',我们不得而知,所以strlen(arr)的值有可能大于5。

字符串str末尾肯定有'\0',所以strlen(str) = 5。

使用printf打印arr的时候,有时候会发现在O之后会出现意想不到的字符,而打印str完全正常。

数组指针

#include <stdio.h>

int main()
{
	int arr[3][4] = {{11,22,33,44},{12,13,15,16},{22,66,77,88}};//arr++
	int i,j;

	//能不能定义一个指针,让指针偏移的时候,也偏移对应大小的数组?
	//数组指针,定义一个指针,指向一个数组!
	//数组指针才是真正等同于二维数组名
	int (*p2)[4];
	p2 = arr;
//	printf("p2=%p\n",p2);
//	printf("++p2=%p\n",++p2);
	for(i=0;i<3;i++){
		for(j=0;j<4;j++){
			//printf("%d\n",*(*(arr+i)+j));
			printf("%d\n",*(*(p2+i)+j));			
		}
		
	}
}

/*
11  22  33  44  12  13  15  16  22  66  77  88
*/

#if 0
//以前写法,这样写有点失去二维数组的味道
#include <stdio.h>

//arr,arr[0]
int main()
{
	int arr[3][4] = {{11,22,33,44},{12,13,15,16},{22,66,77,88}};//arr++
	int i,j;
	int *p;//p++
	p = &arr[0][0];
	// p = arr; //warning: assignment from incompatible pointer type  来自不兼容指针类型的赋值	                                
				//assignment to 'int *' from incompatible pointer type 'int (*)[4]'  从不兼容的指针类型“int(*)[4]”分配到“int*”
	for(i=0;i<12;i++){
			printf("%d\n",*p++);//导致p++会有风险					
	}
}
#endif

 数组指针和二维数组的应用

#include <stdio.h>

int getTheData(int (*p)[4],int hang,int lie)
{
	int data;
	data = *(*(p+hang)+lie);
	return data;
	//return p[hang][lie];
}
void tipsInputHangLie(int *pm, int *pn)
{
	printf("输入行列值:\n");
	scanf("%d%d",pm,pn);
	puts("done!");
}
//arr,arr[0]
int main()
{
	int arr[3][4] = {{11,22,33,44},{12,13,15,16},{22,66,77,88}};//arr++
	int ihang,ilie;
	int data;
	
	//1. 提示用户输入行列值
	tipsInputHangLie(&ihang,&ilie);
	//2. 找出对应行列值的那个数
	data = getTheData(arr,ihang,ilie);
	//3. 打印出来
	printf("%d行%d列的值是%d\n",ihang,ilie,data);
}

/*
输入行列值:
0
0
done!
0行0列的值是11
*/

函数指针

#include <stdio.h>

void printWelcome()
{
	puts("程序启动,欢迎使用\n");
}
int main()
{
	int a = 10;
	int *pa;
	pa = &a;
	printf("%d\n",*pa);
	
	void (*p)();//定义一个函数指针变量
	p = printWelcome;//指向函数
	//printWelcome();
	//(*p)();//调用1  //直接通过指针名字 + ()
	(p)();//调用2  //取函数内容(*指针名字)()
	return 0;
}

/*
10
程序启动,欢迎使用
*/

 函数指针2

#include <stdio.h>

int inCData(int data)
{
	return ++data;
}
void printWelcome()
{
	puts("程序启动,欢迎使用\n");
}
int main()
{
	void (*p)();//定义一个函数指针变量
	int (*p2)(int data);
	
	p = printWelcome;//指向函数
	p2 = inCData;

	(*p)();//调用
	printf("p2测试:%d\n",(*p2)(10));
	return 0;
}

/*
程序启动,欢迎使用

p2测试:11
*/

 函数指针3

#include <stdio.h>
#include <stdlib.h>

int getMax(int data1, int data2)
{
	return data1>data2 ? data1:data2;
}

int getMin(int data1, int data2)
{
	return data1<data2 ? data1:data2;
}

int getSum(int data1, int data2)
{
	return data1+data2;
}
/*
int dataHandler(int data1, int data2, int (*pfunc)(int, int )) //lyf强调形参是什么类型的数据,参数名只是一个访问方式
{
	int ret;
	ret = (*pfunc)(data1,data2);
	return ret;
}
*/

int main()
{	
	int a = 10;
	int b = 20;
	int cmd;
	int ret;
	
	int (*pfunc)(int , int ); //lyf强调形参是什么类型的数据,参数名只是一个访问方式
	
	printf("请输入1(取大值),2(取小值),或者3(求和)\n");
	scanf("%d",&cmd);
	switch(cmd){
		case 1:
			pfunc = getMax;
		break;
		case 2:
			pfunc = getMin;
		break;
		case 3:
			pfunc = getSum;
		break;
		default:
			printf("输入错误!@输入1(取大值),2(取小值),或者3(求和)\n");
		//	exit(-1);
		break;
	}
/*
	ret = dataHandler(a,b,pfunc);//50行不加exit(-1),用gdb调试有段错误,因为switch输入错误的话,pfunc是个野指针 lyf
	printf("ret = %d\n",ret);
*/
	printf("ret = %d\n",pfunc(a,b));//lyf等价于19-24  51-52
	return 0;
}

/*
请输入1(取大值),2(取小值),或者3(求和)
1
ret = 20
rlk@rlk:Downloads$ ./a.out 
请输入1(取大值),2(取小值),或者3(求和)
2
ret = 10
rlk@rlk:Downloads$ ./a.out 
请输入1(取大值),2(取小值),或者3(求和)
3
ret = 30
*/

指针数组

一个数组,若其元素均为指针类型数据,称为指针数组,也就是说,指针数组中的每一个元素都存放一个地址,相当于一个指针变量。

#include <stdio.h>

int main()
{
	int a = 10;
	int b = 20;
	int c = 30;
	int d = 40;
	
	int* p[4] = {&a,&b,&c,&d};
	int i;
	for(i = 0;i<4;i++){
		printf("%d ",*(p[i]));//*p[i] 中括号的优先级高于*,但是在工作中还是希望加上()  lyf
	}
	return 0;
}

/*
10 20 30 40
*/

函数指针数组

#include <stdio.h>
#include <stdlib.h>

int getMax(int data1, int data2)
{
	return data1>data2 ? data1:data2;
}

int getMin(int data1, int data2)
{
	return data1<data2 ? data1:data2;
}

int getSum(int data1, int data2)
{
	return data1+data2;
}
int main()
{	
	int a = 10;
	int b = 20;
	int ret;
	
	int (*pfunc[3])(int , int )={
		getMin,
		getMax,
		getSum};//函数指针数组!
	int i;
	for(i=0;i<3;i++){
		ret = (*pfunc[i])(a,b);
		printf("ret = %d\n",ret);
	}
	
	return 0;
}

/*
ret = 10
ret = 20
ret = 30
*/

 指针函数

 返回指针的函数

#include <stdio.h>

int* getPosPerson(int pos, int (*pstu)[4])//指针函数,返回指针的函数
						   //数组指针  指向一个含有4个元素的数组
{                               
	int *p;
	p = (int *)(pstu+pos);//lyf  pstu(父数组)+pos偏移整个数组,相当于arr+1,就是第二行的全部元素
	return p;   //类型不一致,因为pstu++偏移了16个字节,p++偏移了4个字节,但是没关系,我们拿回来了p的地址
}  //int *p; char *c; 指针操作关心两个问题:1.起始位置 2.偏移值,这段代码只关心起始位置,不关心偏移值
   //消除警告,就是在前面加(int *)类型强转
   
int main()
{
	int scores[3][4]={
		{55,66,77,88},
		{66,55,99,100},
		{11,22,33,59},
	};
	int *ppos;
	int pos;
	printf("请输入你需要看的学生号数:0,1,2\n");
	scanf("%d",&pos);
	
	ppos = getPosPerson(pos, scores);
	int i;
	for(i=0;i<4;i++){
		printf("%d ",*ppos++);
	}
	return 0;
}

/*
J:\lyf\03.other\2023.7.3\code-new\第六章指针>a.exe
请输入你需要看的学生号数:0,1,2
0
55 66 77 88
J:\lyf\03.other\2023.7.3\code-new\第六章指针>a.exe
请输入你需要看的学生号数:0,1,2
1
66 55 99 100
J:\lyf\03.other\2023.7.3\code-new\第六章指针>a.exe
请输入你需要看的学生号数:0,1,2
2
11 22 33 59
*/

二级指针

#include <stdio.h>

int main()
{
	int data = 100;
	int *p = &data;
	printf("data的地址是:%p\n",&data);
	printf("p保存data的地址:%p,内容是%d\n",p,*p);
	
	/* 可以用一级指针变量存放指针变量的地址,但是使用有缺陷,无法获得最终地址内容 */
	// printf("p的地址是:%p\n",&p);
	// int *pp = &p;
	// printf("pp保存p的地址:%p\n",pp);
	// printf("*pp是%p\n",*pp);
	
	int **p2;
	p2 = &p;
	printf("p2保存p的地址:%p\n",p2);
	printf("*p2是%p\n",*p2);
	printf("**p2来访问data:%d\n",**p2);
	
	int ***p3;
	p3 = &p2;
	printf("p3保存p2的地址:%p\n",p3);
	printf("*p3是%p2\n",*p3);
	printf("***p3来访问data:%d\n",***p3);
	return 0;
}

/*
data的地址是:000000000061FE0C
p保存data的地址:000000000061FE0C,内容是100
p的地址是:000000000061FE00
pp保存p的地址:000000000061FE00
*pp是000000000061FE0C
p2保存p的地址:000000000061FE00
*p2是000000000061FE0C
**p2来访问data:100
p3保存p2的地址:000000000061FDF8
*p3是000000000061FE002
***p3来访问data:100
*/

 二级指针的应用

//这个是二级指针的用法
#include <stdio.h>

// void getPosPerson(int pos, int (*pstu)[4], int *ppos)//函数指针,返回指针的函数  xx
void getPosPerson(int pos, int (*pstu)[4], int **ppos)//函数指针,返回指针的函数  xx
{
	*ppos = (int *)(pstu+pos);
}

int main()
{
	int scores[3][4]={
		{55,66,77,88},
		{66,55,99,100},
		{11,22,33,59},
	};
	int *ppos;
	int pos;
	printf("请输入你需要看的学生号数:0,1,2\n");
	scanf("%d",&pos);
	
	//getPosPerson(pos, scores, ppos); //如果第4行、第22行对应使用的话,第17行ppos是野指针,没有初始化,第25行继续打印的话,会出现段错误,正确的处理是23行和第5行配合使用
	getPosPerson(pos, scores, &ppos);
	for(int i=0;i<4;i++){
		printf("%d ",*ppos++);
	}
	return 0;
}

/*
J:\lyf\03.other\2023.7.3\code-new\第六章指针>a.exe
请输入你需要看的学生号数:0,1,2
0
55 66 77 88
J:\lyf\03.other\2023.7.3\code-new\第六章指针>a.exe
请输入你需要看的学生号数:0,1,2
1
66 55 99 100
J:\lyf\03.other\2023.7.3\code-new\第六章指针>a.exe
请输入你需要看的学生号数:0,1,2
2
11 22 33 59
*/

/*
#include <stdio.h>

void getPosPerson(int pos, int (*pstu)[4],int *ppos)//函数指针,返回指针的函数  xx
{
	ppos = (int *)(pstu+pos);
	int i;
	for(i=0;i<4;i++){
		printf("%d ",*ppos++);
	}
}

int main()
{
	int scores[3][4]={
		{55,66,77,88},
		{66,55,99,100},
		{11,22,33,59},
	};
	int *ppos;
	int pos;
	printf("请输入你需要看的学生号数:0,1,2\n");
	scanf("%d",&pos);
	
	getPosPerson(pos, scores,ppos);
	int i;
	for(i=0;i<4;i++){
		printf("%d ",*ppos++);
	}
	return 0;
}*/

 二级指针的认识

//是二级指针的认识,一般不会这么用
#include <stdio.h>


int main()
{
	int scores[3][4]={
		{55,66,77,88},
		{66,55,99,100},
		{11,22,33,59},
	};//int (*p)[4];
/*		
	int **p; 
	p = scores;  //会出问题!
	printf("scores:%p\n",scores);
	printf("p=%p\n",p);
	printf("*p=%p\n",*p);//*p是一个野指针,不是我们认为的会变成列地址
	printf("*score=%p\n",*scores);
	
	**p = 100; //对野指针发起访问,就会发生段错误,第22行代码不会执行
	printf("done\n");
	
	// scores:000000000061FDE0
	// p=000000000061FDE0
	// *p=0000004200000037
	// *score=000000000061FDE0
	//第20行发生了段错误,运行起来光标在转圈圈,退出
*/

	int (*p2)[4] = scores;
	int **p3 = &p2;//能用
	**p3 = 100;//能改的动,但是一般不这么进行
	printf("%d\n",scores[0][0]);
	
	//100
	
	return 0;
}

各种指针的定义

1. 一个整形数:int a;
2. 一个指向整形数的指针:int *a;
3. 一个指向指针的指针,它指向的指针指向一个整型数:int **a;
4. 一个有10个整型数的数组:int a[10];
5. 一个有10个指针的数组,每个指针指向一个整型数:int *a[10];
6. 一个指向有10个整型数的数组的指针:int (*a)[10];
7. 一个指向指针的指针,被指向的指针指向一个有10个整型数的数组:int (**a)[10];
8. 一个指向数组的指针,该数组有10个整形指针:int *(*a)[10];
9. 一个指向函数的指针,该函数有一个整形参数并返回一个整形数:int (*a)[10];
10. 一个有10个指针的数组,每个指针指向一个函数,该函数有一个整形参数并放回一个整型数:int (*a[10])(int);
11. 一个函数的指针,指向的函数类型是有两个整形参数并且返回一个函数指针的函数,返回的函数指针指向有一个整形参数且返回整形数的函数:int(*(*a)(int, int))(int);

栈区

不要返回局部变量的地址,因为局部变量在函数执行之后就释放了,我们没有权限取操作释放后的内存

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

//栈 注意事项 ,不要返回局部变量的地址
int * func()
{
	int a = 10;
	return &a;
}

void test01()
{
	int * p = func();

	//结果已经不重要了,因为a的内存已经被释放了,我们没有权限去操作这块内存
	printf("a = %d\n", *p);
	printf("a = %d\n", *p);
}


char * getString()
{
	char str[] = "hello world";
	return str;
}

void test02()
{
	char * p = NULL;
	p = getString();
	printf("%s\n", p);
}

int main(){

	//test01();
	test02();
	system("pause");
	return EXIT_SUCCESS;
}

这样写是正确的

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

int * func()
{
	int b = 10;
	int *a = &b;
	return a;
}

void test01()
{
	int * p = func();

	printf("a = %d\n", *p);
	printf("a = %d\n", *p);
}

/*
a = 10
a = 10
*/

void getString(char *p)
{
	strcpy(p, "hello");
}

void test02()
{
	//分配到栈上
	char buf[128] = {0};
	getString(buf);
	
	printf("%s\n", buf);
}

int main(){

	test01();
	// test02();
	// system("pause");
	return 0;
}

堆区

在堆区开辟的数据,记得手动开辟,手动释放

注意事项:如果在主调函数中没有给指针分配内存,那么被调函数中需要利用高级指针给主调函数中指针分配内存

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

int * getSpace()
{
	int * p = malloc(sizeof(int)* 5);

	if (p == NULL)
	{
		return NULL;
	}

	for (int i = 0; i < 5;i++)
	{
		p[i] = i + 100;
	}
	return p;

}

void test01()
{
	int * p = getSpace();

	for (int i = 0; i < 5;i++)
	{
		printf("%d  ", p[i]);
	}

	//手动在堆区创建的数据,记得手动释放
	free(p);
	p = NULL;

}

/*
100  101  102  103  104
*/

//注意事项
//如果主调函数中没有给指针分配内存,被调函数用同级指针是修饰不到主调函数中的指针的
void allocateSpace( char * pp )
{
	char * temp =  malloc(100);
	if (temp == NULL)
	{
		return;
	}
	memset(temp, 0, 100);
	strcpy(temp, "hello world");
	pp = temp;

}

void test02()
{
	char * p = NULL;
	allocateSpace(p);

	printf("%s\n", p);
}

/*
Segmentation fault (core dumped)
*/

void allocateSpace2(char ** pp)
{
	char * temp = malloc(100);
	memset(temp, 0, 100);
	strcpy(temp, "hello world");
	*pp = temp;
}

void test03()
{
	char * p = NULL;

	allocateSpace2(&p);

	printf("%s\n", p);

	free(p);
	p = NULL;

}

/*
hello world
*/

int main(){
	//test01();
	//test02();
	test03();
	system("pause");
	return EXIT_SUCCESS;
}

栈的生长方向及内存存储方式

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

//1、栈的生长方向
void test01()
{
	int a = 10; //栈底   --- 高地址 
	int b = 20;
	int c = 30;
	int d = 40; //栈顶   --- 低地址

	printf("a的地址:%p\n", &a);
	printf("b的地址:%p\n", &b);
	printf("c的地址:%p\n", &c);
	printf("d的地址:%p\n", &d);
}

/*
a的地址:0x7ffe8e682438
b的地址:0x7ffe8e68243c
c的地址:0x7ffe8e682440
d的地址:0x7ffe8e682444
*/

//2、内存存储方式
void test02()
{
	int a = 0xaabbccdd;
	unsigned char * p = &a;

	printf("0x%x\n", *p);     // 低位字节数据  ---- 低地址
	printf("0x%x\n", *(p+1)); // 高位字节数据  ----  高地址
	printf("0x%x\n", *(p + 2));
	printf("0x%x\n", *(p + 3));
}

/*
0xdd
0xcc
0xbb
0xaa
*/

int main(){
	// test01();
	test02();
	// system("pause");
	return EXIT_SUCCESS;
}

指针做函数参数的输入输出特性

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

//1、输入特性: 主调函数中分配内存,被调函数中使用内存
void func(char * p)
{
	strcpy(p, "hello world");
}

void test01()
{
	//分配到栈上
	char buf[1024] = { 0 };
	func(buf);

	printf("%s\n", buf);
}

void printString(char * str)
{
	printf("%s\n", str);
}

void test02()
{
	//分配到堆区
	char * p = malloc(sizeof(char)* 64);
	memset(p, 0, 64);
	strcpy(p, "helloworld");
	printString(p);
	free(p);
	p = NULL;
}


//2、输出特性: 被调函数分配内存,主调函数使用内存

void allocateSpace(char ** pp)
{
	char * temp = malloc(sizeof(char)* 64);
	memset(temp, 0, 64);
	strcpy(temp, "hello world");
	*pp = temp;
}

void test03()
{
	char * p = NULL;
	allocateSpace(&p);

	printf("%s\n", p);
	//手动在堆区创建的数据,记得手动释放
	free(p);
	p = NULL;
}


int main(){
	//test01();
	//test02();
	test03();

	system("pause");
	return EXIT_SUCCESS;
}

总结

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

void func(char *p)
{
	strcpy(p, "hello");
}
 
void test01()
{
	//分配到栈区
	char buf[128] = {0};
	func(buf);
	
	printf("%s\n", buf);
}
/*hello*/

/*==========================================*/
void printfString(char *str)
{
	printf("%s\n", str);
}

void test02()
{
	//分配到堆区
	char *p = malloc(sizeof(char) * 128);
	memset(p, 0, 128);
	strcpy(p, "hello world");
	printfString(p);
	
	free(p);
	p = NULL;	
} 
/*hello world*/

/*==========================================*/
void function(char **pp)
{
	//分配到堆区
	char *temp = malloc(sizeof(char) * 128);
	memset(temp, 0, 128);
	strcpy(temp, "123456");
	*pp = temp;	
}

void test03()
{
	char *p = NULL;
	
	function(&p);
	printf("%s\n", p);
	
	free(p);
	p = NULL;
}
/*123456*/

/*==========================================*/
char *function1()
{
	//分配到堆区
	char *p = malloc(sizeof(char) * 128);
	memset(p, 0, 128);
	strcpy(p, "nihao");
	
	return p;		
}

void test04()
{
	char  *p = function1();
	
	printf("%s\n", p);	
    
    free(p);
	p = NULL;
} 
/*nihao*/
 
int main(){
 
	// test01();
	// test02();
	// test03();
	test04();
	// system("pause");
	return EXIT_SUCCESS;
}



1. keil的安装

FreeRTOS完全开发手册之上册_快速入门.pdf

2. STM32·FreeRTOS·新建工程模板

【1】STM32·FreeRTOS·新建工程模板【一步到位】_freertos keil工程模板-优快云博客文章浏览阅读977次,点赞35次,收藏10次。名称描述FreeRTOSFreeRTOS 内核FreeRTOS 组件tools工具FreeRTOS 的 GitHub 仓库链接快速入门指南官方文档链接升级到指定 FreeRTOS 版本官方文档链接FreeRTOS 历史更新记录其他其他。_freertos keil工程模板https://blog.youkuaiyun.com/Cute758/article/details/138443982icon-default.png?t=O83Ahttps://blog.youkuaiyun.com/Cute758/article/details/138443982

3. freeRTOS源码结构与编程规范

FreeRTOS完全开发手册之上册_快速入门.pdf

4.使用STM32CubeMX创建RTOS工程模板

E:\git\rtos_doc_source\RTOS培训资料\02_项目2_基于FreeRTOS实现智能家居\02_CubeMX对FreeRTOS的适配\2_学习文档\00_FreeRTOS工程模板建立.md

该工程是在“5_4_Serialport_RingBuffer_GPIO_IRQ”工程基础上创建一个带有RTOS的工程模板,使用的工具是STM32CubeMX

5.02_STM32CubeMX生成Makefile工程及其编译环境搭建

E:\git\rtos_doc_source\RTOS培训资料\03_项目3_深入理解ARM架构实现多任务系统\2_学习文档\02_STM32CubeMX生成Makefile工程及其编译环境搭建.md

1.目的;2.环境:2.1.windows下安装arm-gcc、2.2.windows下安装马克命令环境;3.STM32CubeMX生成GCC工程;4.使用Git编译;5.使用STM32CubeProgrammer 烧录;6.修改Makefile添加代码。

6.GPIO寄存器操作

E:\git\doc_and_source_for_mcu_mpu\STM32MF103\doc_pic\03_第1个程序(点灯)\02_GPIO引脚操作方法概述.docx

GPIO寄存器的2种操作方法:
原则:不能影响到其他位,直接读写:读出、修改对应位、写入
要设置bit n:
val = data_reg;
val = val | (1<<n);
data_reg = val;
要清除bit n:
val = data_reg;
val = val & ~(1<<n);
data_reg = val;

5.软件定时器消除抖动

E:\git\rtos_doc_source\RTOS培训资料\02_项目2_基于FreeRTOS实现智能家居\01_FreeRTOS快速入门\1_项目源码\02_视频配套的源码

26_freertos_example_readkey

6.单片机主频和定时器的关系

在传统51单片机中,‌一个机器周期=12个时钟周期,‌这意味着单片机的时钟频率越高,‌其机器周期就越短,‌从而使得定时器的计时精度提高。‌例如,‌如果一个单片机使用12MHz的晶振,‌那么一个机器周期就是1微秒(‌us)‌,‌这意味着定时器可以以微秒为单位进行精确计时。‌

详解:单片机中各种周期的关系与定时器原理_工作_时钟_指令这个是分频器的工作原理,我们可以看,分频器设定之前分频系数为1[1],后面的[2][3][4]分频系数为2,分频系数改变后,计数周期也跟着改变了;同时预分频设置生效时,他还会产生一个中断信号,这个…icon-default.png?t=O83Ahttps://www.sohu.com/a/519362542_121118995

时钟周期:也叫振荡周期或晶振周期,即晶振的单位时间发出的脉冲数,一般有外部的振晶产生,比如12MHZ=12×10的6次方,即每秒发出12000000个脉冲信号,那么发出一个脉冲的时间就是时钟周期,也就是1/12微秒。通常也叫做系统时钟周期。是计算机中最基本的、最小的时间单位。

机器周期:在计算机中,为了便于管理,常把一条指令的执行过程划分为若干个阶段,每一阶段完成一项工作。例如,取指令、存储器读、存储器写等,这每一项工作称为一个基本操作。完成一个基本操作所需要的时间,称为机器周期。

指令周期:指令周期是执行一条指令所需要的时间,一般由若干个机器周期组成。指令不同,所需的机器周期数也不同。对于一些简单的的单字节指令,在取指令周期中,指令取出到指令寄存器后,立即译码执行,不再需要其它的机器周期。对于一些比较复杂的指令,例如转移指令、乘法指令,则需要两个或者两个以上的机器周期。

系统时钟:系统时钟就是CPU指令运行的频率,这个才是CPU真正的频率。

bin文件扩大为0x2000,

bin文件不保存初始值为0,或者没有初始值的变量。

资源下载链接为: https://pan.quark.cn/s/9a27693985af 《基于SSM的JSP招聘网》是一款功能丰富的在线招聘平台,主要面向普通游客、求职者、企业和管理员四种用户角色,提供了多样化的服务与管理功能。该系统采用SSM(Spring、SpringMVC、MyBatis)技术栈开发,确保了系统的稳定性与高效性。以下是对系统功能模块及其技术实现的介绍。 对于普通游客,系统提供职位浏览功能。游客可以查看平台上的各种招聘信息,如职位描述、工作职责、薪资待遇等。这需要后台数据库对招聘信息进行有效存储和检索。在SSM框架中,SpringMVC负责处理HTTP请求,将数据传递给Spring服务层进行业务逻辑处理,MyBatis作为持久层工具,执行SQL查询并将结果映射为Java对象。 求职者注册成为平台用户后,可进行职位收藏和投递。收藏的职位信息会保存在个人中心,方便随时查看。职位投递功能涉及用户个人信息与简历的提交,需要系统具备用户认证和授权机制,可通过Spring Security或Apache Shiro实现。此外,系统可能采用AJAX技术进行异步操作,如即时刷新收藏夹状态,以提升用户体验。 企业用户可在系统中发布职位、查看求职者简历。发布职位时,需进行表单验证和数据合法性检查,SpringMVC的控制器可协同前端校验库(如Hibernate Validator)完成。查看简历时,企业可对求职者进行筛选和评价,这要求数据库设计合理,以便快速查询和分析求职者信息。 管理员负责管理平台运行,包括用户管理、职位审核、系统设置等。管理员模块通常包含后台管理界面,通过SpringMVC的模型视图解析器和模板引擎(如Thymeleaf或FreeMarker)生成动态页面。同时,日志记录和异常处理必不可少,Spring框架提供了强大的日志和AOP支持,可方便实现这些功
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值