stm32学习笔记

时光怂恿深爱的人放手 提交于 2020-02-08 11:53:06

一、GPIO的控制

在这里插入图片描述

要求:软件仿真,根据高8位pin输入电平,低8位输出相应的电平。写出main.c代码。

编程思路:

1、配置模式,P0.0~P0.7推挽输出,P0.8-P0.15浮空输入。
2、输入状态反映到对应引脚输出。

方式1:直接读、写输入\输出寄存器。

在这里插入图片描述

代码:

#include "stm32f10x.h"

int main(void)
{
	//1、配置模式,P0.0~P0.7输出,P0.8-P0.15输入。
	GPIOA->CRL = 0x33333333;//0x00000033前面的零是可以省略的
	GPIOA->CRH = 0x44444444;
	//2、输入状态反映到对应引脚输出。
	while(1)
	{
		if((GPIOA->IDR&0x0100) == 0x0100) 	GPIOA->ODR |= 0x0001;
		else 																GPIOA->ODR &= ~(0x0001);
		
		if((GPIOA->IDR&0x0200) == 0x0200)		GPIOA->ODR |= 0x0002;
		else 																GPIOA->ODR &= ~(0x0002);
		
		if((GPIOA->IDR&0x0400) == 0x0400)		GPIOA->ODR |= 0x0004;
		else 																GPIOA->ODR &= ~(0x0004);
		
		if((GPIOA->IDR&0x0800) == 0x0800)		GPIOA->ODR |= 0x0008;
		else 																GPIOA->ODR &= ~(0x0008);
		if((GPIOA->IDR&0x1000) == 0x1000)		GPIOA->ODR |= 0x0010;
		else 																GPIOA->ODR &= ~(0x0010);
		if((GPIOA->IDR&0x2000) == 0x2000)		GPIOA->ODR |= 0x0020;
		else 																GPIOA->ODR &= ~(0x0020);
		if((GPIOA->IDR&0x4000) == 0x4000)		GPIOA->ODR |= 0x0040;
		else 																GPIOA->ODR &= ~(0x0040);
		if((GPIOA->IDR&0x8000) == 0x8000)		GPIOA->ODR |= 0x0080;
		else 																GPIOA->ODR &= ~(0x0080);
	}
	
	return 0;
}

方式2:通过位设置\清除寄存器 和 位清除寄存器控制。

位设置\清除寄存器BSRR,位清除寄存器BRR

BSRR位设置\清除寄存器

高16位清除为0,低16位设置为1.
在这里插入图片描述

BRR位清除寄存器

在这里插入图片描述

代码:

#include "stm32f10x.h"

int main(void)
{
	GPIOA->CRL = 0x33333333;//0x00000033前面的零是可以省略的
	GPIOA->CRH = 0x44444444;
	while(1)
	{
		if((GPIOA->IDR&0x0100) == 0x0100) 	GPIOA->BSRR |= 0x0001;
		else 																GPIOA->BRR |= 0x0001;
		
		if((GPIOA->IDR&0x0200) == 0x0200)		GPIOA->BSRR |= 0x0002;
		else 																GPIOA->BRR |= 0x0002;
		
		if((GPIOA->IDR&0x0400) == 0x0400)		GPIOA->BSRR |= 0x0004;
		else 																GPIOA->BRR |= 0x0004;
		
		if((GPIOA->IDR&0x0800) == 0x0800)		GPIOA->BSRR |= 0x0008;
		else 																GPIOA->BRR |= 0x0008;
	}
	return 0;
}

方式3 位绑定

位绑定的定义:

位段
CortexTM-M3存储器映像包括两个位段(bit-band)区。这两个位段区将别名存储器区中的每个字映射到位段存储器区的一个位,在别名存储区写入-一个字具有对位段区的目标位执行读-改-写操作的相同效果。
在STM32F10xxx里,外设寄存器和SRAM都被映射到-一个 位段区里,这允许执行单一的位段的写和读操作。
下面的映射公式给出了别名区中的每个字是如何对应位带区的相应位的:
bit_ word_addr= bit_ band_base + (byte_offset * 32) + (bit_number * 4)
其中:
bit_ word addr 是别名存储器区中字的地址,它映射到某个目标位。
bit
band_ base 是别名区的起始地址。
byte
offset是包含 目标位的字节在位段里的偏移地址。
bit_ number是目标位所在位置(0-31)

