嵌入式作业(六):stem32串口通信

0 作业要求

1.学习相关资料,说明基于寄存器与基于固件库的stm32 LED流水灯例子的编程方式有什么差异。

2.学习和阅读“零死角玩转STM32F103–指南者”文档中的第20、21章内容,完成STM32的USART窗口通讯程序,要求:
(1)设置波特率为115200,1位停止位,无校验位。

(2)STM32系统给上位机(win10)连续发送“hello windows!”,上位机接收程序可以使用“串口调试助手“,也可自己编程。

(3)当上位机给stm32发送“Stop,stm32”后,stm32停止发送。

3.重温C语言程序里全局变量、局部变量、堆、栈等概念,并在ubuntu系统中编程,输出信息进行验证;

4.重温C语言程序里全局变量、局部变量、堆、栈等概念,在Keil中针对stm32系统进行编程,调试变量,进行验证; 通过串口输出信息到上位机,进行验证。

并归纳出stm32的堆、栈、全局变量的分配地址,与ARM教材中的地址分配进行对比。

1 基于寄存器与基于固件库编程方式的差异

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
基于硬件库:
由于stm32有非常多的寄存器,对于我们来说,想要记住每个寄存器的端口引脚显然不可能,所以为了方便编程或者初学者的学习,stem官方为每款芯片都编写了库文件(标准库),比如上图的里stm32F1xx、led…在这些 .c .h文件中,包括一些常用量的宏定义,把一些外设也通过结构体变量封装起来,如GPIO口时钟等。所以我们只需要配置结构体变量成员就可以修改外设的配置寄存器,从而选择不同的功能。如第一幅图所示,硬件库编程,官方已经通过函数和宏定义,已经把硬件的寄存器封装了起来,我们甚至不需要对硬件的结构进行了解就可以开始编程。

基于寄存器
相比于硬件库编程,基于寄存器的编程是直接在寄存器上进行编译,相当于自己将库函数层到寄存器层的函数写一遍。但stm32的寄存器数量很多,如此多的寄存器根本无法全部记忆,开发时需要经常的翻查芯片的数据手册,所以直接操作寄存器就变得非常的费力了。但直接操作寄存器,这样更接近原理,可以熟悉芯片的结构,有易于我们的学习。

总结:
基于硬件库的方式操作更为简单,能很快的写出一个程序,但是寄存器虽然复杂,但它的可移植性强,在stm上学会了使用寄存器编程,在其他芯片上也可以。但是对于初学者,显然是硬件库的方式要简单一点,但在时间充裕时,也可以学习寄存器编程。

2 STM32的USART窗口通讯程序

编写keil程序

main函数:

 int main(void)
 { 
    
    
  char stop[]={'S','t','o','p',',','s','t','m','3','2'};
 	u16 t;  
	u16 len;	
	u16 times=0;
	
	delay_init();	    	 //延时函数初始化  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组
	uart_init(115200);	     //串口初始化为115200
	KEY_Init();              //初始化与案件连接的硬件端口
	
 	while(i)
	{
		if(USART_RX_STA&0x8000)
		{		   
			len=USART_RX_STA&0x3fff;         //收到的长度
		
			for(t=0;t<len&&USART_RX_BUF[t]==stop[t];i++,t++)
				{if (i>=10) {i=0;break;}}    //判断是否停止
				
				
			for(t=0;t<len;t++)
			{ 
				USART_SendData(USART1, USART_RX_BUF[t]);//串口1发送数据
				while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
			}
			printf("\r\n\r\n");
			USART_RX_STA=0;
		}
		
		else
		{
			times++;
			if(times%300==0)
			{
				printf("\r\n张xx的嵌入式串口实验\r\n");
	
			}
			if(times%100==0){printf("hello windows!\n");i=1;}  

			delay_ms(10);   
		}
	}	 
 }

在这里插入图片描述
以上是需要的其它函数,除了main函数,其它函数都是可以直接使用例程。

硬件连接

硬件原理图:
在这里插入图片描述

实际连线
下载器连接:
在这里插入图片描述

串口连接:

在这里插入图片描述
在这里插入图片描述

烧录程序

(1)编译运行
在这里插入图片描述

在这里插入图片描述
(2)配置

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
(3)下载查询到核心板
在这里插入图片描述
在这里插入图片描述

结果

在这里插入图片描述

3 重温C语言程序里全局变量、局部变量、堆、栈等概念

C程序的内存分配

