目录
二、在Ubuntu(x86)系统和STM32(Keil)中分别进行编程、验证
一、C语言的内存分配
1、栈区(stack)
-
临时创建的局部变量存放在栈区。
-
函数调用时,其入口参数存放在栈区。
-
函数返回时,其返回值存放在栈区。
-
const定义的局部变量存放在栈区。
2、堆区(heap)
堆区用于存放程序运行中被动态分布的内存段,可增可减。
可以有malloc等函数实现动态分布内存。
有malloc函数分布的内存,必须用free进行内存释放,否则会造成内存泄漏。
3、全局区(静态区)
全局区有.bss段和.data段组成,可读可写。
4、.bss段
未初始化的全局变量存放在.bss段。
初始化为0的全局变量和初始化为0的静态变量存放在.bss段。
.bss段不占用可执行文件空间,其内容有操作系统初始化。
5、.data段
已经初始化的全局变量存放在.data段。
静态变量存放在.data段。
.data段占用可执行文件空间,其内容有程序初始化。
const定义的全局变量存放在.rodata段。
6、常量区
字符串存放在常量区。
常量区的内容不可以被修改。
7、代码区
程序执行代码存放在代码区。
字符串常量也有可能存放在代码区。
二、在Ubuntu(x86)系统和STM32(Keil)中分别进行编程、验证
代码:
#include <stdio.h>
#include <stdlib.h>
//定义全局变量
int init_global_a = 1;
int uninit_global_a;
static int inits_global_b = 2;
static int uninits_global_b;
void output(int a)
{
printf("hello");
printf("%d",a);
printf("\n");
}
int main( )
{
//定义局部变量
int a=2;
static int inits_local_c=2, uninits_local_c;
int init_local_d = 1;
output(a);
char *p;
char str[10] = "lyy";
//定义常量字符串
char *var1 = "1234567890";
char *var2 = "qwertyuiop";
//动态分配
int *p1=malloc(4);
int *p2=malloc(4);
//释放
free(p1);
free(p2);
printf("栈区-变量地址\n");
printf(" a:%p\n", &a);
printf(" init_local_d:%p\n", &init_local_d);
printf(" p:%p\n", &p);
printf(" str:%p\n", str);
printf("\n堆区-动态申请地址\n");
printf(" %p\n", p1);
printf(" %p\n", p2);
printf("\n全局区-全局变量和静态变量\n");
printf("\n.bss段\n");
printf("全局外部无初值 uninit_global_a:%p\n", &uninit_global_a);
printf("静态外部无初值 uninits_global_b:%p\n", &uninits_global_b);
printf("静态内部无初值 uninits_local_c:%p\n", &uninits_local_c);
printf("\n.data段\n");
printf("全局外部有初值 init_global_a:%p\n", &init_global_a);
printf("静态外部有初值 inits_global_b:%p\n", &inits_global_b);
printf("静态内部有初值 inits_local_c:%p\n", &inits_local_c);
printf("\n文字常量区\n");
printf("文字常量地址 :%p\n",var1);
printf("文字常量地址 :%p\n",var2);
printf("\n代码区\n");
printf("程序区地址 :%p\n",&main);
printf("函数地址 :%p\n",&output);
return 0;
}
在Ubuntu上运行结果
stm32开发板上运行结果
从图片中可以看出ROM的地址分配是从0x8000000开始,整个大小为0x40000,这个部分用于存放代码区和文字常量区。RAM的地址分配是从0x20000000开始,其大小是0xC000,这个区域用来存放栈、堆、全局区(.bss段、.data段)。与代码结果显示进行对比,也可以看出对应得部分得地址与设置的是相对应的。
三、安装Clion2021
在官网下载 CLion2021.2 Winx64
双击下载好的JetBrains CLion v2021.3 Winx64.exe 安装文件,将安装目录改一个盘,还有就是这个地方勾选一下,其余地方都点next就行了
等待安装好后,需要重启电脑,选择I want to manually reboot later,就是等一下重启的意思,然后点击finish,然后可以看到安装好了
接下来需要安装配置文件arm-none-eabi-gcc, 在这里下载配置文件
下载好后解压,然后将bin文件夹添加到环境变量,找到此电脑右键找到属性,点击进去后如下操作
测试是否安装成功,再命令行输入下面语句看是否有信息输出
arm-none-eabi-gcc -v
安装成功。
接下来安装另一个配置文件arm-none-eabi-gcc,在这里下载文件
注意:不要点击最上面的那个绿色的download
下载好后解压,记住解压的路径
打开CLion(需要身份认证,网上有博客详细解释怎么申请学生身份的认证教程),新建一个工程,将工程文件存放位置修改一下,点击create就行了
然后在在file里面找到setting按如下修改,将MinGW添加进工程
dianjiOK就完成了MinGW的配置
接着安装配置OpenOCD
在官网下载文件,下载最新版的就行了
下载好后解压,记住解压的位置
回到clion,还是在setting里面进行修改,将右侧的 OpenOCD 文件目录转换到自己下载的位置,最后点击 Test
发现提示颜色为墨绿色,即代表配置成功 (顺便把 CubeMX 也配置了)
最后点击OK即可完成。
四、用clion点亮led
用 CLion 创建新工程,选择 STM32CubeMX ,填写好项目要保存的路径,点击 Create
即可
等待调用STM32CubeMX,然后点击Open with STM32CubeMX,进入STM32CubeMX
打开之后,选择芯片
选择使用 STM32F103C,再双击右下方MCUs/MPUs LIst 的选项,就创建了一个工程
配置sys
配置rcc
配置引脚
配置串口USART1
命名工程文件并设置
Project Name 要重新填写一下(因为换芯片的过程,其实是 Cube 新建了一个 ioc 文件),建议填写之前的工程名和文件目录,这样就可以把之前不想要的那个 .ioc 文件覆盖掉。
然后" Toolchain/IDE "那里,选择 SW4STM32,点击右上角create code出现提示点击yes覆盖文件
回到CLion配置工程,可以看到出现了如下界面,选择 st_nucleo_f103rb.cfg 文件,并使用
注意:如果回到 CLion之后没有出现如下界面,那就可能是你上一步生成 CubeMX 工程的时候,文件命名和目录选择与之前的不同导致的,只需要重新在 CLion 打开你重新生成的 CubeMX 工程文件目录即可。
在右上角的OCD test中点击Edit找到st_nucleo_f103rb.cfg 文件位置
然后对文件进行修改,将第十行注释掉
注解
:
第 4 行是选了连接方式是 stlink
第 6 行是选择 swd 通信模式
第 8 行是选择 stm32f1x 的配置文件
第 10 行是选择重启模式,貌似要将其注释起来,不注释掉下载可能失败。
接下来打开main.c文件,在while循环里面添加代码
while (1)
{
/* USER CODE END WHILE */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
HAL_Delay(500);
/* USER CODE BEGIN 3 */
}
点击 File-Settings-CMake
,选择下图中的选项,最后点击 OK
点击编译图标
编译成功
接着就是将烧录程序
效果展示:
五、总结
对C语言的 的内存分配进一步了解了很多,对Ubuntu以及keil的使用以及硬件的烧录更加熟练。用CLion点亮led不难,就是要安装很多东西很难。
六、参考资料
基于ubuntu,树莓派和stm32的C程序的内存分配问题_Harriet的博客-CSDN博客
CLion2021 的详细安装并基于 CLion 实现 stm32F103 点亮 LED_L-GRAZY的博客-CSDN博客