STM32学习笔记

目录

  • C语言语法
    • C语言枚举定义
    • STM32寄存器地址的理解
    • 含参数的宏定义
  • 寄存器与外设的理解
  • #include"stm32f10x.h"
  • GPIO输出
  • 位带操作
  • 系统时钟配置
  • EXTI中断/事件
  • Systick(系统定时器)
  • USART
  • DMA
  • 存储器介绍

C语言语法

C语言枚举定义

//里面的变量值会自动加一,且注意每个变量后是逗号,最后一个变量后没有逗号

//GPIO_Speed_10MHz相当于一个常量可以直接赋值给int,char或者自定义类型的变量
如:int a=GPIO_Speed_10MHz;

//但是如果定义了一个枚举变量
如:GPIOSpeed_TypeDef a;
此时的a就于int定义的变量一样里面是一个不确定的值,但是给其赋值只能赋枚举里的常量值
typedef enum
{ 
  GPIO_Speed_10MHz = 1,         // 10MHZ        (01)b
  GPIO_Speed_2MHz,              // 2MHZ         (10)b
  GPIO_Speed_50MHz              // 50MHZ        (11)b
}GPIOSpeed_TypeDef;

STM32寄存器地址的理解

在计算机中,一个地址,代表一个字节(1Byte)

比如:地址0x0000 0000就占一个字节,

地址0x0000 0001也占一个字节,

地址0x0000 0002也占一个字节,

地址0x0000 000F也占一个字节,

也就是说:

0x0000 0000 - 0x0000 000F总共有16个地址,占16个字节。
STM32是32位的也就是每个完整的寄存器对应着4Byte的地址,即像GPIO的IDR和ODR两个相邻的寄存器的地址间隔是0x04。

含参数的宏定义

#define LED_B(a) if(a) \\ (\)连接符号后面不能带任何东西
						GPIO_SetBits(LED_G_GPIO_PORT,LED_B_GPIO_PIN); \
				 else   GPIO_ResetBits(LED_G_GPIO_PORT,LED_B_GPIO_PIN);

寄存器与外设的理解

ARM将32位4G内存地址中的一部分起别名方便操作,这些命名的部分就是寄存器(所以寄存器实际上就对应一段内存地址),如GPIO的寄存器(包含了GOIO的时钟,赋值,输入输出配置等寄存器),然后通指定的寄存器对GPIO外设进行操作。

#include"stm32f10x.h"

#include"stm32f10x.h"里面包含了"stm32f10x_conf.h",这样每次main函数只用引用"stm32f10x.h"就可以把各种外设的头文件也引入

GPIO输出

推挽输出
1、可以输出高低电平,用于连接数字器件,高电平由VDD决定,低电平由
VSS决定。
2、推挽结构指两个三极管受两路互补的信号控制,总是在一个导通的时候
另外一个截止,优点开关效率效率高,电流大,驱动能力强。
3、输出高电平时,电流输出到负载,叫灌电流,可以理解成推,输出低电
平时,负载电流流向芯片,叫拉电流,即挽。

开漏输出
1、只能输出低电平,不能输出高电平。
2、如果要输出高电平,则需要外接上拉。
3、开漏输出具有“线与”功能,一个为低,全部为低,多用于I2C和
SMBUS总线

位带操作

//GPIOB_ODR_Addr寄存器地址,n要操作的是寄存器的第几位
#define GPIOB_ODR_Addr        (GPIOB_BASE+0X0C)
#define PBout(n)              *(unsigned int *)((GPIOB_ODR_Addr & 0xf0000000)+0x02000000+ ((GPIOB_ODR_Addr & 0x00ffffff)<<5)+(n<<2))

系统时钟配置

STM32学习笔记_第1张图片
红色为默认的时钟配置路线:通过高速外部时钟时钟(8M)经过PLL9倍频后(72M),选择输出的PLLCLK时钟(72M)为系统时钟对后续的外设等时钟进行配置。(最多可以通过自定义超频到128M)
APB1为低速总线时钟为二分频36M
AP2为高速总线时钟为一分频72M

EXTI中断/事件

NVIC 是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。

EXTI外部中断/事件控制器,上面主要包括GPIOA-G的中断
STM32学习笔记_第2张图片

产生中断目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的。而产生事件线的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输,属于硬件级的。

中断属于复用功能,实际上EXTI的配置是在AFIO上
STM32学习笔记_第3张图片

配置按键的输入中断:
EXTI0-15对应的就是GPIOA-G的0-15号引脚
NVIC里要设定中断优先级并且使能总中断
初始化中断端口(这里要配置GPIO的输出)
EXTI(主要是GPIO的中断)设置中断线即选择几号端口(0-15),使能中断

static void EXTI_NVIC_Config(void)
{
	NVIC_InitTypeDef NVIC_InitStruct;
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);//设置中断优先级分组
	
	NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;//中断源
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;//主优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;//子优先级
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;//开总中断使能
	NVIC_Init(&NVIC_InitStruct);
}

void EXTI_Key_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	EXTI_InitTypeDef EXTI_InitStruct;
	
	//配置中断优先级,开总中断
	EXTI_NVIC_Config();
	
	//初始化GPIO
	RCC_APB2PeriphClockCmd(KEY1_PIN_GPIO_CLK, ENABLE);	
	GPIO_InitStruct.GPIO_Pin = KEY1_PIN_GPIO_PIN;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;	
	GPIO_Init(KEY1_PIN_GPIO_PORT, &GPIO_InitStruct);
	
	//初始化EXTI,选择输入线,即GPIOX的几号端口。(这里配置用哪个端口是通过AFIO寄存器)
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);//配置了GPIOA(0端口)
	
	EXTI_InitStruct.EXTI_Line = EXTI_Line0;//中断线对应是GPIOX里面的0号端口
	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;//选择模式,是中断还是事件
	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;//中断触发,上升沿
	EXTI_InitStruct.EXTI_LineCmd = ENABLE;//中断使能
	EXTI_Init(&EXTI_InitStruct);
}

Systick(系统定时器)

#include "bsp_systick.h"

#if 0
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{ 
	//判断ticks的值是否大于2^24(最多只能记这么多次)
  if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            
	//初始化RELOAD寄存器                                                        
  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      
  
	//配置中断优先级,这里__NVIC_PRIO_BITS = 4为默认配置为15
	NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  
	
	//初始化counter的值为0
  SysTick->VAL   = 0;    

  //配置systick时钟为72M
  //使能中断
  //使能systick	
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | 
                   SysTick_CTRL_TICKINT_Msk   | 
                   SysTick_CTRL_ENABLE_Msk;                    
  return (0);                                                  
}
#endif


void SysTick_Delay_us(uint32_t us)
{
	uint32_t i;
	
	SysTick_Config(72);//设置计时的启示数字数,这里数字为72的时间为:
	                   //时钟频率/72 = 72MHz/72,相当于完成一次记数即1us
	
	for(i=0;i<us;i++)
	{
		while(!((SysTick->CTRL) & 1<<16));//这里与CTRL寄存器的16位相与
		                                  //判断计时器是否已经记完一次
	}
	
	SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;//关闭systicks使能
}

void SysTick_Delay_ms(uint32_t ms)
{
	uint32_t i;
	
	SysTick_Config(72000);
	
	for(i=0;i<ms;i++)
	{
		while(!((SysTick->CTRL) & 1<<16));
	}
	
	SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}

USART

STM32学习笔记_第4张图片

不知道为啥上位机发送hex数据为1无反应,而发11有反应。一般还是接收字符数据吧。

要记得USART1的时钟是APB2,USART2-5的时钟是APB1。

因为数串口据寄存器(DR)只有八位所以接收的时候一次也只能接收八位即1byte一个字符型。

接收数据为字符时将数据减去0x30即可变为对应的整型数值

串口配置:

static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* 嵌套向量中断控制器组选择 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  
  /* 配置USART为中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
  /* 抢断优先级*/
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* 使能中断 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* 初始化配置NVIC */
  NVIC_Init(&NVIC_InitStructure);
}

void USART_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

	// 打开串口GPIO的时钟
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
	
	// 打开串口外设的时钟
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

	// 将USART Tx的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  // 将USART Rx的GPIO配置为浮空输入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
	
	// 配置串口的工作参数
	// 配置波特率
	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
	// 配置 针数据字长
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	// 配置停止位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	// 配置校验位
	USART_InitStructure.USART_Parity = USART_Parity_No ;
	// 配置硬件流控制
	USART_InitStructure.USART_HardwareFlowControl = 
	USART_HardwareFlowControl_None;
	// 配置工作模式,收发一起
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	// 完成串口的初始化配置
	USART_Init(DEBUG_USARTx, &USART_InitStructure);
	
	// 串口中断优先级配置
	NVIC_Configuration();
	
	// 使能串口接收中断
	USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);	
	
	// 使能串口
	USART_Cmd(DEBUG_USARTx, ENABLE);	    
}

DMA

DMA1
STM32学习笔记_第5张图片
DMA2STM32学习笔记_第6张图片
DMA: Data Memory Access,直接存储器访问。主要功能是可以把数据从一个地方搬到另外一个地方,而且不占用CPU。
DMA1: 有7个通道,可以实现 P(外设)->M(内存), M->P,M->M
DMA2: 有7个通道,可以实现 P->M, M->P,M->M

多个DMA请求一起来,怎么办?
1、软件阶段, DMA_CCRx: PL[1:0]。
2、硬件阶段,通道编号小的优先级大, DM1
的优先级高于DMA2的优先级。

M TO P(串口)
将数据直接发送大串口的数据存储器
编程步骤:
1-初始化串口(从现有的例程移植过来)
2-配置DMA初始化结构体。
3-编写主函数(开启串口发送DMA请求)。
注意主程序最后还要使能串口发送DMA请求

USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Tx, ENABLE);//哪个串口,请求
//发送还是接收。这里是通过DMA将数据直接发送到串口数据寄存器然后通过串口
//发送给上位机所以请求发送

存储器介绍

STM32学习笔记_第7张图片
RAM:“Random Access Memory”的缩写,被译为随机存储器。现在 RAM 已经专门用于指代作为计算机内存的易失性半导体存储器。根据 RAM 的存储机制,又分为动态随机存储器 DRAM(Dynamic RAM) 以及静态随机存储器SRAM(Static RAM) 两种。
SDRAM(Synchronous DRAM):同步DRAM。为了进一步提高 SDRAM 的通讯速度,人们设计了 DDR SDRAM 存储(Double Data RateSDRAM)。它的存储特性与 SDRAM 没有区别,但 SDRAM 只在上升沿表示有效数据,在 1 个时钟周期内,只能表示 1 个有数据;而 DDRSDRAM 在时钟的上升沿及下降沿各表示一个数据,也就是说在 1 个时钟周期内可以表示 2 位数据,在时钟频率同样的情况下,提高了一倍的速度。内存条一般是DDRSDRAM。
STM32学习笔记_第8张图片

ROM: 是“Read Only Memory”的缩写,意为只能读的存储器。现在一般用于指代非易失性半导体存储器。
EEPROM(Electrically Erasable Programmable ROM): 是电可擦除存储器。 EEPROM 可以重复擦写,它的擦除和写入都是直接使用电路控制,不需要再使用外部设备来擦写。而且可以按字节为单位修改数据,无需整个芯片擦除。现在主要使用的 ROM 芯片都是 EEPROM。
FLASH 存储器:又称为闪存,它也是可重复擦写的储器,部分书籍会把 FLASH 存储器称为 FLASH ROM,但它的容量一般比 EEPROM 大得多,且在擦除时,一般以多个字节为单位。如有的 FLASH存储器以 4096 个字节为扇区,最小的擦除单位为一个扇区。根据存储单元电路的不同, FLASH存储器又分为 NOR FLASH 和 NAND FLASH。
STM32学习笔记_第9张图片
NOR 与 NAND 的共性是在数据写入前都需要有擦除操作,而擦除操作一般是以“扇区/块”为单位的。而 NOR 与 NAND 特性的差别,主要是由于其内部“地址/数据线”是否分开导致的。由于 NOR 的地址线和数据线分开,它可以按“字节”读写数据,符合 CPU 的指令译码执行要求,所以假如 NOR 上存储了代码指令, CPU 给 NOR 一个地址, NOR 就能向 CPU 返回一个数据让CPU 执行,中间不需要额外的处理操作。
而由于 NAND 的数据和地址线共用,只能按“块”来读写数据,假如 NAND 上存储了代码指令,CPU 给 NAND 地址后,它无法直接返回该地址的数据,所以不符合指令译码要求。表 NOR_FLASH与 NAND_FLASH 特性对比 中的最后一项“是否支持 XIP”描述的就是这种立即执行的特性 (eXecute In Place)。若代码存储在 NAND 上,可以把它先加载到 RAM 存储器上,再由 CPU 执行。所以在功能上可以认为 NOR 是一种断电后数据不丢失的 RAM,但它的擦除单位与 RAM 有区别,且读写速度比RAM 要慢得多。
另外, FLASH 的擦除次数都是有限的 (现在普遍是 10 万次左右),当它的使用接近寿命的时候,可能会出现写操作失败。由于 NAND 通常是整块擦写,块内有一位失效整个块就会失效,这被称为坏块,而且由于擦写过程复杂,从整体来说 NOR 块块更少,寿命更长。由于可能存在坏块,所以FLASH 存储器需要“探测/错误更正 (EDC/ECC)”算法来确保数据的正确性。
由于两种 FLASH 存储器特性的差异, NOR FLASH 一般应用在代码存储的场合,如嵌入式控制器内部的程序存储空间。而 NAND FLASH 一般应用在大数据量存储的场合,包括 SD 卡、 U 盘以及固态硬盘等,都是 NAND FLASH 类型的。

你可能感兴趣的:(STM32学习,stm32,学习,arm开发)