STM32F407 纯寄存器操作GPIO,串口,中断(专治花里胡哨)

发布者:InspiredDreamer最新更新时间:2019-08-23 来源: eefocus关键字:STM32F407  寄存器操作  GPIO  串口  中断 手机看文章 扫描二维码
随时随地手机看文章

配置时钟,延时函数,均使用正点原子的SYSTEM文件

硬件:正点原子探索者,STLINK调试


系统时钟配置步骤

**加粗样式**

正点原子,时钟配置函数(我都是直接把sys.c和sys.h拷贝过来,直接使用)


//设置向量表偏移地址

//NVIC_VectTab:基址

//Offset:偏移量  

void MY_NVIC_SetVectorTable(u32 NVIC_VectTab,u32 Offset)  

{       

SCB->VTOR=NVIC_VectTab|(Offset&(u32)0xFFFFFE00);//设置NVIC的向量表偏移寄存器,VTOR低9位保留,即[8:0]保留。

//时钟设置函数

//Fvco=Fs*(plln/pllm);

//Fsys=Fvco/pllp=Fs*(plln/(pllm*pllp));

//Fusb=Fvco/pllq=Fs*(plln/(pllm*pllq));


//Fvco:VCO频率

//Fsys:系统时钟频率

//Fusb:USB,SDIO,RNG等的时钟频率

//Fs:PLL输入时钟频率,可以是HSI,HSE等. 

//plln:主PLL倍频系数(PLL倍频),取值范围:64~432.

//pllm:主PLL和音频PLL分频系数(PLL之前的分频),取值范围:2~63.

//pllp:系统时钟的主PLL分频系数(PLL之后的分频),取值范围:2,4,6,8.(仅限这4个值!)

//pllq:USB/SDIO/随机数产生器等的主PLL分频系数(PLL之后的分频),取值范围:2~15.


//外部晶振为8M的时候,推荐值:plln=336,pllm=8,pllp=2,pllq=7.

//得到:Fvco=8*(336/8)=336Mhz

//     Fsys=336/2=168Mhz

//     Fusb=336/7=48Mhz

//返回值:0,成功;1,失败。

u8 Sys_Clock_Set(u32 plln,u32 pllm,u32 pllp,u32 pllq)

u16 retry=0;

u8 status=0;

RCC->CR|=1<<16; //HSE 开启 

while(((RCC->CR&(1<<17))==0)&&(retry<0X1FFF))retry++;//等待HSE RDY

if(retry==0X1FFF)status=1; //HSE无法就绪

else   

{

RCC->APB1ENR|=1<<28; //电源接口时钟使能

PWR->CR|=3<<14; //高性能模式,时钟可到168Mhz

RCC->CFGR|=(0<<4)|(5<<10)|(4<<13);//HCLK 不分频;APB1 4分频;APB2 2分频. 

RCC->CR&=~(1<<24); //关闭主PLL

RCC->PLLCFGR=pllm|(plln<<6)|(((pllp>>1)-1)<<16)|(pllq<<24)|(1<<22);//配置主PLL,PLL时钟源来自HSE

RCC->CR|=1<<24; //打开主PLL

while((RCC->CR&(1<<25))==0);//等待PLL准备好 

FLASH->ACR|=1<<8; //指令预取使能.

FLASH->ACR|=1<<9; //指令cache使能.

FLASH->ACR|=1<<10; //数据cache使能.

FLASH->ACR|=5<<0; //5个CPU等待周期. 

RCC->CFGR&=~(3<<0); //清零

RCC->CFGR|=2<<0; //选择主PLL作为系统时钟  

while((RCC->CFGR&(3<<2))!=(2<<2));//等待主PLL作为系统时钟成功. 

return status;

}  


//系统时钟初始化函数

//plln:主PLL倍频系数(PLL倍频),取值范围:64~432.

//pllm:主PLL和音频PLL分频系数(PLL之前的分频),取值范围:2~63.

//pllp:系统时钟的主PLL分频系数(PLL之后的分频),取值范围:2,4,6,8.(仅限这4个值!)

//pllq:USB/SDIO/随机数产生器等的主PLL分频系数(PLL之后的分频),取值范围:2~15.

void Stm32_Clock_Init(u32 plln,u32 pllm,u32 pllp,u32 pllq)

{  

RCC->CR|=0x00000001; //设置HISON,开启内部高速RC振荡

RCC->CFGR=0x00000000; //CFGR清零 

RCC->CR&=0xFEF6FFFF; //HSEON,CSSON,PLLON清零 

RCC->PLLCFGR=0x24003010; //PLLCFGR恢复复位值 

RCC->CR&=~(1<<18); //HSEBYP清零,外部晶振不旁路

RCC->CIR=0x00000000; //禁止RCC时钟中断 

Sys_Clock_Set(plln,pllm,pllp,pllq);//设置时钟 

//配置向量表   

#ifdef  VECT_TAB_RAM

MY_NVIC_SetVectorTable(1<<29,0x0);

#else   

MY_NVIC_SetVectorTable(0,0x0);

#endif 

}


时钟与外设

在这里插入图片描述

使能则相应位置一

image.png


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


 RCC->AHB1ENR |= 0x00000008; //使能GPIOD时钟

 RCC->APB2ENR |= (1<<14);  //使能syscfg时钟



GPIO


在这里插入图片描述

设置步骤:


使能相关时钟


设置相应的IO口为输入或输出 BSRRH BSRRL


设置输入输出的类型 OTYPER


设置输出的速度(如果设置为输入,此步跳过) OSPEEDR


如果输出速度>=50M,开启补偿单元 SYSCFG->CMPCR


设置上拉下拉寄存器 PUPDR


注意:

IDR寄存器,(只读),对GPIO输入的读取

ODR寄存器,对GPIO输出的读写

BSSR寄存器,(只写),对GPIO输出,低16位,输出高电平,高16位,输出低电平

//reset register GPIOx_BSRRH, write only

//set register GPIOx_BSRRL, write only

在这里插入图片描述

GPIO F9,F10 交替闪烁


#include  

#include

#include

 

int main ()

{   

  Stm32_Clock_Init(336,8,2,7); //系统时钟

delay_init(48);

  

  RCC->AHB1ENR |= 0x00000020; //使能GPIOF时钟

  

  GPIOF->MODER &= 0x00000000; //设置F9,F10  0001 0100 0000 0000 0000 0000

  GPIOF->MODER |= 0x00140000; 

  

  GPIOF->OTYPER &= 0x00000600; //设置F9,F10推挽输出

  

  GPIOF->OSPEEDR &= 0x00000000; //F9,F10 速度50m 0010 1000 0000 0000 0000 0000

  GPIOF->OSPEEDR |= 0x00280000;

  

  

  //GPIOF->PUPDR &= 0x00280000;  //10下拉   00无   

//GPIOF->PUPDR &= 0x00140000;    //01上拉

  

  

  while(1)

  {

      GPIOF->BSRRH = 0x0200; //0000 0000 0010

  delay_ms(1000);

  GPIOF->BSRRL = 0x0200; //0000 0000 0010

   

      GPIOF->BSRRH = 0x0400; 

  delay_ms(1000);

  GPIOF->BSRRL = 0x0400; 

// GPIOF->ODR=0x00000600;

// delay_ms(1000);

// GPIOF->ODR=0x00000000;

// delay_ms(1000);


  }

}


串口与GPIO复用

GPIO复用功能映射表,只给出PORTA,B,C


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

根据表中找出对应外设的复用引脚,及复用功能选择位,AFn。


GPIO复用:

由两个寄存器控制,GPIOx_AFRH(高八位),GPIOx_AFRL(低八位)。

在程序中,GPIOB->AFR[0]表示GPIOx_AFRL(低八位),

GPIOB->AFR[1]表示GPIOx_AFRH(高八位)。


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

找到对应的引脚y,写入AFn。

比如串口三对应的复用功能是AF7,引脚是B10,11。则设置引脚的输出类型,速度,上拉下拉。

引脚位于高八位,写入GPIOx_AFRH,GPIOB->AFR[1] = 0x00007700


串口三 内容回显

内容以回车(0x0D)结束


设置步骤:


使能相关时钟

RCC->AHB1ENR|=1<<1;    //使能PORTB口时钟  

RCC->APB1ENR|=1<<18;  //使能串口3时钟 


设置相应的IO口为复用输出

GPIO_Set(GPIOB,PIN10|PIN11,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_50M,GPIO_PUPD_PU);//PB10,PB11,复用功能,上拉输出

  GPIO_AF_Set(GPIOB,10,7); //PB10,AF7

GPIO_AF_Set(GPIOB,11,7); //PB11,AF7


波特率设置

//计算波特率

float temp;

u16 mantissa;

u16 fraction;    

temp=(float)(pclk1*1000000)/(bound*16);//得到USARTDIV,OVER8设置为0

mantissa=temp;     //得到整数部分

fraction=(temp-mantissa)*16; //得到小数部分,OVER8设置为0  

   mantissa<<=4;

mantissa+=fraction; 


//串口设置

USART3->BRR=mantissa; //波特率设置  

USART3->CR1|=1<<3;  //串口发送使能  

USART3->CR1|=1<<2;  //串口接收使能

USART3->CR1|=1<<5;    //接收缓冲区非空中断使能

USART3->CR1|=1<<13;  //串口使能  


//中断优先级

MY_NVIC_Init(0,0,USART3_IRQn,2);//组2,优先级0,0,最高优先级 


状态寄存器 USART3->SR


数据寄存器 USART3->DR


缓冲区数组内容清空 memset(USART3_RX_BUF,0,USART3_MAX_RECV_LEN)


完整代码:


#include

#include

#include  

#include "string.h"

 

u8 ok_to_send; //可以发送的标志

u8 Rx_data_counter;


#define USART3_MAX_RECV_LEN 400 //最大接收缓存字节数


//串口接收缓存区

u8 USART3_RX_BUF[USART3_MAX_RECV_LEN]; //接收缓冲,最大USART3_MAX_RECV_LEN个字节.


u8 Rx_data_counter=0;

u8 ok_to_send=0;


void USART3_IRQHandler(void)

{

  if(USART3->SR & (1<<5)) //接收数据寄存器非空

{

USART3_RX_BUF[Rx_data_counter] = USART3->DR;

Rx_data_counter++;

if(USART3_RX_BUF[Rx_data_counter-1] ==0x0D)

{

      Rx_data_counter = 0;

ok_to_send=1;

}

}  



//初始化IO 串口3

//pclk1:PCLK1时钟频率(Mhz)

//bound:波特率 


void usart3_init(u32 pclk1,u32 bound)

{   

float temp;

u16 mantissa;

u16 fraction;    

temp=(float)(pclk1*1000000)/(bound*16);//得到USARTDIV,OVER8设置为0

mantissa=temp; //得到整数部分

fraction=(temp-mantissa)*16; //得到小数部分,OVER8设置为0  

  mantissa<<=4;

mantissa+=fraction; 

RCC->AHB1ENR|=1<<1;    //使能PORTB口时钟  

RCC->APB1ENR|=1<<18;  //使能串口3时钟 

GPIO_Set(GPIOB,PIN10|PIN11,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_50M,GPIO_PUPD_PU);//PB10,PB11,复用功能,上拉输出

  GPIO_AF_Set(GPIOB,10,7); //PB10,AF7

GPIO_AF_Set(GPIOB,11,7); //PB11,AF7

//波特率设置

  USART3->BRR=mantissa; // 波特率设置  

USART3->CR1|=1<<3;  //串口发送使能  

USART3->CR1|=1<<2;  //串口接收使能

USART3->CR1|=1<<5;    //接收缓冲区非空中断使能

USART3->CR1|=1<<13;  //串口使能  

MY_NVIC_Init(0,0,USART3_IRQn,2);//组2,优先级0,0,最高优先级 

}


 

int main()


u8 j=0;


Stm32_Clock_Init(336,8,2,7); 


usart3_init(42,9600);//初始化串口3为:9600,波特率.


while(1)

if(ok_to_send==1)

{

for(j=0;j

USART3->DR=USART3_RX_BUF[j];

while((USART3->SR&0X40)==0);//循环发送,直到发送完毕 

if( USART3_RX_BUF[j] ==0x0D)

{

ok_to_send=0;

j=0;

memset(USART3_RX_BUF,0,USART3_MAX_RECV_LEN);

  }

    }

}

}

}


