• 【RTOS训练营】站在更高的角度学习C语言


    一. 前言

    C语言基础,有些同学基础扎实,有同学能用但是理解不深,这个训练营的重点在于RTOS和芯片架构,对C语言的要求也不算高. 结构体、指针、链表,掌握这三点就可以,基本不涉及复杂的语法,基础弱的同学,可以看唐老师的C语言视频,免费的。

    我们并不需要停下来单独去学习C语言,C语言可以在RTOS的学习过程中再慢慢精进,不用担心。

    唐老师的C语言课程在我们官网(www.100ask.net):请添加图片描述
    现在,我们一起来站在更高角度学习C语言

    二. 代码引入

    int a;
    volatile unsigned int *p;
    void main()
    {
    	a = 123;
    
    	p = (volatile unsigned int *)(0x40010800 + 0x0c); /* GPIOA_ODR寄存器的地址 */
      *p = 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    1. 从这段代码可以引出几个问题:

    ①这段代码涉及哪些硬件?

    • Flash、RAM、GPIO、CPU

    ②程序保存在哪里?

    • Flash

    ③变量a、p保存在哪里?

    • RAM

    ④操作p时,操作哪里?

    • GPIO

    ⑤谁来执行这个程序?

    • CPU
    • 硬件方面,至少涉及这4部分:Flash、RAM、GPIO、CPU。

    1.1 大家先看这个动图(图中的地址是假设的):

    请添加图片描述

    2. 全局变量a、p:

    int a;
    volatile unsigned int *p;
    
    • 1
    • 2

    这两行代码,会在RAM中分配空间给这2个变量(图中的地址是假设的)
    请添加图片描述
    变量a、p的地址,你是控制不了的,编译器分配的地址

    3. 执行main函数的代码:a = 123;是怎么进行的呢?

    • 代码保存在Flash;

    • CPU读取Flash得到代码;

    • CPU根据代码指示,写变量a;

    4. p = (volatile unsigned int *)(0x40010800 + 0x0c);的执行

    • 代码保存在Flash;

    • CPU读取Flash得到代码;

    • CPU根据代码指示,写变量p;

    执行完下面两行代码后:

    	a = 123;
    
    	p = (volatile unsigned int *)(0x40010800 + 0x0c); /* GPIOA_ODR寄存器的地址 */
    
    • 1
    • 2
    • 3

    内存情况如下图(图中的地址是假设的):

    请添加图片描述
    大家可以看到:

    • 假设CPU将地址0x2000 0000分配给了a,0x2000 1000分配给了p;

    • 对变量a和变量p的写操作都是在写RAM(在STM32F103中地址0x2000 0000开始的一片内存是映射的是RAM空间);

    • 当对变量a进行赋值时,CPU就在a所在的地址空间 ,即从地址0x2000 0000开始的一小段空间(根据a的类型和cpu的位数决定a占用多少长度的空间)写入数据;

    • 对于变量p的写操作也是类似的;

    执行那两行代码a = 123;``p = (volatile unsigned int *)(0x40010800 + 0x0c);的结果就是:

    • 在RAM地址0x2000 0000处写入了一个数据是123;

    • 在RAM地址0x2000 1000处写入了一个数据是(0x40010800 + 0x0c)0x4001 080C

    • 又根据指针的定义, 我们对
      p = (volatile unsigned int *)(0x40010800 + 0x0c);这个操作就得到了一个信息: 指针p指向的地址是0x4001 080C

    5. *p = 1

    *p = 1;
    
    • 1

    我们刚才已经知道, 指针p指向的地址是0x4001 080C,所以这一行代码的作用就是让CPU在p指向的地址写入一个数据1,即在地址0x4001 080C处写入数据1。我们在前面以STM32F103的内存空间举例得知地址0x2000 0000开始的一片内存映射的是RAM空间,那么地址0x4001 080C也是RAM空间嘛?不是的。根据STM32的手册我们可以发现地址0x4001 080C处映射的其实是STM32F103的引脚GPIOA(GPIO:通用输入输出)的输出寄存器ODR。

    也就是说, *p = 1就是让地址0x4001 080C保存的数据变成了1,对于STM32F103而言,完成的功能就是让GPIOA的寄存器ODR的最低位bit0 = 1,最终表现出来的结果我们放在后面的课程说。

    在整个过程中,最重要的是:地址

    CPU读Flash得到指令

    • 怎么读Flash?用地址
    • 得到什么?得到指令,也就是Flash上的数据
      请添加图片描述

    CPU这个大爷,向外面发出地址,读到Flash上的数据。这些数据就是指令,然后CPU执行指令

    6. 深入CPU的指令执行

    6.1 指令a = 123?

    • 怎么写变量a?用地址

    • 做了什么?把数值123写到了内存里

    请添加图片描述

    6.2 指令p = (volatile unsigned int *)(0x40010800 + 0x0c)

    ​ CPU得到这条指令后,怎么执行?

    • 怎么写变量p?用地址
    • 做了什么?把数值0x4001080c写到了内存里

    6.3 指令*p = 1

    ​CPU得到这条指令后,怎么执行?

    • 怎么写GPIO寄存器?用地址;
    • 做了什么?把数值1写到了地址0x4001080c上,就是写到了GPIOA_ODR寄存器,导致LED变亮或熄灭;
      请添加图片描述

    7. 程序核心

    这个程序的核心是什么?写地址
    最核心是什么:地址

    我们以葫芦娃举例:
    请添加图片描述
    这位大爷,怎么使唤葫芦娃?

    • 叫名字, 7个葫芦娃都编号1234567;
    • 使唤

    在电子系统中,CPU也是大爷,外面的RAM、GPIO、Flash就是儿子。CPU要访问RAM、GPIO、Flash,也要先点名:发出地址:
    请添加图片描述

    7.1 地址和内存

    RAM很大,CPU读写数据时,是不是要发出地址给RAM,再收发数据?假设我们有个这样的硬件框图:
    请添加图片描述
    我们说RAM、GPIO、Flash,是兄弟,是平等的,都给CPU大爷使唤,那么CPU大爷是如何访问这多个平等关系的设备的呢?

    ​大家看,设备1、设备2都需要地址线,都需要数据线。CPU大爷发出地址,同时到达设备1、设备2;CPU大爷发出数据,同时到达设备1,设备2;那么问题来了:这数据给谁的啊?

    ​所以,这些连线不够!还需要一个叫做片选信号的信号线和一个内存管理器,如图所示:
    请添加图片描述
    CPU大爷,和它的儿子之间,需要插入一个传话人,这个传话人叫:内存管理器/内存控制器。因为RAM、GPIO、FLASH都是同类的设备,都有地址,都能读、写。都 “类似” 内存。假设设备1的地址范围是XXX-YYY,假设设备2的地址范围是AAA-BBB,访问它们的过程是这样的:

    • CPU大爷发出地址 addr;
    • 内存管理器/内存控制器发现addr 处于 XXX-YYY之间,就知道了,哦,你要访问设备1;
    • 内存管理器/内存控制器就把cs1设置为有效值,表示说:CPU大爷选中你了;同时,cs2保持无效值,也就是设备2没被选中,设备2就保持沉默;
    • 所以,CPU发出的addr、数据,只会影响到设备1

    根源在于:传话人,根据大爷发出的地址,判断要访问哪个设备,就去选中设备。地址!地址!地址!非常重要!

    7.2 深入地址

    什么叫地址?

    ​在一栋安居房里,政府故意把房子设计得很小,每个房间都是单独的。

    • 单身汉只有1房:char c;
    • 夫妻有2房:short a;
    • 有娃的家庭有4房:int b;
    • 大家庭有8房:char buf[8];

    变量在内存哪个位置?我们一般无法决定,链接时确定的。
    请添加图片描述
    每个单间,都有一个地址。char c;占据一个字节,有一个地址;short a;占据2字节,有2个地址值;int b;占据4字节,有4个地址值;但是,我们去拜访这些居民时,只使用一个地址:首地址
    请添加图片描述
    在同一栋楼里,大家的地址都是类似的:XX街道XXX小区XXX栋XXX房。在C语言里,就是:char, short, int, struct 、字符串,它们的首地址都是类似的:位数都一样。对于32位CPU,地址都是32位的。用一段伪代码来表示的话,就是这样一个结果:
    请添加图片描述

  • 相关阅读:
    没有想好标题
    Java经典面试题集
    轻松掌握 jQuery 基础
    作用域与作用域链--面试题
    玩转树莓派二、树莓派配置工具 raspi-config 使用指南
    【软件测试】如何用python连接Linux服务器
    如何进行列间排序
    共享门店模式:一种新兴的商业模式
    linux 系统内存诊断
    请不要忽略软件测试的业务能力
  • 原文地址:https://blog.csdn.net/thisway_diy/article/details/125885885