个人理解

绑定地址 = 位绑定基地址+目标位偏移量
(不过这里的位绑定基地址是芯片制造商定好的地址。)

bit_ band_base 位绑定基地址
SRAM 的位绑定基地址:0x2200 0000
片上外设的位绑定的基地址:0x4200 0000
byte_ offset目标位偏移量
GPIOA->ODR为例子:
GPIOA->ODR地址 
= GPIOA_BASE + 0x0C
= APB2PERIPH_BASE + 0x0800 + 0x0C 
= PERIPH_BASE+0x10000+0x0800+0x0C 

而 GPIOA->ODR 的目标位偏移量: byte_ offset =  PERIPH_BASE+0x10000+0x0800+0x0C  - PERIPH_BASE = 0x00010800C

以计算GPIOA->ODR 的 第n位绑定地址为例子:
bit_word_addr = 片上外设别名地址 + GPIOA->ODR相对GPIOA的偏移地址 + n * 4
bit_word_addr = 0x42000000 + 0x00010800C + n * 4

相关寄存器:
在这里插入图片描述
学习截图:
在这里插入图片描述

代码:

注意IDR绑定的位是第8位,不是第0位!!!

#include "stm32f10x.h"

int main(void)
{
	//配置GPIO
	GPIOA->CRL = 0x33333333;
	GPIOA->CRH = 0x44444444;
	
	//GPIOA->ODR地址 = (int*)(GPIOA_BASE+0x0C+0*4)
	//= (int*)((APB2PERIPH_BASE + 0x0800)+0x0C+0*4)
	//= (int*)(((PERIPH_BASE + 0x10000) + 0x0800)+0x0C+0*4)
	//= (int*)(((0x40000000 + 0x10000) + 0x0800)+0x0C+0*4)
	//= (int*)(0x4001080C)
	int *PAO0 = (int*)(0x42000000 + (0x4001080C - 0x40000000)*32 +0*4);
	
	//GPIOA->IDR地址 = GPIOA_BASE + 0x08
	//= (int*)((APB2PERIPH_BASE + 0x0800)+0x08+0*4)
	//= (int*)(((PERIPH_BASE + 0x10000) + 0x0800)+0x08+0*4)
	//= (int*)(((0x40000000 + 0x10000) + 0x0800)+0x08+0*4)
	//= (int*)(0x40010808)
	int *PAI0 = (int*)(0x42000000 + (0x40010808 - 0x40000000)*32 +8*4);
	while(1)
	{
		//位绑定
		if(*PAI0 == 1) 	*PAO0 = 1;
		else 						*PAO0 = 0;
		
		//下面与上面的位绑定作对比
		if((GPIOA->IDR&0x0200) == 0x0200)		GPIOA->BSRR |= 0x0002;
		else 																GPIOA->BRR |= 0x0002;
	}
	return 0;
}

公式代码:

(利用位的与运算保留 高位 或 低位)
(注意输入是高位,是8-15)

#include "stm32f10x.h"

#define GPIOA_ODR (GPIOA_BASE+0x0C)
#define GPIOA_IDR (GPIOA_BASE+0x08)

#define BitBand(Addr,bitNum) *((volatile unsigned long *)((Addr&0xf0000000)+0x2000000+((Addr&0xfffff)<<5)+(bitNum<<2)))
#define PAout(n) BitBand(GPIOA_ODR,n)
#define PAin(n)  BitBand(GPIOA_IDR,n)
int main(void)
{
	GPIOA->CRL = 0x33333333;//0x00000033前面的零是可以省略的
	GPIOA->CRH = 0x44444444;

	while(1)
	{
		//位绑定
		if(PAin(8) == 1) 	
			PAout(0) = 1;
		else 						
			PAout(0) = 0;
		
		if(PAin(9) == 1) 	
			PAout(1) = 1;
		else 						
			PAout(1) = 0;					
		
		if(PAin(10) == 1)	
			PAout(2) = 1;
		else 							
			PAout(2) = 0;
	}
	return 0;
}


二、通信。

通信就是指微处理机与外设交换数据的过程。

通信基本方式:

方式一:并行通信(多车道,多窗口)。

数据的各数位同时传输。

方式二:串行通信(单车道,单窗口)。

数据按位一位位的传输。

串行通信种类:

种类一:单工。

只允许一方发给另一方。
在这里插入图片描述

种类二:半双工。(对讲机)

双方可以相互通信,但是不能同时发送、同时接收。
在这里插入图片描述

种类三:全双工。

双方可以同时相互通信。
在这里插入图片描述

串行通信的标准:

在这里插入图片描述

标准1:UART 异步通信

异步通信协议:

异步通信协议需要定义5个方面的内容:
1、起始位。
2、数据位。
3、奇偶校验位。
4、停止位。
5、波特率(通信速度,因为通信双方没有时钟)。
在这里插入图片描述

异步通信应用场合:

1.芯片间的近距离通信。
在这里插入图片描述
2.与PC机的通信。
在这里插入图片描述
3.模块之间的远距离通信。
在这里插入图片描述
用较高的电压差表示逻辑“0”、“1”

stm32f10x USART框图

在这里插入图片描述

stm32f10x波特率的计算

设波特率位9600,根据公式
在这里插入图片描述
9600 = 7210001000/(16*U)
分频值U = 72 * 1000 * 1000/(9600 * 16) = 468.75
DIV_Mantissa = 468 = 0x1d4;
DIV_Fraction = 16 * 0.75 = 12 = 0x0C

仿真代码

晶振调为8Mhz
在这里插入图片描述
main.c 寄存器代码

#include "stm32f10x.h"

typedef unsigned          char u8;
typedef unsigned short     int u16;
typedef unsigned           int u32;



//系统时钟初始化函数
//pll:选择的倍频数,从2开始,最大值为16		 
void Stm32_Clock_Init(void)
{
	/*----------使用外部RC晶振----------*/
	RCC_DeInit() ;
	//初始化为缺省值
	RCC_HSEConfig(RCC_HSE_ON); //使 能外部的高速时钟
	while (RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET); //等 待外部高速时钟使能就绪
	
	RCC_HCLKConfig (RCC_SYSCLK_Div1) ;
	//HCLK = SYSCLK
	RCC_PCLK2Config(RCC_HCLK_Div1) ;
	//PCLK2 =HCLK
	RCC_PCLK1Config(RCC_HCLK_Div2) ;
	//PCLK1 = HCLR/2
	RCC_PLLConfig (RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //PLLCLK = 8MHZ * 9 =72MHZ
	RCC_PLLCmd(ENABLE) ;
	//Enable PLLCLK
	while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //Wait till PLLCLK is ready
	RCC_SYSCLKConfig (RCC_SYSCLKSource_PLLCLK) ;
	//Select PLL as system clock
	while (RCC_GetSYSCLKSource () !=0x08) ;
	//wait till PLL is used as system clock source
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1,ENABLE);//使能外设
}

int main(void)
{
	
	u16 M,F,BRR;
	float Div;
	u32 Bound;
	u8 data = 'A';
	u8 i ;
	Stm32_Clock_Init();
	
	GPIOA->CRH&=0XFFFFF00F;//IO状态设置
	GPIOA->CRH|=0X000008B0;//IO状态设置,1000复用推挽输出
	
	//USART设置:UE位使能USART1,M位来定义字长,停止位,波特率,TE发送使能位、
	//UE:USART1->CR1 的13位
	
	USART1->CR1 |= (1<<13);//UE使能
	USART1->CR1 &= ~(1<<12);//M字长,8bit
	
	
	//或者USART1->CR2 &= ~(3<<12);或者USART1->CR2 &= ~((1<<13)|(1<<12));
	USART1->CR2 &= ~(3<<12);//配置停止位,1bit
	USART1->CR1 |= (1<<3);//TE位发送,使能发送
	
	//波特率设置
	Bound = 9600;
	Div = (float)(72*1000*1000)/(Bound*16);//整数运算要强制转化成浮点数才能编变成浮点数。
	M = Div;
	F = (Div - M)*16;
	BRR = (M<<4|F);//不是 0x10+0x01 = 0x11,而是0x10|0x01 = 0x11;
	USART1->BRR = BRR;
	
	//发送字符'A'及其后面的20个字符到USART的TXD
	for(i=0;i<20;i++)
	{
		USART1->DR = data;
		data++;
		while((USART1->SR & (1<<6)) == 0);//依据SR-TC标志位,判断是否发送完毕。1表示发送完毕,0表示未发送完毕。
	}

	
	return 0;
}

在这里插入图片描述
main.c 库函数代码

#include "stm32f10x.h"

typedef unsigned          char u8;
typedef unsigned short     int u16;
typedef unsigned           int u32;

//系统时钟初始化函数
//pll:选择的倍频数,从2开始,最大值为16		 
void Stm32_Clock_Init(void)
{
	/*----------使用外部RC晶振----------*/
	RCC_DeInit() ;
	//初始化为缺省值
	RCC_HSEConfig(RCC_HSE_ON); //使 能外部的高速时钟
	while (RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET); //等 待外部高速时钟使能就绪
	
	RCC_HCLKConfig (RCC_SYSCLK_Div1) ;
	//HCLK = SYSCLK
	RCC_PCLK2Config(RCC_HCLK_Div1) ;
	//PCLK2 =HCLK
	RCC_PCLK1Config(RCC_HCLK_Div2) ;
	//PCLK1 = HCLR/2
	RCC_PLLConfig (RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //PLLCLK = 8MHZ * 9 =72MHZ
	RCC_PLLCmd(ENABLE) ;
	//Enable PLLCLK
	while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //Wait till PLLCLK is ready
	RCC_SYSCLKConfig (RCC_SYSCLKSource_PLLCLK) ;
	//Select PLL as system clock
	while (RCC_GetSYSCLKSource () !=0x08) ;
	//wait till PLL is used as system clock source

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1,ENABLE);

}

void GPIO_Config()
{
	GPIOA->CRH&=0XFFFFF00F;//IO状态设置
	GPIOA->CRH|=0X000008B0;//IO状态设置,1000复用推挽输出
}

void USART_Config(u32 BaudRate)
{
	USART_InitTypeDef USARTST;
	USARTST.USART_WordLength = USART_WordLength_9b;
	USARTST.USART_StopBits = USART_StopBits_1;
	USARTST.USART_Parity = USART_Parity_No;
	USARTST.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USARTST.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;
	USARTST.USART_BaudRate = BaudRate;
	USART_Init(USART1,&USARTST);//配置USART1
	USART_Cmd(USART1,ENABLE);//使能USART1
}

int main(void)
{
	u8 data = 'A';
	u8 i ;
	Stm32_Clock_Init();
	GPIO_Config();
	USART_Config(9600);

	for(i=0;i<20;i++)
	{
		USART_SendData(USART1,data);
		data++;
		while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);//判断标志位。
	}
	return 0;
}

USART库函数:
在这里插入图片描述
USART_init配置串口:
在这里插入图片描述
在这里插入图片描述
USART_Cmd使能串口:
在这里插入图片描述
USART_GetFlagStatus判断标志位:
在这里插入图片描述
RESET 和 SET 的值:
在这里插入图片描述

三、stm32库函数

stm32库函数是由st公司提供的API,开发者可以使用库函数来配置寄存器,使开发者摆脱最底层的寄存器操作。有着开发快速,易于阅读的优点。
它向下操作寄存器,向上提供操作配置寄存器的接口。
SMSIS(Contex MicroController Software Interface Standard)标准,ARM公司与其他外设厂家建立的标准。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!