参考博客(C/C++程序内存的各种变量存储区域和各个区域详解


  • 通常是用于那些在编译期间就能确定存储大小的变量的存储区,用于在函数作用域内创建,在离开作用域后自动销毁的变量的存储区。通常是局部变量,函数参数等的存储区。他的存储空间是连续的,两个紧密挨着定义的局部变量,他们的存储空间也是紧挨着的。栈的大小是有限的,通常Visual C++编译器的默认栈的大小为1MB,所以不要定义int a[1000000]这样的超大数组。
    内存地址由高到低方向生长。


  • 通常是用于那些在编译期间不能确定存储大小的变量的存储区,它的存储空间是不连续的,一般由malloc(或new)函数来分配内存块,并且需要用free(delete)函数释放内存。如果程序员没有释放掉,那么就会出现常说的内存泄漏问题。需要注意的是,两个紧挨着定义的指针变量,所指向的malloc出来的两块内存并不一定的是紧挨着的,所以会产生内存碎片。另外需要注意的一点是,堆的大小几乎不受限制,理论上每个程序最大可达4GB。
    自由申请的空间,按内存地址由低到高方向生长。

  • 全局变量(Global Variable)
    在所有函数外部定义的变量称为全局变量(Global Variable),它的作用域默认是整个程序,也就是所有的源文件,包括 .c 和 .h 文件。

  • 局部变量(Local Variable)
    定义在函数内部的变量称为局部变量(Local Variable),它的作用域仅限于函数内部, 离开该函数后就是无效的,再使用就会报错。
    (1)在 main 函数中定义的变量也是局部变量,只能在 main 函数中使用;同时,main 函数中也不能使用其它函数中定义的变量。main 函数也是一个函数,与其它函数地位平等。
    (2) 形参变量、在函数体内定义的变量都是局部变量。实参给形参传值的过程也就是给局部变量赋值的过程。
    (3) 可以在不同的函数中使用相同的变量名,它们表示不同的数据,分配不同的内存,互不干扰,也不会发生混淆。
    (4)在语句块中也可定义变量,它的作用域只限于当前语句块。

  • 全局区(静态区)(static)
    全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量、未初始化的静态变量在相邻的另一块区域。当程序结束后,变量由系统释放 。

其它:

  • 全局/静态存储区
    和“栈”一样,通常是用于那些在编译期间就能确定存储大小的变量的存储区,但它用于的是在整个程序运行期间都可见的全局变量和静态变量。

  • 常量存储区
    和“全局/静态存储区”一样,通常是用于那些在编译期间就能确定存储大小的常量的存储区,并且在程序运行期间,存储区内的常量是全局可见的。这是一块比较特殊的存储去,他们里面存放的是常量,不允许被修改。

  • 关于数据储存的地址
    静态变量:地址由高到低方向生长。
    全局常量:地址由低到高方向生长。
    函数:地址由低到高方向生长。

验证程序

参考博客(C语言中,局部变量、全局变量、静态变量、堆、栈的内存地址

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 //验证函数地址
void before()
{ 
}
void after()
{
}
//验证全局变量的地址 
char g_buf1[16];
char g_buf2[16];
char g_buf3[16];
char g_buf4[16];
char g_i_buf1[]="123";
char g_i_buf2[]="123";
char g_i_buf3[]="123";
 
int main(int argc, char **argv)
{
//验证局部变量的地址  栈区
        char l_buf1[16];
        char l_buf2[16];
        char l_buf3[16];
//验证静态变量的地址
        static char s_buf1[16];
        static char s_buf2[16];
        static char s_buf3[16];
//验证动态分配地址 堆区 
        char *p_buf1;
        char *p_buf2;
        char *p_buf3;
        p_buf1 = (char *)malloc(sizeof(char) * 16);
        p_buf2 = (char *)malloc(sizeof(char) * 16);
        p_buf3 = (char *)malloc(sizeof(char) * 16);
        printf("全局变量的地址 :\n");
        printf("g_buf1: 0x%x\n", g_buf1);
        printf("g_buf2: 0x%x\n", g_buf2);
        printf("g_buf3: 0x%x\n", g_buf3);
        printf("g_buf4: 0x%x\n", g_buf4);
  
        printf("g_i_buf1: 0x%x\n", g_i_buf1);
        printf("g_i_buf2: 0x%x\n", g_i_buf2);
        printf("g_i_buf3: 0x%x\n", g_i_buf3);
        printf("栈区/局部变量的地址 :\n"); 
        printf("l_buf1: 0x%x\n", l_buf1);
        printf("l_buf2: 0x%x\n", l_buf2);
        printf("l_buf3: 0x%x\n", l_buf3);
        printf("静态变量的地址 :\n"); 
        printf("s_buf1: 0x%x\n", s_buf1);
        printf("s_buf2: 0x%x\n", s_buf2);
        printf("s_buf3: 0x%x\n", s_buf3);
        printf("动态变量/堆区的地址 :\n");        
        printf("p_buf1: 0x%x\n", p_buf1);
        printf("p_buf2: 0x%x\n", p_buf2);
        printf("p_buf3: 0x%x\n", p_buf3);
        printf("函数的地址 :\n");
        printf("before: 0x%x\n", before);
        printf("after: 0x%x\n", after);
        printf("main: 0x%x\n", main);
 
        if (argc > 1)
        {
                strcpy(l_buf, argv[1]);
        }
        return 0;
}

Ubuntu上运行

vim  a.c
gcc a.c
./a.out

在这里插入图片描述

stm32开发板上运行

代码需要与串口通信的程序相结合
在keil中,上面的代码的赋值会出错,所以不能用以上的代码
堆区:

#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include <stdlib.h>
int i=1;
int k1=1;
int k2=1;
int k3=1;
int main(void)
 { 
 int *p1=malloc(4);
  int *p2=malloc(4); 
  int *p3=malloc(4);
  free(p1);
  free(p2);
  free(p3);
	 
  char a1[16];
  char a2[16];
  char a3[16];
	 
  static int m1=2;
  static int m2=2;
  static int m3=2;
  char stop[]={'S','t','o','p',',','s','t','m','3','2'};
 	u16 t;  
	u16 len;	
	u16 times=0;
	free(p1);
  free(p2);
	delay_init();	    	 //ÑÓʱº¯Êý³õʼ»¯	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //ÉèÖÃNVICÖжϷÖ×é2:2λÇÀÕ¼ÓÅÏȼ¶£¬2λÏìÓ¦ÓÅÏȼ¶
	uart_init(115200);	 //´®¿Ú³õʼ»¯Îª115200
 	LED_Init();			     //LED¶Ë¿Ú³õʼ»¯
	KEY_Init();          //³õʼ»¯Óë°´¼üÁ¬½ÓµÄÓ²¼þ½Ó¿Ú
	
 	while()             //while函数里是printf函数,和上面几乎一致,
 	{
 	......
 	}
 	

在这里插入图片描述

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值