教你如何轻松写单片机的指针

发布者:温暖的拥抱最新更新时间:2024-05-06 来源: elecfans关键字:单片机  指针  STM32 手机看文章 扫描二维码
随时随地手机看文章

摘要:大家想过没有我们用keil写单片机的代码,你的函数啊、变量啊最终都放在了哪里?我们一直说的内存五区,到底是哪五区?到底放在芯片的哪个地方呢?还有为什么你学完C语言指针和结构体,32单片机里面的关于结构体指针的内容还是搞不清楚呢?如果你有这些问题,今天就带你研究研究!


这张图学过STM32单片机的小伙伴应该都不陌生,我们看到的STM32芯片已经是已经封装好的成品,主要由内核和片上外设组成。若与电脑类比,内核与外设就如同电脑上的CPU与主板、内存、显卡、硬盘的关系。芯片和外设之间通过各种总线连接。连接被控总线的是FLASH,RAM和片上外设,这些功能部件共同排列在一个4GB的地址空间内。上面这些张图是STM32F40XXX系列单片机的内存地址映射图。


我们的代码就是放在Flash里面(0x8000000~0x80FFFFF)。代码就是你写得各种函数,而在程序中声明的各种变量都放在RAM中,局部变量就是在函数运行完空间释放,全局变量就是程序运行完了再释放,可以这样简单的理解。

CPU使用的变量是存储在RAM里面的,要问我RAM是啥,RAM就是个芯片。就是上图的Block1的SRAM区。CPU是通过导线和RAM芯片连接的,然后可以通过导线往RAM芯片里面存储数据和读数据。首先RAM需要有个一开始的地址,对于STM32单片机来说开始地址是0x20000000,要问我为啥要规定地址。只有规定了地址CPU才好对数据进行存储,要是没有地址,瞎几把存,瞎几把取。。.。。.

1、变量1.定义一个int型的变量,通过打印可以看到这个变量存储的地址是:0x20000000。这也证明了我们内存的首地址是0x20000000。我们定义的value变量就放在这里。

3aea0c10-ca98-11eb-9e57-12bb97331649.png

3b100280-ca98-11eb-9e57-12bb97331649.png

2.再定义一个变量

通过打印可以看到这个变量存储的地址是:0x20000004。因为int类型在内存中占据4个字节,所以第二个变量就存放在0x20000004这个地方。

3b1f7e2c-ca98-11eb-9e57-12bb97331649.png

综上所述,定义的两个变量在内存里面是下面这样子。

0x2000 0000地址里面存储的是 0

0x2000 0004地址里面存储的是 1

2、指针变量定义指针其实和定义变量一样的,只不过变量名前头有个*

下面就定义一个int型的指针变量,变量的名字是p。然后有人会问,为啥变量名字前面加个*就是指针了?

答:搞C语言那帮家伙们规定的。

定义指针和定义变量一样,然后可以定义各种类型的。

然后记住一句话:

“指针这个变量是存变量的地址的! 指针这个变量是存变量的地址的! 指针这个变量是存变量的地址的!”所以给指针赋值自然是把变量的地址给它。

#include “sys.h”#include “led.h”#include “delay.h”#include “usart.h”int value = 0;

int value2 = 1;

int *p;

int main(void)

{

uart_init(115200);

delay_init();

p=&value;//把变量value的地址复制给这个指针

printf(“Address of a: %p

”,p);//打印下这个指针指向的地址

while(1)

{

}

}

一般什么类型的指针变量就应该赋值什么类型变量的地址。如再定义个char型

#include “sys.h”#include “led.h”#include “delay.h”#include “usart.h”int value = 0;

int value2 = 1;

int *p;//定义一个指针char value3=1;

char *q;

int main(void)

{

uart_init(115200);//串口初始化

delay_init();

p=&value;//把变量value的地址复制给这个指针

q=&value3;//把变量value的地址复制给这个指针

printf(“Address of a: %p

”,q);//打印下这个指针指向的地址

while(1)

{

}

}

那些规定C语言的大佬弄出来指针这个玩意有啥用?

3、指针有啥用?1.咱先使用下指针,然后具体有啥用就自己体会了。前面咱把一个变量的地址赋值给了指针了,然后搞C语言的那帮家伙们又规定。*{指针变量名} :代表了这个指针所指向的变量。

啥意思呢?

对照下面的程序p=&value,p记录的就是变量value的地址, 然后*p就代表value。

#include “sys.h”#include “led.h”#include “delay.h”#include “usart.h”int value = 0;

