系列文章目录
提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:STM32点亮LED 和流水灯
kiel5下载ARM和c51教程:
复制打开https://blog.youkuaiyun.com/weixin_45352361/article/details/123448846?app_version=6.1.4&code=app_1562916241&csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22123448846%22%2C%22source%22%3A%22qq_67882143%22%7D&uLinkId=usr1mkqgl919blen&utm_source=app
文章目录
STM32/51单片机编程入门(点亮LED)
前言
`
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了STM32学习的基础内容。
提示:以下是本篇文章正文内容,下面案例可供参考
一、GPIO和串口
可以看到控制引脚的寄存器很多,引脚的模式也很多,对于初学者不是很友好,这些寄存器我们在下一节代码里会再拿出来分析,下一节我们会学习代码究竟怎么实现对这些寄存器的配置的,这里先知道一下GPIO的寄存器,每个IO端口都有7个寄存器来控制,分别是:配置模式的2个32位的端口配置寄存器GPIOx_CRL和GPIOx_CRH,2个32位的数据寄存器GPIOx_IDR和GPIOx_ODR,1个32 位的置位/复位寄存器GPIOx_BSRR,1个 16位的复位寄存器GPIOx_BRR;1个32 位的锁存寄存器GPIOx_LCKR。本节我们主要关注和用到的寄存器是CRL、CRH、BRR、BSRR寄存器。
本文我们要完成的是点亮LED,从最简单的角度去思考,我们需要做的就是让一个端口输出高电平或者低电平实现点亮或者关闭LED灯的效果,我们先来看一下硬件连接图:
二、51点亮一个LED
示例:学习51单片机的代码规律,编写一个点亮LED的代码。
我这里用的是keil4编写代码。
先打开proteus点击AT89c51开发板
连接好电路
接着打开kiel4
#include<reg51.h>
sbit led=P1^0;
int main()
{
led=0;
while(1);
}
可得点亮了LED
三、stm32流水灯
1.参数
GPIO_InitTypeDef GPIO_InitStructure;
开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE)
这里开启的是A串口(GPIOA)
配置参数
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP;
这是进行推挽输出
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_All;
定义串口,这里我定义的是全部串口。
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
配置频率为50MHZ
GPIO_Init(GPIOA,&GPIO_InitStructure);
赋值地址
GPIO_SetBits(GPIOA,GPIO_Pin_0);//输出高电平
GPIO_ResetBits(GPIOA,GPIO_Pin_0);//输出低电平
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);//清零置低
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET); //清零置高
四种输出方式
2.循环代码
在这里插入代码片while(1)
{
GPIO_Write(GPIOA,0x0001);
Delay_ms(500);
GPIO_Write(GPIOA,0x0002);
Delay_ms(500);
GPIO_Write(GPIOA,0x0008);
Delay_ms(500);
GPIO_Write(GPIOA,0x0020);
Delay_ms(500);
GPIO_Write(GPIOA,0x0080);
Delay_ms(500);
}
3.总代码
#include "stm32f10x.h"
#include "Delay.h"
int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//¿ªÆôʱÖÓ
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_All;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//GPIO_SetBits(GPIOA,GPIO_Pin_0);//Êä³ö¸ßµãƽ
//GPIO_ResetBits(GPIOA,GPIO_Pin_0)Êä³öµÍµçƽ
//GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);ÇåÁãÖõÍ
//GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET); ÇåÁãÖøß
while(1)
{
GPIO_Write(GPIOA,0x0001);
Delay_ms(500);
GPIO_Write(GPIOA,0x0002);
Delay_ms(500);
GPIO_Write(GPIOA,0x0008);
Delay_ms(500);
GPIO_Write(GPIOA,0x0020);
Delay_ms(500);
GPIO_Write(GPIOA,0x0080);
Delay_ms(500);
}
}
四、效果图
电路接法:
流水灯
利用寄存器点灯
利用GPIOA,GPIOAB,GPIOC实现流水灯
根据stm32f103使用手册可以看出GOIOABC的总线时AHB,查看寄存器的起始地址可以找出AHB的时钟寄存器起始地址为0x4002 1000-0x4002 13FF
查看地址可以开起时钟。
因为GPIOABC的外设为APB2偏移地址为0x18
则时钟开启
*(volatile unsinged int*)0x40021018//AHB外设+APB2偏移地址:(0x4002 1000)+1018
GPIOA |=(1<<2)//因为A口在位2所以向左移2位到A口
再查看手册查找外设基地址
这里举例GPIOA为例
配置GPIOA的输出
#define GPIOA_CRL *(volatile unsigned int *)40010800//GPIOA口的基地址40010800+端口配置低寄存器0x00
GPIOA_CRL|=1<<(4*3)//配置MODE3就是AP3口
再查找手册输出数据寄存器
#define GPIOA_ODR *(volatile unsigned int *)40010800+0x0C//GPIOA的地址+端口输出寄存器
GPIOA_ODR &= ~(1<<3);
总代码
#include "stm32f10x.h"
#include "Delay.h"
#define GPIOA_CLK (*(volatile unsigned int *)(0x40021000 + 0x18))
#define GPIOB_CLK (*(volatile unsigned int *)(0x40021000 + 0x18))
#define GPIOC_CLK (*(volatile unsigned int *)(0x40021000 + 0x18))
#define GPIOA_CRH (*(volatile unsigned int *)(0x40010800 + 0x00))
#define GPIOA_ODR (*(volatile unsigned int *)(0x40010800 + 0x0C))
#define GPIOB_CRH (*(volatile unsigned int *)(0x40010C00 + 0x00))
#define GPIOB_ODR (*(volatile unsigned int *)(0x40010C00 + 0x0C))
#define GPIOC_CRH (*(volatile unsigned int *)(0x40011000 + 0x04))
#define GPIOC_ODR (*(volatile unsigned int *)(0x40011000 + 0x0C))//
int main(void)
{
GPIOA_CLK |= (1<<2);
GPIOA_CRH |= (1<<(4*3));
GPIOB_CLK |= (1<<3);
GPIOB_CRH |= (1<<(4*7));
GPIOC_CLK |= (1<<4);
GPIOC_CRH |= (1<<(4*5));
GPIOA_ODR |= (1<<3);
GPIOB_ODR |= (1<<7);
GPIOC_ODR |= (1<<13);
while(1)
{
GPIOA_ODR &= ~(1<<3);
Delay_ms(500);
GPIOA_ODR |= (1<<3);
Delay_ms(500);
GPIOB_ODR &= ~(1<<7);
Delay_ms(500);
GPIOB_ODR |= (1<<7);
Delay_ms(500);
GPIOC_ODR &= ~(1<<13);
Delay_ms(500);
GPIOC_ODR |= (1<<13);
Delay_ms(500);
}
}
QQ视频20230924115534
总结
本节我们实现了单片机控制LED灯,学会了对引脚最基础的配置和使用,可以看到这一节我们并没有控制时间,只是运用了软件for循环做的延时函数,相信大家一定不是很喜欢,下一节会讲STM32滴答时钟配置出我们的延时函数!做出精确的延时控制us、ms、s级的延时。局部变量: 栈区
局部静态变量:静态区
全局变量: 静态区的常量区
全局静态变量:静态区
问题三:在进行C/C++编程时,需要程序员对内存的了解比较精准。经常需要操作的内存可分为以下几个类别:
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码
因为STM32点亮LED需要寄存器,而51不要
问题四: 寄存器变量就是把使用高频繁的一个变量 系统会将内存中高频繁的变量放入到寄存器中。
使用registe 可以强制把一个变量放到寄存器中。
{
......
}
for(register int i=1;i<=1000000;i++)
{
......
}
在第一个 for 循环中,变量 i 存储在内存中,cpu 每次要从内存中取出变量 i,这样 cpu 就要来回读取10000次,只是很低效的。
而在第二个 for 循环中,cpu每次都会直接去寄存器上读取变量i,而不用再去内存读取,因此,代码的效率也会大大提高。
volatile是防止编译器优化,如果是高频繁的变量编译器会自动将变量放到寄存器中,但是有的变量需要实时更新不能间断,放到寄存器中会隔一段时间再去获取变量,导致变量的值不在准确。
在这里插入代码片
int main()
{
volatile int i = 10;
int a = i;
printf("%d", i);
//下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道
__asm
{
mov dword ptr[ebp - 4], 20h
}
int b = i;
printf("i=%d", b);
return 0;
}
