刚开始看STM32的库函数,会有很多疑惑,例如指针怎么用,结构体跟指针怎么配合,例如函数的参数有什么要求,如何实时更新IO口的数据等。如果重新进行C语言的学习,那么要学很久才能够系统地认识。本文则将比较容易想不起来的知识点进行简单的整理。

  1、#ifdef 和 #ifndef

  #ifdef 标识符A// 如果标识符A定义了,就编译程序段1,否则编译程序段2

  程序段1

  #else

  程序段2

  #endif

  #ifndef 的功能则与 #ifdef相反,是没有定义标识符A的时候编译程序段1。

  2、全局define

  在软件的选项中,有如此一栏,在上面填写的变量则表示在所有的文件中,上述的标识均被定义过。

  #ifdef STM32F10X_HD

  大容量芯片需要的一些变量定义

  #end

  3、extern变量申明

  C语言中extern可以置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。这里面要注意,对于extern申明变量可以多次,但定义只有一次。

  extern u16 USART_RX_STA;

  这个语句是申明USART_RX_STA变量在其他文件中已经定义了,在这里要使用到。

  下面通过一个例子说明一下使用方法。

  在Main.c定义的全局变量id,id的初始化都是在Main.c里面进行的。

  Main.c文件

  u8 id; //定义只允许一次

  main() {

  id=1; printf("d%",id); //id=1

  test();

  printf("d%",id);//id=2

  }

  但是我们希望在test.c的 changeId(void)函数中使用变量id,这个时候我们就需要在test.c里面去申明变量id是外部定义的了,因为如果不申明,变量id的作用域是到不了test.c文件中。

  看下面test.c中的代码:

  extern u8 id;//申明变量id是在外部定义的,申明可以在很多个文件中进行

  void test(void){ id=2; }

  在test.c中申明变量id在外部定义,然后在test.c中就可以使用变量id了。

  4、typedef类型别名

  typedef用于为现有类型创建一个新的名字,或称为类型别名,用来简化变量的定义。typedef在MDK用得最多的就是定义结构体的类型别名和枚举类型了。

  struct _GPIO { __IO uint32_t CRL; __IO uint32_t CRH; … };

  定义了一个结构体GPIO,这样我们定义变量的方式为:

  struct _GPIO GPIOA;//定义结构体变量GPIOA

  但是这样很繁琐。这里我们可以为结体定义一个别名GPIO_TypeDef,这样我们就可以在其他地方通过别名GPIO_TypeDef来定义结构体变量了。

  方法如下:

  typedef struct {

  __IO uint32_t CRL; __IO uint32_t CRH; … } GPIO_TypeDef;

  Typedef为结构体定义一个别名GPIO_TypeDef,

  这样我们可以通过GPIO_TypeDef来定义结构体变量: GPIO_TypeDef _GPIOA,_GPIOB;

  这里的GPIO_TypeDef就跟struct _GPIO是等同的作用了。

  5、结构体

  声明结构体类型: Struct 结构体名 { 成员列表; }变量名列表; 例如:

  Struct U_TYPE { Int BaudRate Int WordLength; }usart1,usart2;

  在结构体申明的时候可以定义变量,也可以申明之后定义,方法是:

  Struct 结构体名字 结构体变量列表 ; 例如:struct U_TYPE usart1,usart2;

  结构体成员变量的引用方法是: 结构体变量名字.成员名

  比如要引用usart1的成员BaudRate,方法是:usart1.BaudRate;

  结构体指针变量定义也是一样的,跟其他变量没有啥区别。

  例如:struct U_TYPE *usart3;//定义结构体指针变量usart1;

  结构体指针成员变量引用方法是通过“->”符号实现,

  比如要访问usart3结构体指针指向的结构体的成员变量BaudRate,方法是:

  Usart3->BaudRate;

  在我们单片机程序开发过程中,经常会遇到要初始化一个外设比如串口,它的初始化状态是由几个属性来决定的,比如串口号,波特率,极性,以及模式。对于这种情况,在我们没有学习结构体的时候,我们一般的方法是: void USART_Init(u8 usartx,u32 u32 BaudRate,u8 parity,u8 mode);

  这种方式是有效的同时在一定场合是可取的。但是试想,如果有一天,我们希望往这个函数里面再传入一个参数,那么势必我们需要修改这个函数的定义,重新加入字长这个入口参数。但是如果我们这个函数的入口参数是随着开发不段的增多,那么是不是我们就要不断的修改函数的定义呢?这是不是给我们开发带来很多的麻烦呢?那又怎样解决这种情况呢?

  这样如果我们使用到结构体就能解决这个问题了。我们可以在不改变入口参数的情况下,只需要改变结构体的成员变量,就可以达到上面改变入口参数的目的。

  我们可以将他们通过定义一个结构体来组合在一个。MDK中是这样定义的:

  typedef struct { uint32_t USART_BaudRate;

  uint16_t USART_WordLength;

  uint16_t USART_StopBits;

  uint16_t USART_Parity;

  uint16_t USART_Mode;

  uint16_t USART_HardwareFlowControl; } USART_InitTypeDef;

  于是,我们在初始化串口的时候入口参数就可以是USART_InitTypeDef类型的变量或者指针变量了,MDK中是这样做的: void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct); 这样,任何时候,我们只需要修改结构体成员变量,往结构体中间加入新的成员变量,而不需要修改函数定义就可以达到修改入口参数同样的目的了。

  6、关于函数中结构体的参数传递

  在ST的库函数中,有许多结构体的用法,就像第5点中讲到的一样,用结构体封装有利于函数的传递。

  下面是摘抄的一些解读,具有一定的典型性。

  在ST的结构体参数传递中,有指针式,也有结构体地址式。

  (1)用结构体变量名作为参数。

  #include

  #include

  using namespace std;

  struct Student{

  string name;

  int score;

  };

  int main(){

  Student one;

  void Print(Student one);

  one.name="千手";

  one.score=99;

  Print(one);

  cout<

  cout<

  return 0;

  }

  void Print(Student one){

  cout<

  cout<<++one.score<

  }

  这种方式值采取的“值传递”的方式,将结构体变量所占的内存单元的内存全部顺序传递给形参。在函数调用期间形参也要占用内存单元。这种传递方式在空间和实践上开销较大,如果结构体的规模很大时,开销是很客观的。并且,由于采用值传递的方式,如果在函数被执行期间改变了形参的值,该值不能反映到主调函数中的对应的实参,这往往不能满足使用要求。因此一般较少使用这种方法。

  (2)用指向结构体变量的指针作为函数参数

  #include

  #include

  using namespace std;

  struct Student{

  string name;

  int score;

  };

  int main(){

  Student one;

  void Print(Student *p);

  one.name="千手";

  one.score=99;

  Student *p=&one;

  Print(p);

  cout<

  cout<

  return 0;

  }

  void Print(Student *p){

  cout<name<< p="">

  cout<<++p->score<

  }

  这种方式虽然也是值传递的方式,但是这次传递的值却是指针。通过改变指针指向的结构体变量的值,可以间接改变实参的值。并且,在调用函数期间,仅仅建立了一个指针变量,大大的减小了系统的开销。

  (3)用结构体变量的引用变量作函数参数

  #include

  #include

  using namespace std;

  struct Student{

  string name;

  int score;

  };

  int main(){

  Student one;

  void Print(Student &one);

  one.name="千手";

  one.score=99;

  Print(one);

  cout<

  cout<

  return 0;

  }

  void Print(Student &one){

  cout<

  cout<<++one.score<

  }

  实参是结构体变量,形参是对应的结构体类型的引用,虚实结合时传递的是地址,因而执行效率比较高。而且,与指针作为函数参数相比较,它看起来更加直观易懂。因而,引用变量作为函数参数,它可以提高效率,而且保持程序良好的可读性。

  7、IMPORT 伪指令

  IMPORT伪指令用于通知编译器要使用的标号在其他的源文件中定义,但要在当前源文件中引用,而且无论当前源文件是否引用该标号,该标号均会被加入到当前源文件的符号表中。

  在ST的工程建立当中,会有两种方式,一种是寄存器版本,一种是固件库版本。

  寄存器版本在新建的过程中就有一些功能和文件不需要添加到。

  在寄存器版本新建工程后,添加启动文件startup_stm32f10x_hd.s (堆栈、PC初始化,向量异常地址入口初始化、调用MAIN函数),其中,教程里要求注释掉下面几行(绿色部分):

  Reset_Handler PROC

  EXPORT Reset_Handler [WEAK]

  IMPORT __main

  ;寄存器版本代码,因为没有用到 SystemInit 函数,所以注释掉

  ;库函数版本代码,建议加上这里(外部必须实现 SystemInit 函数),以初始化 stm32 时钟等。

  ;IMPORT SystemInit 调用SystemInit这个函数

  ;LDR R0, =SystemInit

  ;BLX R0

  LDR R0, =__main

  BX R0

  ENDP

  当报找不到 SystemInit 函数时,解决的办法有下面三个

  ①在外部(其他任何.c文件里面)定义SystemInit这个函数,空函数也可以。

  ②把

  IMPORT SystemInit

  LDR R0, =SystemInit

  BLX R0

  这两句话注释掉或者去掉。

  ③可以添加system_stm32f10x.c这个库文件,到工程里面,也可以解决。

  但是第三种方法比较麻烦,因为如果你自己定义了一些函数,也许和system_stm32f10x.c有冲突。

  8、文件的包含问题

  #include操作是,若后面带的是<>,则文件在安装路径中找;

  若后面带的是“”,则文件在源目录中找。

  9、Volatile 语句

  变量前若有加volatile 这个关键字,则每当系统用到这个变量时,则必须重新读取这个变量的值。

  这种语句被大量用来描述一个对应于内存映射的输入输出端口,或者寄存器,如IO口的寄存器等。

  如下:

  int flag = 0;

  void car_action ()

  {

  while(1)

  {

  if (flag) car_go( );

  }

  }

  void car_stop( )

  {

  flag = 1;

  }

  在上述例子中,car_action 没有更改flag 的操作,所以可能只有第一次执行car_action 才会读取flag的值。后续都直接采用第一次读取的值。而实际上在car_stop中,flag的值已经变化。

  在这种情况下,car_action函数的执行结果就可能出错。

  但若在定义中采用 volatile int flag的写法,则每次要识别flag时,就会追溯到源地址中存储的数据去取数据,程序就能正常执行。