int *p;//定义一个指针

int main(void)

{

uart_init(115200);//串口初始化

delay_init();

p=&value;//把变量value的地址复制给指针变量p

printf(“Address of a: %d

”,value);

printf(“Address of b: %d

”,*p);

while(1)

{

}

}

3bdfdb54-ca98-11eb-9e57-12bb97331649.png

有人会想。。.。。.就这?

有人觉得多此一举?

其实我一开始也是这样想的。。.。。.

既然 * p就代表value,那么* p=XXXX

不就是相当于value=XXXX

看看下面这个例子

#include “sys.h”#include “led.h”#include “delay.h”#include “usart.h”int value = 0;

int *p;//定义一个指针 int main(void)

{

uart_init(115200);//串口初始化

delay_init();

p=&value;//把变量value的地址复制给指针变量p

printf(“value of a: %d

”,value);

*p=520;

printf(“value of b: %d

”,value);

while(1)

{

}

}

还是没感觉到指针有啥用?别着急,先把基本的知识点学完哈。没有最基本的知识储备是不可以的,因为厚积而薄发!

见过返回值是指针的函数没?

4、函数指针先看一下,如果感觉不理解就接着往下看

#include “sys.h”#include “led.h”#include “delay.h”#include “usart.h”int value = 0;

int *p;//定义一个指针

int *function(void)

{

return &value;//把value的地址返回

}

int main(void)

{

uart_init(115200);//串口初始化

delay_init();

p=function();//调用函数,其实就是把value的地址赋值给了p

printf(“Address1 of a: %p

”,&value);//打印value的地址

printf(“Address2 of a: %p

”,p);//打印p所代表的地址

while(1)

{

}

}

很多人用过返回值是int、char的函数,但是在int,char 后面加个*

估计对于初学者没有用过。其实就是指针之间赋值。下面就是把p(int*类型的指针) 代表的地址赋值给q

变量之间可以互相赋值吧,指针之间也一样,可以互相之间赋值。

其实和上面是一样的道理,那个函数function返回值是一个int*类型的指针,然后赋值给了p而已

#include “sys.h”#include “led.h”#include “delay.h”#include “usart.h”int value = 0;

int *p;//定义一个指针int *q;//定义一个指针

int main(void)

{

uart_init(115200);//串口初始化

delay_init();

p=&value;//把value的地址赋值给了p

q=p;//把p代表的地址给q

printf(“Address1 of a: %p

”,&value);//打印value的地址

printf(“Address2 of a: %p

”,q);//打印p所代表的地址

while(1)

{

}

}

姑且再问一句,函数名字是啥?

咱们都知道这样调用函数

#include “sys.h”#include “led.h”#include “delay.h”#include “usart.h”void function()

{

printf(“zhiguoxin

”);

}

int main(void)

{

uart_init(115200);//串口初始化

delay_init();

function();

while(1)

{

}

}

但是这样的见过没

#include “sys.h”#include “led.h”#include “delay.h”#include “usart.h”void (*fun)();

void function()

{

printf(“zhiguoxin

”);

}

int main(void)

{

uart_init(115200);//串口初始化

delay_init();

fun = function;

fun();

while(1)

{

}

}

这里采用了函数指针

先记住一句话

“函数名就是这个函数的地址! 函数名就是这个函数的地址! 函数名就是这个函数的地址!”既然是地址,那么这个地址应该可以赋值给一个指针。因为是函数的地址,所以咱定义的指针也一定是一个函数类型的。

上面的函数void function()是一个没有返回值,没有形参的函数。那么咱需要定义一个这种的指针类型,其实就是void (*指针变量名字,随意写) ()。上面写的是 void (*fun)(); fun就是一个函数类型的指针,是一个没有返回值,没有形参的函数指针。

咱可以把这种函数赋值给这个指针变量。就是上面的fun=function。那么这个函数指针便代表了那个函数fun就等同于function。所以调用 fun(); 就等同于调用function()。

如果函数有形参怎么办? 好办,它有咱就+

#include “sys.h”#include “led.h”#include “delay.h”#include “usart.h”void (*fun)(int a);

void function(int value)

{

printf(“value= %d

”,value);

}

int main(void)

{

uart_init(115200);//串口初始化

delay_init();

fun = function;//把function赋值给fun

fun(520);//fun就等同于function

while(1)

{

}

}

如果函数有返回值怎么办?照+不误

#include “sys.h”#include “led.h”#include “delay.h”#include “usart.h”int res;

