关于嵌入式学习随笔->3《C语言基础》

1、位操作

    位操作主要用于在进行寄存器操作的时候,为了使实现某一功能而又不改变原来的功能所需要用到的一种按位运算的方法。比如我们要改变GPIOA->ODR的状态,一般来说需要两步,第一步,将原来位置的数据清除,第二步在将要存入的数据写入。

运算符 含义 运算符 含义
& 按位与 ~ 取反
| 安慰或 << 左移
^ 按位异或 >> 右移

操作及其示例代码:

1)对某位进行操作

GPIOA->ODR &=0XFF0F;//将第4-7位清0。

GPIOA->ODR |=0X0040;//设置相应位的值,不改变其他位的值。

2)位移操作提高代码的可读性

GPIOA->ODR |= 1<<5;//将ODR寄存器第五位设置位1,其他位置不变。

GPIOA->ODR = 0X0020;//这样也是第五位置1,但是其他位会强制清0,改变了原来的配置。

3)取反操作

GPIOA->ODR &= 0XFFF7;//可读性不高,其实就是第3位清0,其他位置不变。

-》按位移来操作:

GPIOA->ODR |= ~(1<<3);//将第3位清0

如果有几位同时需要置1,可以取成8421码格式,来按整体位移。

注意:C语言中没有同或运算符。

2、define宏定义关键词

    define 时C语言中的预处理命令,它用于宏定义,可以提高源代码的可读性,为编程提供方便。

一般使用方法为:

#define 标识符 字符串(可以是常数、表达式、格式串等)

  1 #define Author Yimiu
  2 #define number 123

在声明之后就可以用标识符来代替原来的字符串或数字。

3.条件编译

一般#ifdef语句要与#else、#end连用:即定义了情况一就编译程序段1,否则编译程序段2。

  1 #ifndef condition1
  2 //程序段1
  3 #else
  4 //程序段2
  5 #endif

#ifndef、#define、#endif往往在头文件中使用,防止重复定义。

  1 #ifndef __DELAY_H
  2 #define __DELAY_H
  3 //头文件及其函数声明		   
  4 #endif

另外还有#if、#endif语句:满足condition1则编译程序段1,满足condition2则编译程序段2否则编译程序段3

  1 #if condition1
  2 //程序段1
  3 #elseif condition2
  4 //程序段2
  5 #else
  6 //程序段3
  7 #endif

4、extern变量声明

    在C语言中extern可以放在函数声明或是变量声明之前,它的作用是提示编译器在遇到此变量和函数时在其他文件或是模块中寻找其定义,需要注意的是,对于变量或是函数的extern声明可以有很多次,但是变量或是函数的定义只能有一次。

  1 extern void function(void);
  2 extern u8 Author;

5、typedef类型别名

   除了可以直接使用C提供的标准类型名(如int、char、float、double和long等)和程序员自己声明的机构提、共用体、美剧类型外,还可以用typedef制定新的类型名来代替已有的类型名。有以下两种情况:

1)简单的用一个新的类型名代替原有的类型名

  1 typedef int integer;//指定用integer来做类型名,作用于int相同
  2 typedef float Real;//指定用Real来做类型名,作用于float相同
  3 integer a;//实际上就是int a

2)命名一个简单的类型名代替复杂的表示方法

--》1>命名一个新的类型名代表结构体类型

  1 typedef struct
  2 {
  3   int month;
  4   int day;
  5   int year;
  6 }Data;
  7 //声明了一个新的类型名Date,来代表上边的结构体类型
  8 Date birthday;//定义结构体类型变量birthday
  9 Date *p;          //定义结构体指针变量p,指向此结构体类型数据

--》2>命名一个新的类型名代表数组类型

  1 typedef int Num[100];//声明Num为整型数组类型名
  2 Num a;                         //定义a为整型数组名,它有一百个元素

--》3>命名一个新的类型名代表指针类型

  1 typedef char *string;  //声明string为字符指针类型
  2 string p,s[10];              //声明p字符指针变量,s为字符指针数组

--》4>命名一个行的类型名代表指向函数的指针

  1 typedef int (*pointer)();  //声明pointer为指向函数的指针类型,该函数返回整型值
  2 pointer p1,p2;                //p1、p2为pointer类型指针变量

    简单的说,就是按定义变量的方式,把变量名换上新类型名,并且在最前面加上“typedef”,就声明了新类型名代表原来的类型。

6、结构体

结构体声明:

  1 struct struct_name
  2 {
  3 //成员列表
  4 };
  5 stuct Student
  6 {   int number;
  7     char name;
  8     char sex;
  9     int age;
 10     float score;
 11 };//注意分号
 12 Student Yimiu;//定义了一个Yimiu结构体变量
 13 //可以通过“.”来呼出变量成员
 14 Yimiu.number = 092414216
 15 Yimiu.name    = yimi
 16 //这样就为名为Yimiu的结构体变量成员赋值了

7、static关键字

    除了从变量的作用域(也就是空间上)来说的角度观察,分为全局变量和局部变量外,还可以从两外一个角度,即变量存在时间的长短(即生存期)角度来观察。有的变量在程序运行过程中都是存在的,而又的变量在则是在调用其所在函数时才会零时分配存储单元,当执行完毕后就会被释放,变量就不存在了。也就是说变量存储两种方式:静态存储方式和动态存储方式。静态存储方式是指在程序运行期间由系统分配固定的存储空间,而动态存储方式则是在程序运行期间根据需要进行动态的分配存储空间的方式。

    static关键字为静态局部变量声明,用此关键字声明的变量在函数调用后不会消失而继续保留原值,也就是其占用的存储空间没有被释放,下一次调用该函数时,该变量已经有值(上一次运行结果)。

  1 int func(int a)
  2 {
  3   suto int b=0;
  4   static c=3;
  5   b=b+1;
  6   c=c+1;
  7   return(a+b+c);
  8 }
  9 int main()
 10 {
 11   int a=2,i;
 12   for(i=0;i<3;i++)
 13   {
 14      printf("%d\n\r",func(a));
 15   }
 16 }//注意在STM32系列中主函数虽然为int型,但是不用返回函数值

运行的结果为7、8、9可以自行分析。

8、__weak弱函数

    __weak关键字用来声明的函数叫做弱函数,我们可以这样理解,当应用层没有定义此函数时,那么系统默认编译此弱函数中的内容(系统必须要调用此函数),若我们在其他地方定义了没有用弱函数声明的函数时,此时就会编译我们自己定义的函数。其实当程序运行到某一函数时,系统先找其定义,若果没有再找其弱定义,最后都没有时会报错。

  1 __weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
  2 {
  3   /* Prevent unused argument(s) compilation warning */
  4   UNUSED(huart);
  5 
  6   /* NOTE : This function should not be modified, when the callback is needed,
  7             the HAL_UART_RxCpltCallback can be implemented in the user file
  8    */
  9 }
 10 *********************************************************************************
 11 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
 12 {
 13 #if EN_USART1_RX
 14 	if(huart->Instance==USART1)//如果是串口1
 15 	{
 16 		if((USART_RX_STA&0x8000)==0)//接收未完成
 17 		{
 18 			if(USART_RX_STA&0x4000)//接收到了0x0d
 19 			{
 20 				if(aRxBuffer[0]!=0x0a)USART_RX_STA=0;//接收错误,重新开始
 21 				else USART_RX_STA|=0x8000;	//接收完成了 
 22 			}
 23 			else //还没收到0X0D
 24 			{
 25 				if(aRxBuffer[0]==0x0d)USART_RX_STA|=0x4000;
 26 				else
 27 				{
 28 					USART_RX_BUF[USART_RX_STA&0X3FFF]=aRxBuffer[0] ;
 29 					USART_RX_STA++;
 30 					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
 31 				}
 32 			}
 33 		}
 34 
 35 	}

    当我们不使用UART回调功能时,那么我们就不用写此函数,系统就会执行相应的弱定义函数,在整个工程当中,往往会有大量的弱函数被定义。

猜你喜欢

转载自www.cnblogs.com/vcan123/p/10421349.html