C语言编程基础视频资料

http://www.makeru.com.cn/course/details/2233?s=45051

Linux C语言网盘资料,网盘资料更加全面系统,因为网盘链接有失效性所以就不放网盘的链接了,有要系统全面的资料还请+下方的vx获取,请说明需要啥资料

C语言编程基础有网盘资料哦的更多相关文章

  1. Python网络编程基础|百度网盘免费下载|零基础入门学习资料

    百度网盘免费下载:Python网络编程基础|零基础学习资料 提取码:k7a1 目录: 第1部分 底层网络 第1章 客户/服务器网络介绍 第2章 网络客户端 第3章 网络服务器 第4章 域名系统 第5章 ...

  2. LINUX下C语言编程基础

    实验二 Linux下C语言编程基础 一.实验目的 1. 熟悉Linux系统下的开发环境 2. 熟悉vi的基本操作 3. 熟悉gcc编译器的基本原理 4. 熟练使用gcc编译器的常用选项 5 .熟练使用 ...

  3. Linux基础与Linux下C语言编程基础

    Linux基础 1 Linux命令 如果使用GUI,Linux和Windows没有什么区别.Linux学习应用的一个特点是通过命令行进行使用. 登录Linux后,我们就可以在#或$符后面去输入命令,有 ...

  4. 【转】Linux基础与Linux下C语言编程基础

    原文:https://www.cnblogs.com/huyufeng/p/4841232.html ------------------------------------------------- ...

  5. c语言编程基础入门必备知识

    数据类型 基本数据类型 类型名称说明char字符类型存放字符的ASCII码int整型存放有符号整数short短整型存放有符号整数long长整型存放有符号整数long long存放有符号整数float单 ...

  6. 20191310李烨龙Linux C语言编程基础

    Linux C语言编程基础 任务详情 0. 基于Ubuntu或OpenEuler完成下面的任务(OpenEuler有加分) 1. 选择教材第二章的一节进行编程基础练习(2.10,2.11,2.12,2 ...

  7. Linux下下载百度网盘资料

    因为百度网盘没有Linux下的客户端,所以无法直接下载网盘里的资料了.各路大神各显神通,提出了各种解决方法,这里只介绍两种. 1.BaiduPCS Github上有人通过Go语言写了一个Baidu网盘 ...

  8. iTOP-4412开发板网盘资料介绍

    iTOP-4412开发板网盘视频资料内容如下: 01-烧写.编译以及基础知识视频 02-嵌入式Linux 视频 03-iTOP-4412 开发板硬件设计指导视频 04-Android 应用程序视频 0 ...

  9. 黑马新版PYTHON教学课程(全)资料加视频完整版百度网盘资料

    黑马新版PYTHON教学课程(全)资料加视频完整版 无加密,适合0基础人群.基础班+就业班.不用解压在线看 百度网盘地址一 淘宝店地址二

随机推荐

  1. ZBLOG PHP调用相关文章列表以及上一篇下一篇文章代码

    如果是比较小的个人博客.专题类网站项目,老蒋还是比较喜欢使用ZBLOG PHP程序的,无论是轻便度还是易用性上比WordPress简单很多,虽然WP的功能很强大,比如强大的插件和主题丰富功能是当前最为 ...

  2. nmap使用命令(转载)原文地址https://www.jianshu.com/p/4030c99fcaee

  3. 解决vscode可以编译通过c++项目,但头文件有红色波浪线的问题

    解决vscode可以编译通过c++项目,但头文件有红色波浪线的问题 一.问题描述 我是在Ubuntu 16.04的环境下,用vscode写代码的,一般不使用vscode自带的编译环境,而是用cmake ...

  4. CSP-J 2021 游记

    今年是本人第一次参加CSP组的竞赛. Day 0 晚上复习了几套初赛试卷,做到晚上十点多结束.其实暑假已经做过不少了. Day 1 早上继续复习noip历年真题,在洛谷有题上面自己做题,一向只能考十几 ...

  5. 彻底解决Hive小文件问题

    最近发现离线任务对一个增量Hive表的查询越来越慢,这引起了我的注意,我在cmd窗口手动执行count操作查询发现,速度确实很慢,才不到五千万的数据,居然需要300s,这显然是有问题的,我推测可能是有 ...

  6. TP5 数据保存、更新问题(save、saveAll)

    一.今天写项目的时候,突然发现一个坑爹的问题,使用saveAll新增多条数据,但是一直提示缺少更新条件,然而我发现代码里面并没有更新,而且saveAll我仅仅是去新增多条数据而已 原来源码 模型类中有 ...

  7. Linux系列(31) - rpm命令管理之升级与卸载命令(3)

    升级命令 rpm -Uvh 包全名(要升级到的软件版本),如果没有安装这个软件的任何版本,这个命令可以代替rpm -ivh. rpm -Uvh 包全名 选项: - -U(upgrade):升级 卸载命 ...

  8. 面试官问:App测试和Web测试有什么区别?

    WEB 测试和 App 测试从流程上来说,没有区别.都需要经历测试计划方案,用例设计,测试执行,缺陷管理,测试报告等相关活动. 从技术上来说,WEB 测试和 APP 测试其测试类型也基本相似,都需要进 ...

  9. Ubuntu18.04安装MySQL教程

    Ubuntu18.04下安装MySQL 提示:以下操作均在root权限下进行. # 查看有没有安装MySQL: dpkg -l | grep mysql # 安装MySQL: apt install ...

  10. 题解 Weak in the Middle

    题目传送门 Description 有一个长度为 \(n\) 的序列 \(a_{1,2,...,n}\) ,每次可以删掉 \(a_i\),当 \(\min(a_{i-1},a_{i+1})>a_ ...