int (*fun)(int a);

int function(int value)

{

return value;

}

int main(void)

{

uart_init(115200);//串口初始化

delay_init();

fun = function;//把function赋值给fun

res = fun(520);//fun就等同于function

printf(“res = %d”,res);

while(1)

{

}

}

总结一下

指针呢其实基本的也就是上面那些,指针就是用来记录变量的地址的。或是做地址之间的传递的。

&代表取地址符。

*代表取数据。

&{变量名} :就是把这个变量的地址取出来。

*{指针变量名} :就是把这个指针所代表的地址里面的存的值取出来”下面看一些比较常见的应用。把数组的地址赋值给指针,然后用指针操作数组

#include “sys.h”#include “led.h”#include “delay.h”#include “usart.h”char temp[3]={1,2,3};

char *p;

int main(void)

{

uart_init(115200);//串口初始化

delay_init();

p=temp;//将数组名赋值给指针变量p,p就指向数组temp的首地址

printf(“value0 = %d

”,*p); //p就代表数组的第一个数据的地址

printf(“value1 = %d

”,*(p+1));//p+1就代表数组的第二个数据的地址

printf(“value2 = %d

”,*(p+2));//p+2就代表数组的第三个数据的地址

printf(“temp[0] = %d

”,p[0]);//p[0]等同于temp[0]

printf(“temp[1] = %d

”,p[1]);//p[1]等同于temp[1]

printf(“temp[2] = %d

”,p[2]);//p[2]等同于temp[2]

while(1)

{

}

}

5、函数的形参是一个指针#include “sys.h”#include “led.h”#include “delay.h”#include “usart.h”char temp[3]={1,2,3};

void function(char *value)

{

printf(“value0 = %d

”,value[0]);

printf(“value1 = %d

”,value[1]);

printf(“value2 = %d

”,value[2]);

}

int main(void)

{

uart_init(115200);//串口初始化

delay_init();

function(temp);

while(1)

{

}

}

以上的指针的基本知识,多练习几遍就可以。指针真正的应用是在于代码的封装。可能对于初学者感受不到其作用,但是当你成为真正的开发人员。你会发现把好多功能函数封装起来,然后留出接口来调用是以后必不可少的。


封装的时候会大量的使用指针、函数指针、结构体指针等,怎么说呢!90%的程序员敲的是字母,写的是代码。当你开始封装的时候,你写的便是思想,但是需要一定的基础知识储备才能达到。


关键字:单片机  指针  STM32 引用地址:教你如何轻松写单片机的指针

上一篇:SM32MCU支持的MAC地址Hash过滤
下一篇:CANFD总线异构通讯简单实例

推荐阅读最新更新时间:2024-11-03 12:29

MCU厂商,都在重视IDE
国产MCU经历几年的大洗牌,格局基本稳定下来,很多厂商已经发力从兼容逐渐拓展自己的版图,IDE(Integrated Development Environment,集成开发环境)就是其中一环。 最近,工程师发现雅特力搞了个IDE。可以说,对于MCU厂商而言,IDE似乎又是一个发力点。那么,国产的IDE到底怎么样,现在IDE的格局又如何? IDE的三种类型 所谓IDE,正如其名,其中囊括了编辑器、编译器、调试器和图形用户界面等工具,是一个集成了代码编写功能、分析功能、编译功能、调试功能等一体化的开发软件服务套件。IDE主要分成三种类型。 第一种是通用IDE,也被称作是第三方IDE。顾名思义支持STM32、P
[单片机]
<font color='red'>MCU</font>厂商,都在重视IDE
采用PIC12C508单片机蓄电池监控电路
   在卡车,汽车,娱乐车和不间断电源使用 的12v Lead Acid 蓄电池通常其额定值为12V,这个电路监控着电池,充放电曲线,给出当前电压值并预测到供电结束所剩的时间。   12V电池在完全充电时的电压值为13.8V,完全释放时的电压值为10.8v。在3v的范围之内是线性的,能够使用作预测UPS所剩的供电的时间的值。   图中说明了1位的A/D转换器。在单片机GP0,GP1,GP2和GP4脚的电阻存放高,低和开三状态,允许GP3输入端作为电压比较器。   电路示意图:
[单片机]
采用PIC12C508<font color='red'>单片机</font>蓄电池监控电路
关于STM32串口printf输出调试信息问题
1,遇到的问题(使用HAL库) 在STM32使用过程中,我们程序调试时一般都会用到printf重定向串口输出调试信息来进行程序开发调试,从网上我们找到了重定向的代码部分加入到串口代码文件中,如下: UART_HandleTypeDef husart_printf; #ifdef __GNUC__ /* With GCC/RAISONANCE, small printf (option LD Linker- Libraries- Small printf set to 'Yes') calls __io_putchar() */ #define PUTCHAR_PROTOTYPE int __io_
[单片机]
基于CAN总线的单片机与数字信号处理器通信系统设计
0 引 言 众所周知,虽然目前8位单片机正逐渐被速度高,性能强的16位或32位微处理器所取代,但8位单片机仍以其低廉的价格、丰富的外围芯片以及众多的多功能产品而在低端应用市场占据主流地位。数字信号处理器(Digital Signal Processor,DSP)作为一种具有高速数字信号处理能力的新型单片机,在通信、自动控制、航天航空、军事、医疗等领域广泛应用。在比较复杂的测控系统中,如微机电动机保护装置,要求在毫秒级的短时间内对电动机实现实时保护和测量,所以对装置硬件系统的实时数据处理能力要求较高,而传统的基于单 CPU微处理器的方案己经难以胜任。因此,这里采用数字信号处理器与单片机构成的双CPU结构。由数字信号处理器完成多
[单片机]
基于CAN总线的<font color='red'>单片机</font>与数字信号处理器通信系统设计
联电推出40奈米SST嵌入式闪存制程 东芝的MCU将采用
联华电子今宣布,该公司推出40奈米结合Silicon Storage Technology(SST)嵌入式SuperFlash非挥发性内存的制程平台。 新推出的40nm奈米SST嵌入式快闪记忆,较量产的55奈米单元尺寸减少20%以上,并使整体内存面积缩小了20-30%。 东芝电子组件&存储产品公司已开始评估其微处理器(MCU)芯片于联电40奈米SST技术平台的适用性。 东芝电子组件&存储产品公司混合信号芯片部门副总松井俊也表示:「我们期待采用联电40奈米SST技术有助于提升我们MCU产品的性能。 与联电合作,透过稳定的制造供应及配合我们的生产需求提供灵活的产能,亦将使我们能够保持强劲的业务连续性计划 (BCP)。 」 已有超过20
[半导体设计/制造]
STM32学习——内存管理
内存管理简介: 内存管理,是指软件运行时对mcu内存资源的分配和使用的技术。主要目的是高效快速的分配使用内存资源。在适当的时候释放回收内存资源。内存管理实现的方式最终是实现两个函数:malloc和free;malloc函数用于内存申请,free函数用于内存释放。 内存分配原理: 当指针P调用malloc申请内存的时候,先判断P要分配的内存块数(m),然后从n项开始,向下查找,直到找到连续的m块空间(即对应的内存管理项为“0”)。然后将这m个内存管理项的值都赋值为m(标记为占用)。,最后,把最后的内存地址返回给指针P,完成一次内存分配。注意:如果内存不够用的时候,或者没有连续的m块内存时,则返回NULL给P,表示分配失败。 内存释放
[单片机]
单片机上拉电阻、下拉电阻的详解
一、定义 1、上拉就是将不确定的信号通过一个电阻嵌位在高电平!“电阻同时起限流作用”!下拉同理! 2、上拉是对器件注入电流,下拉是输出电流 3、弱强只是上拉电阻的阻值不同,没有什么严格区分 4、对于非集电极(或漏极)开路输出型电路(如普通门电路)提升电流和电压的能力是有限的,上拉电阻的功能主要是为集电极开路输出型电路输出电流通道。 二、拉电阻作用 1、一般作单键触发使用时,如果IC本身没有内接电阻,为了使单键维持在不被触发的状态或是触发后回到原状态,必须在IC外部另接一电阻。 2、数字电路有三种状态:高电平、低电平、和高阻状态,有些应用场合不希望出现高阻状态,可以通过上拉电阻或下拉电阻的方式使处于稳定状态,具体
[单片机]
<font color='red'>单片机</font>上拉电阻、下拉电阻的详解
STM32初学笔记1之RCC(上)
我参考了STM32的标准外设库中的RCC例程,然后对其在原有的基础上做了一定的修改,单独添加到了RCC_ClkConfig.C和RCC_ClkConfig.H两个文件当中,把这个作为以后系统时钟配置的通用函数,在这里共享出来,示例代码如下: ////////////////////////////////////////////////////////////////////////////////////////////////////// RCC_ClkConfig.C /////////////////////////////////////////////////////////
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved