STM32
直播中

文甘翀

7年用户 935经验值
私信 关注
[问答]

基于寄存器与固件库的stm32 LED流水灯实例分享

基于寄存器与固件库的STM32 LED流水灯实例分享

回帖(1)

刘海欢

2021-12-2 10:04:11
一、基于寄存器与基于固件库的stm32 LED流水灯例子的编程方式的差异

固件库编程方式:简单易于理解,资料多。适合新手入门。
寄存器编程方式:可移植性强,更贴近底层,要求对外设的工作原理和运行机理有更深的理解。
二、STM32的USART窗口通讯

准备工作

开发板:野火指南者(STM32F103VE)带3.2寸屏
代码编译:Keil5
USBQ驱动:CH340
串口调试助手版本:v1.0.1.5
野火产品资料 提取码:123y
1、打开开发板,并将USB线和ST-LINK线与电脑连接



2、在保持开发板与电脑通过USB线的连接的情况下安装CH340驱动
在上述网盘内打开5-开发软件中找到USB转串口驱动压缩包并解压进行安装





3、在上述网盘内打开5-开发软件中找到调试助手压缩包并解压进行安装下载野火串口调试助手





安装成功后运行如图:



串口通信实现

要求:
1)设置波特率为115200,1位停止位,无校验位。
2)STM32系统给上位机(win10)连续发送“hello windows!”,上位机接收程序可以使用“串口调试助手“,也可自己编程。
3)当上位机给stm32发送“Stop,stm32”后,stm32停止发送。
1、上述网盘内依照 1-书籍配套例程-F103VE指南者.rar21-USART—串口通信USART1接发ProjectRVMDK(uv5)路径找到BH-F103工程,并在keil5中打开






2、修改stm32f10x_it.c文件的串口中断服务函数部分为:


int i=0;
uint8_t ucTemp[50];
void DEBUG_USART_IRQHandler(void)
{
        if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)
        {
                ucTemp = USART_ReceiveData(USART1);       
        }
  if(ucTemp == '!')
        {
                if(ucTemp[i-1] == '2'&&ucTemp[i-2] == '3'&&ucTemp[i-3] == 'm'&&ucTemp[i-4] == 't'&&ucTemp[i-5] == 's'&&ucTemp[i-6] == ' ')
                        if(ucTemp[i-7] == 'p'&&ucTemp[i-8] == 'o'&&ucTemp[i-9] == 't'&&ucTemp[i-10] == 's')
                        {
                                printf("ÊÕµ½£¡");
        while(1);
                        }
        }
        i++;
}


修改main.c函数为:


#include "stm32f10x.h"
#include "bsp_usart.h"




void delay(uint32_t count)
{
        while(count--);
}
int main(void)
{       
  USART_Config();
  while(1)
        {       
                printf("hello windows 10!n");
                delay(5000000);
        }       
}


3、接下来进行编译并生成hex文件:





4、烧录程序:
(1)将电脑与单开发板通过ST-LINK线连接起来,并在keil中设置




















(2)编译、下载烧录





最终结果

打开野火调试助手:





当上位机给stm32发送“Stop,stm32”后,stm32停止发送:



三、C语言程序里全局变量、局部变量、堆、栈

变量存储区域和各个区域简介

  一个由c/C++编译的程序占用的内存分为以下几个部分:
1、栈区(stack)—— 由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 按内存地址由高到低方向生长,其最大大小由编译时确定,速度快,但自由性差,最大空间不大。
2、堆区(heap) —— 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式类似于链表。自由申请的空间,按内存地址由低到高方向生长,其大小由系统内存/虚拟内存上限决定,速度较慢,但自由性大,可用空间大。
3、全局区(静态区)(static)——全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后有系统释放。
4、文字常量区 ——存放局部变量或者全局变量的值,程序结束后由系统释放 。
5、程序代码区——存放函数体的二进制代码。
  注意:静态局部变量和静态全局变量
属于静态存储方式的量不一定就是静态变量。
例如:全局变量虽属于静态存储方式,但不一定是静态变量, 必须由 static加以定义后才能成为静态外部变量,或称静态全局变量。
把局部变量改变为静态变量后是改变了它的存储方式,即改变了它的生存期。
把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。
存储区域的内存地址

编写main.cpp文件:

#include
#include
#include

void before()
{

}

char g_buf[16];
char g_buf2[16];
char g_buf3[16];
char g_buf4[16];
char g_i_buf[]="123";
char g_i_buf2[]="123";
char g_i_buf3[]="123";

void after()
{

}

int main(int argc, char **argv)
{
        char l_buf[16];
        char l_buf2[16];
        char l_buf3[16];
        static char s_buf[16];
        static char s_buf2[16];
        static char s_buf3[16];
        char *p_buf;
        char *p_buf2;
        char *p_buf3;

        p_buf = (char *)malloc(sizeof(char) * 16);
        p_buf2 = (char *)malloc(sizeof(char) * 16);
        p_buf3 = (char *)malloc(sizeof(char) * 16);

        printf("g_buf: 0x%xn", g_buf);
        printf("g_buf2: 0x%xn", g_buf2);
        printf("g_buf3: 0x%xn", g_buf3);
        printf("g_buf4: 0x%xn", g_buf4);

        printf("g_i_buf: 0x%xn", g_i_buf);
        printf("g_i_buf2: 0x%xn", g_i_buf2);
        printf("g_i_buf3: 0x%xn", g_i_buf3);

        printf("l_buf: 0x%xn", l_buf);
        printf("l_buf2: 0x%xn", l_buf2);
        printf("l_buf3: 0x%xn", l_buf3);

        printf("s_buf: 0x%xn", s_buf);
        printf("s_buf2: 0x%xn", s_buf2);
        printf("s_buf3: 0x%xn", s_buf3);

        printf("p_buf: 0x%xn", p_buf);
        printf("p_buf2: 0x%xn", p_buf2);
        printf("p_buf3: 0x%xn", p_buf3);

        printf("before: 0x%xn", before);
        printf("after: 0x%xn", after);
        printf("main: 0x%xn", main);

        if (argc > 1)
        {
                strcpy(l_buf, argv[1]);
        }
        return 0;
}
用gcc编译过后执行





由结果可看出:




四、stm32的堆、栈、全局变量的分配地址

基于STM32分析C语言经编译后的分区情况

在Keil中针对stm32系统进行编程,调试变量,进行验证; 通过串口输出信息到上位机,进行验证。
1、 将上述二中的工程文件中的main.c文件改为:

   #include "stm32f10x.h"
#include "bsp_usart.h"


char global1[16];
char global2[16];
char global3[16];
       
int main(void)
{       
        char part1[16];
  char part2[16];
  char part3[16];


  USART_Config();


  printf("part1: 0x%pn", part1);
  printf("part2: 0x%pn", part2);
  printf("part3: 0x%pn", part3);
         
  printf("global1: 0x%pn", global1);
  printf("global2: 0x%pn", global2);
  printf("global3: 0x%pn", global3);
  while(1)
        {       
               
        }       
}
此程序分别在stm32中定义了全局变量和局部变量,并把它们的地址返回给上位机。
按上述方法进行烧录,结果如下:



part1、part2、part3为栈中的局部变量,地址逐渐减小。
global1、global2、global3为静态区中的全局变量,地址逐渐增加。
2、再将main.c文件进行以下修改:

#include "stm32f10x.h"
#include "bsp_usart.h"
#include


int main(void)
{       
  static char st1[16];
  static char st2[16];
  static char st3[16];
  char *p1;
  char *p2;
  char *p3;



  USART_Config();


  printf("st1: 0x%pn", st1);
  printf("st2: 0x%pn", st2);
  printf("st3: 0x%pn", st3);
         
  p1 = (char *)malloc(sizeof(char) * 16);
  p2 = (char *)malloc(sizeof(char) * 16);
  p3 = (char *)malloc(sizeof(char) * 16);
       
  printf("p1: 0x%pn", p1);
  printf("p2: 0x%pn", p2);
  printf("p3: 0x%pn", p3);
  while(1)
        {       
               
        }       
}
程序定义静态变量和指针,并返回它们的地址给上位机。
编译、烧录后结果如下:



st1、st2、st3都是静态变量,他们的地址依次增加。
p1、p2、p3是堆中的指针,他们的地址也是依次增加。
总结

keil V5环境下默认的内存配置如图





  ① 默认分配的ROM区域是0x8000000开始,大小是0x80000的一片区域,那么这篇区域是只读区域,不可修改,也就是存放的代码区和常量区。
② 默认分配的RAM区域是0x20000000开始,大小是0x10000的一片区域,这篇区域是可读写区域,存放的是静态区、栈区和堆区。
归纳如下:





举报

更多回帖

×
20
完善资料,
赚取积分