注意:

在这里插入图片描述

串口数据的读写,可以看到,是以8bit,一个字节为单位来读写的。利用DR寄存器,对DR进行读取,即DR赋值给别人,BUFF=DR。对DR进行写入,即别人赋值给DR,DR=BUFF。


再利用状态寄存器,SR来读取状态信息控制。


在这里插入图片描述

在这里插入图片描述

能否像小船博主一样,全部用寄存器来设置呢?

https://blog.csdn.net/w471176877/article/details/7957187


也就是关于GPIO复用


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

注释了GPIO设置的三个函数,完全用寄存器来设置


// GPIO_Set(GPIOB,PIN10|PIN11,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_50M,GPIO_PUPD_PU);//PB10,PB11,复用功能,上拉输出

// GPIO_AF_Set(GPIOB,10,7); //PB10,AF7

// GPIO_AF_Set(GPIOB,11,7); //PB11,AF7

GPIOB->AFR[1] = 0x00007700;//选择PB10,11复用功能 

 

GPIOB->MODER &= 0xFF0FFFFF; //设置PB10,11,复用模式

GPIOB->MODER |= 0x00A00000; 



GPIOB->OSPEEDR &= 0xFFCFFFFF; //PB10速度50m

GPIOB->OSPEEDR |= 0x00200000;

 

GPIOB->PUPDR &= 0xFFCFFFFF; //PB10

GPIOB->PUPDR |= 0x00100000;


中断优先级函数也用寄存器来替代了


  //MY_NVIC_Init(0,0,USART3_IRQn,2);//组2,优先级0,0,最高优先级 

  SCB->AIRCR = 0x05FA0000 | 0x400;  //中断优先级分组 抢占:响应=3:1

NVIC->IP[39] = 0xf0; //最低抢占优先级,最低响应优先级1111

  NVIC->ISER[1] |= (1<<(39-32)); //使能中断线39,也就是usart3中断


除了时钟树配置,其它都自己用寄存器来配置了。


#include

#include

#include  

#include "string.h"

 

u8 suffer[100];

u8 ok_to_send;

u8 Rx_data_counter;


#define USART3_MAX_RECV_LEN 400 //最大接收缓存字节数


//串口接收缓存区

u8 USART3_RX_BUF[USART3_MAX_RECV_LEN]; //接收缓冲,最大USART3_MAX_RECV_LEN个字节.


u8 Rx_data_counter=0;

u8 ok_to_send=0;


void USART3_IRQHandler(void)

{

  if(USART3->SR & (1<<5)) //接收数据寄存器非空

{

USART3_RX_BUF[Rx_data_counter] = USART3->DR;

Rx_data_counter++;

if(USART3_RX_BUF[Rx_data_counter-1] ==0x0D)

{

      Rx_data_counter = 0;

ok_to_send=1;

}

}  



//初始化IO 串口3

//pclk1:PCLK1时钟频率(Mhz)

//bound:波特率 


void usart3_init(u32 pclk1,u32 bound)

{   

float temp;

u16 mantissa;

u16 fraction;    

temp=(float)(pclk1*1000000)/(bound*16);//得到USARTDIV,OVER8设置为0

mantissa=temp; //得到整数部分

fraction=(temp-mantissa)*16; //得到小数部分,OVER8设置为0  

  mantissa<<=4;

mantissa+=fraction; 

RCC->AHB1ENR|=1<<1;    //使能PORTB口时钟  

RCC->APB1ENR|=1<<18;  //使能串口3时钟 


GPIOB->AFR[1] = 0x00007700;//选择PB10,11复用功能 

 

GPIOB->MODER &= 0xFF0FFFFF; //设置PB10,11,复用模式

GPIOB->MODER |= 0x00A00000; 

[1] [2]
关键字:STM32F407  寄存器操作  GPIO  串口  中断 引用地址:STM32F407 纯寄存器操作GPIO,串口,中断(专治花里胡哨)

上一篇:STM32F407 纯寄存器操作定时器,PWM,ADC(专治花里胡哨)
下一篇:stm32 SCB->AIRCR 寄存器和中断优先级寄存器使用理解

推荐阅读最新更新时间:2024-11-10 10:43

MSP430F149-利用IO中断方式实现按键检测程序
#include msp430x14x.h #define key1 0x01 #define key2 0x02 #define key3 0x04 #define delay_small 200 #define key_1 0xfe //定义返回键值1 #define key_2 0xfd //定义返回键值2 #define key_3 0xfb //定义返回键值3 void key_process_0(void); void key_process_1(void); void key_process_2(void); void key_check (void); unsigned char key_value
[单片机]
STM32使用CubeMAX配置的串口中断接收方法
STM32使用cubeMAX可以快速建立工程模板,但是默认使用的是Hal库构成的工程,对于习惯使用了ST标准库的同学来说,灵活调用HAL库可能会比较生疏,我也是这么觉得的,但是还是要逐步去接触学习它,毕竟这个hal库的封装还是相当好的,有好多先进的思想和用法。 在学习过程中,我遇到了一个问题,之前也遇到过,但是没时间去研究,就是串口在CUBUMAX上配置好后,如何实现串口中断接收,接下来就来记录一下我学习到的知识: 1.定位串口中断发生的地方 HAL库的中断处理还是和标准库一样的,在stm32xxxx_it.c中定义我们定位到如下函数: HAL_UART_IRQHandler(&huart1); 再往下定位,我们找
[单片机]
STM32 串口烧写 FLASH 外部字库 UCGUI显示 自我学习总结
最近学习TFT显示问题,在多种汉字显示方面有点难,主要是字库太大,几个字库就不得了。开始是使用SD卡向外部FLASH---W25X16写,完全能够完成。后来觉得这样比较麻烦,有时候还没有SD接口,于是打算用串口写一下试一试,网上有很多人说会丢失数据,在后面的试验中暂时没有发现。 我是在我前段时间学习的UCGUI的基础基础上修改的。 主要功能是----启动开发板,首先写入地址指令----必须十六进制----比如---2A 23 00 05 00 03 23 2A-----其中2A,23为验证码,前后都有,第3,4位是地址码,前面就表示将要写入的起始地址是0x05*4096,第5,6位是为了写入数据将要从起始地址连续擦除的扇区
[单片机]
STM32 <font color='red'>串口</font>烧写 FLASH 外部字库 UCGUI显示 自我学习总结
stm32的串口空闲中断接收数据
举个例子: 1、后台数据- USART1- USART2- 其它设备,其它设备数据- USART2- USART1- 后台,这两个数据过程也可能同时进行。 2、由于硬件的限制,USART1和USART2的传输波特率不一样,比如USART1使用GPRS通信,USART2使用短距离无线通信;或者USART1使用以太网通信,USART2使用485总线通信。 现在我把我实现的过程简单描述一下: 1、 初始化设置:USART1_RX DMA1_ Channel5,USART2_RX DMA1_ Channel6,USART1_TX DMA1_ Channel4,USART2_TX DMA1_ Channel7(具体设置请看程序包) 2
[单片机]
stm32串口奇偶校验
STM32串口通信使用奇偶校验的时候应该设置数据位长度9bit,奇偶校验是硬件完成的,并且stm32用校验位时,数据位要选9位,8位会出现故障可能。 STM32串口通信使用奇偶校验代码如下:#ifdef USART1_ON //允许USART1的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); _InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_9b; USART_InitStructure.USART_StopBits = U
[单片机]
STM32之GPIO按键实验
实验板:带有按键的Stm32开发板一块,本人用得是Open107V实验板,型号是107的 库函数:STM32官方3.5的库 其他材料:ST-LINK、跳线若干 实验原理:按键实验和LED实验类似, GPIO点灯是向led的pin脚输出高低电平, 而按键则是向pin口输入高低电平。 对于按键有两种输入模式可以选择, 一,采用上拉输入模式,二,采用浮动浮空输入模式,上拉输入模式在没有按键按下的时候是默认是高电平。符合这个要求。原理图如下: 实验描述:PE5连接到key1,用扫描的方式查询是否有按键按下,key1按下时,LED1状态取反。 库文件:startup/start_stm32f10x_cl.s CMSIS/cor
[单片机]
STM32:GPIO基础与对应管脚操作库函数
USE_STDPERIPH_DRIVER, STM32F10X_HD STM32固件库Libraries\CMSIS\Core\CM3\startup\arm中启动文件的文件名英文缩写意义: cl:互联型产品, stm32f105/107 系列 vl:超值型产品, stm32f100 系列 xl:超高密度(容量) 产品, stm32f101/103 系列 ld:低密度产品, FLASH 小于 64K md:中等密度产品, FLASH=64 or 128 hd:高密度产品, FLASH 大于 128 GPIO_TypeDef * GPIOx; //定义一个 GPIO_TypeDef 型结构体指针 GPIOx GPIOx = G
[单片机]
STM32 常用GPIO操作函数记录
STM32读具体GPIOx的某一位是1还是0 1 /** 2 * @brief Reads the specified input port pin. 3 * @param GPIOx: where x can be (A..G) to select the GPIO peripheral. 4 * @param GPIO_Pin: specifies the port bit to read. 5 * This parameter can be GPIO_Pin_x where x can be (0..15). 6 * @retval The input port pin value. 7 *
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved