Mini STM32 IO输入输出寄存器配置

这段代码实现了基于STM32F10x系列微控制器的LED闪烁控制程序。主要包括以下功能:

  1. 系统初始化:配置系统时钟为72MHz,初始化延时函数和LED控制IO口
  2. LED控制:通过PA8(LED0)和PD2(LED1)实现双LED交替闪烁,间隔3秒
  3. 底层驱动:包含时钟配置、延时函数、NVIC中断配置、GPIO控制等基础模块
  4. 串口通信:支持USART1串口通信和printf重定向功能

代码采用寄存器级编程方式,包含完善的时钟树配置、精确的毫秒级延时,并支持中断处理机制。所有功能模块都进行了模块化封装,便于扩展和维护。

/***2012 9 9 5:15*************************/

#include <stm32f10x_lib.h>

#include "sys.h"

#include "usart.h"

#include "delay.h"

#include "led.h"

//Mini STM32开发板范例代码1

int main(void)

{

Stm32_Clock_Init(9); //系统时钟设置

delay_init(72); //延时初始化

LED_Init(); //初始化与LED连接的硬件接口

while(1)

{

LED0=0;

LED1=1;

delay_ms(3000);

LED0=1;

LED1=0;

delay_ms(3000);

}

}

#include <stm32f10x_lib.h>

#include "delay.h"

//////////////////////////////////////////////////////////////////////////////

////

//本程序只供学习使用,未经作者许可,不得用于其它任何用途

//Mini STM32开发板

//使用SysTick的普通计数模式对延迟进行管理

//包括delay_us,delay_ms

//All rights reserved

//********************************************************************************

//V1.2修改说明

//修正了中断中调用出现死循环的错误

//防止延时不准确,采用do while结构!

//////////////////////////////////////////////////////////////////////////////

////

static u8 fac_us=0;//us延时倍乘数

static u16 fac_ms=0;//ms延时倍乘数

//初始化延迟函数

//SYSTICK的时钟固定为HCLK时钟的1/8

//SYSCLK:系统时钟

void delay_init(u8 SYSCLK)

{

SysTick->CTRL&=0xfffffffb;//bit2清空,选择外部时钟 HCLK/8

fac_us=SYSCLK/8;

fac_ms=(u16)fac_us*1000;

}

//延时nms

//注意nms的范围

//SysTick->LOAD为24位寄存器,所以,最大延时为:

//nms<=0xffffff*8*1000/SYSCLK

//SYSCLK单位为Hz,nms单位为ms

//对72M条件下,nms<=1864

void delay_ms(u16 nms)

{

u32 temp;

SysTick->LOAD=(u32)nms*fac_ms;//时间加载(SysTick->LOAD为24bit)

SysTick->VAL =0x00; //清空计数器

SysTick->CTRL=0x01 ; //开始倒数

do

{

temp=SysTick->CTRL;

}

while(temp&0x01&&!(temp&(1<<16)));//等待时间到达

SysTick->CTRL=0x00; //关闭计数器

SysTick->VAL =0X00; //清空计数器

}

//延时nus

//nus为要延时的us数.

void delay_us(u32 nus)

{

u32 temp;

SysTick->LOAD=nus*fac_us; //时间加载

SysTick->VAL=0x00; //清空计数器

SysTick->CTRL=0x01 ; //开始倒数

do

{

temp=SysTick->CTRL;

}

while(temp&0x01&&!(temp&(1<<16)));//等待时间到达

SysTick->CTRL=0x00; //关闭计数器

SysTick->VAL =0X00; //清空计数器

}

#include <stm32f10x_lib.h>

#include "sys.h"

//////////////////////////////////////////////////////////////////////////////

////

//本程序只供学习使用,未经作者许可,不得用于其它任何用途

//All rights reserved

//********************************************************************************

//V1.4修改说明

//把NVIC KO了,没有使用任何库文件!

//加入了JTAG_Set函数

//////////////////////////////////////////////////////////////////////////////

////

//设置向量表偏移地址

//NVIC_VectTab:基址

//Offset:偏移量

//CHECK OK

//091207

void MY_NVIC_SetVectorTable(u32 NVIC_VectTab, u32 Offset)

{

//检查参数合法性

assert_param(IS_NVIC_VECTTAB(NVIC_VectTab));

assert_param(IS_NVIC_OFFSET(Offset));

SCB->VTOR = NVIC_VectTab|(Offset & (u32)0x1FFFFF80);//设置NVIC的向量表偏

移寄存器

//用于标识向量表是在CODE区还是在RAM区

}

//设置NVIC分组

//NVIC_Group:NVIC分组 0~4 总共5组

//CHECK OK

//091209

void MY_NVIC_PriorityGroupConfig(u8 NVIC_Group)

{

u32 temp,temp1;

temp1=(~NVIC_Group)&0x07;//取后三位

temp1<<=8;

temp=SCB->AIRCR; //读取先前的设置

temp&=0X0000F8FF; //清空先前分组

temp|=0X05FA0000; //写入钥匙

temp|=temp1;

SCB->AIRCR=temp; //设置分组

}

//设置NVIC

//NVIC_PreemptionPriority:抢占优先级

//NVIC_SubPriority :响应优先级

//NVIC_Channel :中断编号

//NVIC_Group :中断分组 0~4

//注意优先级不能超过设定的组的范围!否则会有意想不到的错误

//组划分:

//组0:0位抢占优先级,4位响应优先级

//组1:1位抢占优先级,3位响应优先级

//组2:2位抢占优先级,2位响应优先级

//组3:3位抢占优先级,1位响应优先级

//组4:4位抢占优先级,0位响应优先级

//NVIC_SubPriority和NVIC_PreemptionPriority的原则是,数值越小,越优先

//CHECK OK

//100329

void MY_NVIC_Init(u8 NVIC_PreemptionPriority,u8 NVIC_SubPriority,u8 NVIC_Channel,u8

NVIC_Group)

{

u32 temp;

u8 IPRADDR=NVIC_Channel/4; //每组只能存4个,得到组地址

u8 IPROFFSET=NVIC_Channel%4;//在组内的偏移

IPROFFSET=IPROFFSET*8+4; //得到偏移的确切位置

MY_NVIC_PriorityGroupConfig(NVIC_Group);//设置分组

temp=NVIC_PreemptionPriority<<(4-NVIC_Group);

temp|=NVIC_SubPriority&(0x0f>>NVIC_Group);

temp&=0xf;//取低四位

if(NVIC_Channel<32)NVIC->ISER0|=1<<NVIC_Channel;//使能中断位(要清除的话,

相反操作就OK)

else NVIC->ISER1|=1<<(NVIC_Channel-32);

NVIC->IPRIPRADDR|=temp<<IPROFFSET;//设置响应优先级和抢断优先级

}

//外部中断配置函数

//只针对GPIOA~G;不包括PVD,RTC和USB唤醒这三个

//参数:GPIOx:0~6,代表GPIOA~G;BITx:需要使能的位;TRIM:触发模式,1,下升沿;2,上降沿;3,

任意电平触发

//该函数一次只能配置1个IO口,多个IO口,需多次调用

//该函数会自动开启对应中断,以及屏蔽线

//待测试...

void Ex_NVIC_Config(u8 GPIOx,u8 BITx,u8 TRIM)

{

u8 EXTADDR;

u8 EXTOFFSET;

EXTADDR=BITx/4;//得到中断寄存器组的编号

EXTOFFSET=(BITx%4)*4;

RCC->APB2ENR|=0x01;//使能io复用时钟

AFIO->EXTICREXTADDR&=~(0x000F<<EXTOFFSET);//清除原来设置!!!

AFIO->EXTICREXTADDR|=GPIOx<<EXTOFFSET;//EXTI.BITx映射到

GPIOx.BITx

//自动设置

EXTI->IMR|=1<<BITx;// 开启line BITx上的中断

//EXTI->EMR|=1<<BITx;//不屏蔽line BITx上的事件 (如果不屏蔽这句,在硬件上

是可以的,但是在软件仿真的时候无法进入中断!)

if(TRIM&0x01)EXTI->FTSR|=1<<BITx;//line BITx上事件下降沿触发

if(TRIM&0x02)EXTI->RTSR|=1<<BITx;//line BITx上事件上升降沿触发

}

//不能在这里执行所有外设复位!否则至少引起串口不工作.

//把所有时钟寄存器复位

//CHECK OK

//091209

void MYRCC_DeInit(void)

{

RCC->APB1RSTR = 0x00000000;//复位结束

RCC->APB2RSTR = 0x00000000;

RCC->AHBENR = 0x00000014; //睡眠模式闪存和SRAM时钟使能.其他关闭.

RCC->APB2ENR = 0x00000000; //外设时钟关闭.

RCC->APB1ENR = 0x00000000;

RCC->CR |= 0x00000001; //使能内部高速时钟HSION

RCC->CFGR &= 0xF8FF0000; //复位SW1:0,HPRE3:0,PPRE12:0,PPRE2

2:0\],ADCPRE\[1:0\],MCO\[2:0

RCC->CR &= 0xFEF6FFFF; //复位HSEON,CSSON,PLLON

RCC->CR &= 0xFFFBFFFF; //复位HSEBYP

RCC->CFGR &= 0xFF80FFFF; //复位PLLSRC, PLLXTPRE, PLLMUL3:0 and

USBPRE

RCC->CIR = 0x00000000; //关闭所有中断

//配置向量表

#ifdef VECT_TAB_RAM

MY_NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);

#else

MY_NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);

#endif

}

//THUMB指令不支持汇编内联

//采用如下方法实现执行汇编指令WFI

//CHECK OK

//091209

__asm void WFI_SET(void)

{

WFI;

}

//进入待机模式

//check ok

//091202

void Sys_Standby(void)

{

SCB->SCR|=1<<2;//使能SLEEPDEEP位 (SYS->CTRL)

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

PWR->CSR|=1<<8; //设置WKUP用于唤醒

PWR->CR|=1<<2; //清除Wake-up 标志

PWR->CR|=1<<1; //PDDS置位

WFI_SET(); //执行WFI指令

}

//后备寄存器写入操作

//reg:寄存器编号

//reg:要写入的数值

////check ok

////091202

//void BKP_Write(u8 reg,u16 dat)

//{

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

// RCC->APB1ENR|=1<<27; //使能备份时钟

// PWR->CR|=1<<8; //取消备份区写保护

// switch(reg)

// {

// case 1:

// BKP->DR1=dat;

// break;

// case 2:

// BKP->DR2=dat;

// break;

// case 3:

// BKP->DR3=dat;

// break;

// case 4:

// BKP->DR4=dat;

// break;

// case 5:

// BKP->DR5=dat;

// break;

// case 6:

// BKP->DR6=dat;

// break;

// case 7:

// BKP->DR7=dat;

// break;

// case 8:

// BKP->DR8=dat;

// break;

// case 9:

// BKP->DR9=dat;

// break;

// case 10:

// BKP->DR10=dat;

// break;

// }

//}

//系统软复位

//CHECK OK

//091209

void Sys_Soft_Reset(void)

{

SCB->AIRCR =0X05FA0000|(u32)0x04;

}

//JTAG模式设置,用于设置JTAG的模式

//mode:jtag,swd模式设置;00,全使能;01,使能SWD;10,全关闭;

//CHECK OK

//100818

void JTAG_Set(u8 mode)

{

u32 temp;

temp=mode;

temp<<=25;

RCC->APB2ENR|=1<<0; //开启辅助时钟

AFIO->MAPR&=0XF8FFFFFF; //清除MAPR的26:24

AFIO->MAPR|=temp; //设置jtag模式

}

//系统时钟初始化函数

//pll:选择的倍频数,从2开始,最大值为16

//CHECK OK

//091209

void Stm32_Clock_Init(u8 PLL)

{

unsigned char temp=0;

MYRCC_DeInit(); //复位并配置向量表

RCC->CR|=0x00010000; //外部高速时钟使能HSEON

while(!(RCC->CR>>17));//等待外部时钟就绪

RCC->CFGR=0X00000400; //APB1=DIV2;APB2=DIV1;AHB=DIV1;

PLL-=2;//抵消2个单位

RCC->CFGR|=PLL<<18; //设置PLL值 2~16

RCC->CFGR|=1<<16; //PLLSRC ON

FLASH->ACR|=0x32; //FLASH 2个延时周期

RCC->CR|=0x01000000; //PLLON

while(!(RCC->CR>>25));//等待PLL锁定

RCC->CFGR|=0x00000002;//PLL作为系统时钟

while(temp!=0x02) //等待PLL作为系统时钟设置成功

{

temp=RCC->CFGR>>2;

temp&=0x03;

}

}

#include "sys.h"

#include "usart.h"

//////////////////////////////////////////////////////////////////////////////

////

//本程序只供学习使用,未经作者许可,不得用于其它任何用途

//Mini STM32开发板

//串口1初始化

//All rights reserved

//********************************************************************************

//V1.3修改说明

//支持适应不同频率下的串口波特率设置.

//加入了对printf的支持

//增加了串口接收命令功能.

//修正了printf第一个字符丢失的bug

//////////////////////////////////////////////////////////////////////////////

////

//////////////////////////////////////////////////////////////////

//加入以下代码,支持printf函数,而不需要选择use MicroLIB

#if 1

#pragma import(__use_no_semihosting)

//标准库需要的支持函数

struct __FILE

{

int handle;

/* Whatever you require here. If the only file you are using is */

/* standard output using printf() for debugging, no file handling */

/* is required. */

};

/* FILE is typedef' d in stdio.h. */

FILE __stdout;

//定义_sys_exit()以避免使用半主机模式

_sys_exit(int x)

{

x = x;

}

//重定义fputc函数

int fputc(int ch, FILE *f)

{

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

USART1->DR = (u8) ch;

return ch;

}

#endif

//end

//////////////////////////////////////////////////////////////////

#ifdef EN_USART1_RX //如果使能了接收

//串口1中断服务程序

//注意,读取USARTx->SR能避免莫名其妙的错误

u8 USART_RX_BUF64; //接收缓冲,最大64个字节.

//接收状态

//bit7,接收完成标志

//bit6,接收到0x0d

//bit5~0,接收到的有效字节数目

u8 USART_RX_STA=0; //接收状态标记

void USART1_IRQHandler(void)

{

u8 res;

if(USART1->SR&(1<<5))//接收到数据

{

res=USART1->DR;

if((USART_RX_STA&0x80)==0)//接收未完成

{

if(USART_RX_STA&0x40)//接收到了0x0d

{

if(res!=0x0a)USART_RX_STA=0;//接收错误,重新开始

else USART_RX_STA|=0x80; //接收完成了

}else //还没收到0X0D

{

if(res==0x0d)USART_RX_STA|=0x40;

else

{

USART_RX_BUFUSART_RX_STA\&0X3F=res;

USART_RX_STA++;

if(USART_RX_STA>63)USART_RX_STA=0;//

接收数据错误,重新开始接收

}

}

}

}

}

#endif

//初始化IO 串口1

//pclk2:PCLK2时钟频率(Mhz)

//bound:波特率

//CHECK OK

//091209

void uart_init(u32 pclk2,u32 bound)

{

float temp;

u16 mantissa;

u16 fraction;

temp=(float)(pclk2*1000000)/(bound*16);//得到USARTDIV

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

fraction=(temp-mantissa)*16; //得到小数部分

mantissa<<=4;

mantissa+=fraction;

RCC->APB2ENR|=1<<2; //使能PORTA口时钟

RCC->APB2ENR|=1<<14; //使能串口时钟

GPIOA->CRH&=0XFFFFF00F;

GPIOA->CRH|=0X000008B0;//IO状态设置

RCC->APB2RSTR|=1<<14; //复位串口1

RCC->APB2RSTR&=~(1<<14);//停止复位

//波特率设置

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

USART1->CR1|=0X200C; //1位停止,无校验位.

#ifdef EN_USART1_RX //如果使能了接收

//使能接收中断

USART1->CR1|=1<<8; //PE中断使能

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

MY_NVIC_Init(3,3,USART1_IRQChannel,2);//组2,最低优先级

#endif

}

#include <stm32f10x_lib.h>

#include "led.h"

//////////////////////////////////////////////////////////////////////////////

////

//本程序只供学习使用,未经作者许可,不得用于其它任何用途

//All rights reserved

//////////////////////////////////////////////////////////////////////////////

////

//初始化PA8和PD2为输出口.并使能这两个口的时钟

//LED IO初始化

void LED_Init(void)

{

RCC->APB2ENR|=1<<2; //使能PORTA时钟

RCC->APB2ENR|=1<<5; //使能PORTD时钟

GPIOA->CRH&=0XFFFFFFF0;

GPIOA->CRH|=0X00000003;//PA8 推挽输出

GPIOA->ODR|=1<<8; //PA8 输出高

GPIOD->CRL&=0XFFFFF0FF;

GPIOD->CRL|=0X00000300;//PD.2推挽输出

GPIOD->ODR|=1<<2; //PD.2输出高

}

函数 GPIO_Init

|-------|--------------------------------------------------------------------------|
| 函数名 | GPIO_Init |
| 函数原形 | void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct) |
| 功能描述 | 根据 GPIO_InitStruct中指定的参数初始化外设 GPIOx 寄存器 |
| 输入参数1 | GPIOx:x 可以是 A,B,C,D或者 E,来选择 GPIO外设 |
| 输入参数2 | GPIO_InitStruct:指向结构 GPIO_InitTypeDef的指针,包含了外设 |

typedef struct

{

u16 GPIO_Pin;

GPIOSpeed_TypeDef GPIO_Speed;

GPIOMode_TypeDef GPIO_Mode;

} GPIO_InitTypeDef;

GPIO_Pin

该参数选择待设置的 GPIO管脚,使用操作符"|"可以一次选中多个管脚。可以使用下表中的任意组合。

|---------------|---------|
| GPIO_Pin | 描述 |
| GPIO_Pin_None | 无管脚被选中 |
| GPIO_Pin_0 | 选中管脚 0 |
| 。。。 | 。。。 |
| GPIO_Pin_15 | 选中管脚 15 |
| GPIO_Pin_All | 选中全部管脚 |

GPIO_Speed

GPIO_Speed 用以设置选中管脚的速率。Table 184. 给出了该参数可取的值

|------------------|--------------|
| GPIO_Speed | 描述 |
| GPIO_Speed_10MHz | 最高输出速率 10MHz |
| GPIO_Speed_2MHz | 最高输出速率 2MHz |
| GPIO_Speed_50MHz | 最高输出速率 50MHz |

GPIO_Mode

GPIO_Mode用以设置选中管脚的工作状态。Table 185. 给出了该参数可取的值

|-----------------------|--------|
| GPIO_Speed | 描述 |
| GPIO_Mode_AIN | 模拟输入 |
| GPIO_Mode_IN_FLOATING | 浮空输入 |
| GPIO_Mode_IPD | 下拉输入 |
| GPIO_Mode_IPU | 上拉输入 |
| GPIO_Mode_Out_OD | 开漏输出 |
| GPIO_Mode_Out_PP | 推挽输出 |
| GPIO_Mode_AF_OD | 复用开漏输出 |
| GPIO_Mode_AF_PP | 复用推挽输出 |

注意:

„ 当某管脚设置为上拉或者下拉输入模式,使用寄存器 Px_BSRR 和 PxBRR

„ GPIO_Mode 允许同时设置 GPIO方向(输入/输出)和对应的输入/输出设置,:位7:4对应 GPIO方向,

4:0对应配置。GPIO 方向有如下索引

  • GPIO输入模式 = 0x00

  • GPIO输出模式 = 0x01

Table 186. 给出了所有 GPIO_Mode 的索引和编码

|-------------|------|-----------------------|------|------|
| GPIO方向 | 索引 | 模式 | 设置 | 模式代码 |
| GPIO Input | 0x00 | GPIO_Mode_AIN | 0x00 | 0x00 |
| GPIO Input | 0x00 | GPIO_Mode_IN_FLOATING | 0x04 | 0x04 |
| GPIO Input | 0x00 | GPIO_Mode_IPD | 0x08 | 0x28 |
| GPIO Input | 0x00 | GPIO_Mode_IPU | 0x08 | 0x48 |
| GPIO Output | 0x01 | GPIO_Mode_Out_OD | 0x04 | 0x14 |
| GPIO Output | 0x01 | GPIO_Mode_Out_PP | 0x00 | 0x10 |
| GPIO Output | 0x01 | GPIO_Mode_AF_OD | 0x0C | 0x1C |
| GPIO Output | 0x01 | GPIO_Mode_AF_PP | 0x08 | 0x18 |

例:

GPIO_InitTypeDef GPIO_InitStructure;

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

GPIO_Init(GPIOA, &GPIO_InitStructure);

//

|------|-------------------------------------------------------|
| 函数名 | NVIC_PriorityGroupConfig |
| 函数原形 | void NVIC_PriorityGroupConfig(u32 NVIC_PriorityGroup) |
| 功能描述 | 设置优先级分组:先占优先级和从优先级 |
| 输入参数 | NVIC_PriorityGroup:优先级分组位长度 |
| 先决条件 | 优先级分组只能设置一次 |

NVIC_PriorityGroup

该参数设置优先级分组位长度(见 Table 270.)

|----------------------|-------------------|
| NVIC_PriorityGroup | 描述 |
| NVIC_PriorityGroup_0 | 先占优先级 0 位 从优先级 4位 |
| NVIC_PriorityGroup_1 | 先占优先级 1 位 从优先级 3位 |
| NVIC_PriorityGroup_2 | 先占优先级 2 位 从优先级 2位 |
| NVIC_PriorityGroup_3 | 先占优先级 3 位 从优先级 1位 |
| NVIC_PriorityGroup_4 | 先占优先级 4 位 从优先级 0位 |

例:

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

//

|-------|-------------------------------------------------------------------------------------|
| 函数名 | NVIC_SetVectorTable |
| 函数原形 | Void NVIC_SetVectorTable(u32 NVIC_VectTab, u32 Offset) |
| 功能描述 | 设置向量表的位置和偏移 |
| 输入参数1 | NVIC_VectTab:指定向量表位置在 RAM 还是在程序存储器 |
| 输入参数2 | Offset:向量表基地址的偏移量 对 FLASH,该参数值必须高于 0x08000100;对 RAM 必须高于 0x100。它同时必须是 256(64×4)的整数倍 |
| 返回值 | 指定中断活动位的新状态(SET 或者 RESET) |

NVIC_VectTab

该参数设置向量表基地址(见 Table 290.)

|--------------------|-------------|
| NVIC_ VectTab | 描述 |
| NVIC_VectTab_FLASH | 向量表位于 FLASH |
| NVIC_VectTab_RAM | 向量表位于 RAM |

例:

NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);

//

SystemInit(); //配置时钟时必须有

//

|--------|---------------------------------------------------------------------------|
| 函数名 | RCC_APB2PeriphClockCmd |
| 函数原形 | void RCC_APB2PeriphClockCmd(u32 RCC_APB2Periph, FunctionalState NewState) |
| 功能描述 | 使能或者失能 APB2 外设时钟 |
| 输入参数 1 | RCC_APB2Periph: 门控 APB2 外设时钟 |
| 输入参数 2 | NewState:指定外设时钟的新状态 这个参数可以取:ENABLE 或者 DISABLE |

RCC_APB2Periph

该参数被门控的APB2外设时钟,可以取下表的一个或者多个取值的组合作为该参数的值。

|-----------------------|-------------|
| RCC_AHB2Periph | 描述 |
| RCC_APB2Periph_AFIO | 功能复用 IO时钟 |
| RCC_APB2Periph_GPIOA | GPIOA 时钟 |
| RCC_APB2Periph_GPIOB | GPIOB 时钟 |
| RCC_APB2Periph_GPIOC | GPIOC 时钟 |
| RCC_APB2Periph_GPIOD | GPIOD 时钟 |
| RCC_APB2Periph_GPIOE | GPIOE 时钟 |
| RCC_APB2Periph_ADC1 | ADC1 时钟 |
| RCC_APB2Periph_ADC2 | ADC2 时钟 |
| RCC_APB2Periph_TIM1 | TIM1 时钟 |
| RCC_APB2Periph_SPI1 | SPI1 时钟 |
| RCC_APB2Periph_USART1 | USART1 时钟 |
| RCC_APB2Periph_ALL | 全部 APB2外设时钟 |

例:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB | RCC_APB2Periph_SPI1, ENABLE);

//

|--------|---------------------------------------------------------------------------|
| 函数名 | RCC_APB1PeriphClockCmd |
| 函数原形 | void RCC_APB1PeriphClockCmd(u32 RCC_APB1Periph, FunctionalState NewState) |
| 功能描述 | 使能或者失能 APB1 外设时钟 |
| 输入参数 1 | RCC_APB1Periph: 门控 APB1 外设时钟 |
| 输入参数 2 | NewState:指定外设时钟的新状态 这个参数可以取:ENABLE 或者 DISABLE |

RCC_APB1Periph

该参数被门控的APB1外设时钟,可以取下表的一个或者多个取值的组合作为该参数的值。

|-----------------------|-------------|
| RCC_AHB1Periph | 描述 |
| RCC_APB1Periph_TIM2 | TIM2 时钟 |
| RCC_APB1Periph_TIM3 | TIM3 时钟 |
| RCC_APB1Periph_TIM4 | TIM4 时钟 |
| RCC_APB1Periph_WWDG | WWDG时钟 |
| RCC_APB1Periph_SPI2 | SPI2 时钟 |
| RCC_APB1Periph_USART2 | USART2 时钟 |
| RCC_APB1Periph_USART3 | USART3 时钟 |
| RCC_APB1Periph_I2C1 | I2C1 时钟 |
| RCC_APB1Periph_I2C2 | I2C2 时钟 |
| RCC_APB1Periph_USB | USB 时钟 |
| RCC_APB1Periph_CAN | CAN时钟 |
| RCC_APB1Periph_BKP | BKP时钟 |
| RCC_APB1Periph_PWR | PWR 时钟 |
| RCC_APB1Periph_ALL | 全部 APB1外设时钟 |

例:

RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP | RCC_APB1Periph_PWR,

ENABLE);

//

|--------|-------------------------------------------------------------------------|
| 函数名 | RCC_AHBPeriphClockCmd |
| 函数原形 | void RCC_AHBPeriphClockCmd(u32 RCC_AHBPeriph, FunctionalState NewState) |
| 功能描述 | 使能或者失能 AHB 外设时钟 |
| 输入参数 1 | RCC_AHBPeriph: 门控 AHB 外设时钟 |
| 输入参数 2 | NewState:指定外设时钟的新状态 这个参数可以取:ENABLE 或者 DISABLE |

RCC_AHBPeriph

该参数被门控的AHB外设时钟,可以取下表的一个或者多个取值的组合作为该参数的值。

|---------------------|---------|
| RCC_AHBPeriph | 描述 |
| RCC_AHBPeriph_DMA | DMA时钟 |
| RCC_AHBPeriph_SRAM | SRAM 时钟 |
| RCC_AHBPeriph_FLITF | FLITF时钟 |

  1. SRAM和FLITF时钟只能在睡眠(SLEEP)模式下被失能。

例:

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA);

STM32_GPIO设置

2012-03-21 21:39

|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| (1)GPIO_Mode_AIN 模拟输入 (2)GPIO_Mode_IN_FLOATING 浮空输入 (3)GPIO_Mode_IPD 下拉输入 (4)GPIO_Mode_IPU 上拉输入 (5)GPIO_Mode_Out_OD 开漏输出 (6)GPIO_Mode_Out_PP 推挽输出 (7)GPIO_Mode_AF_OD 复用开漏输出 (8)GPIO_Mode_AF_PP 复用推挽输出 对于刚入门的新手,我想这几个概念是必须得搞清楚的,平时接触的最多的也就是推挽输出、开漏输出、上拉输入这三种,但一直未曾对这些做过归纳。因此,在这里做一个总结: 推挽输出:可以输出高,低电平,连接数字器件; 推挽结构一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通的时候另一个截止。高低电平由IC的电源低定。 注意:推挽和开漏会导致电路的不一样!推挽如同51的P2口,开漏就是P0口 开漏形式的电路有以下几个特点: 1. 利用外部电路的驱动能力,减少IC内部的驱动。当IC内部MOSFET导通时,驱动电流是从外部的VCC流经R pull-up ,MOSFET到GND。IC内部仅需很下的栅极驱动电流。 2. 一般来说,开漏是用来连接不同电平的器件,匹配电平用的,因为开漏引脚不连接外部的上拉电阻时,只能输出低电平,如果需要同时具备输出高电平的功能,则需要接上拉电阻,很好的一个优点是通过改变上拉电源的电压,便可以改变传输电平。比如加上上拉电阻就可以提供TTL/CMOS电平输出等。( 上拉电阻的阻值决定了逻辑电平转换的沿的速度 。阻值越大,速度越低功耗越小,所以负载电阻的选择要兼顾功耗和速度。3. OPEN-DRAIN提供了灵活的输出方式,但是也有其弱点,就是带来上升沿的延时。因为上升沿是通过外接上拉无源电阻对负载充电,所以当电阻选择小时延时就小,但功耗大;反之延时大功耗小。所以如果对延时有要求,则建议用下降沿输出。 4. 可以将多个开漏输出的Pin,连接到一条线上。通过一只上拉电阻,在不增加任何器件的情况下,形成"与逻辑"关系。这也是I2C,SMBus等总线判断总线占用状态的原理。补充:什么是"线与"?: 在一个结点(线)上, 连接一个上拉电阻到电源 VCC 或 VDD 和 n 个 NPN 或 NMOS 晶体管的集电极 C 或漏极 D, 这些晶体管的发射极 E 或源极 S 都接到地线上, 只要有一个晶体管饱和, 这个结点(线)就被拉到地线电平上. 因为这些晶体管的基极注入电流(NPN)或栅极加上高电平(NMOS),晶体管就会饱和, 所以这些基极或栅极对这个结点(线)的关系是或非 NOR 逻辑. 如果这个结点后面加一个反相器, 就是或 OR 逻辑. 其实可以简单的理解为:在所有引脚连在一起时,外接一上拉电阻,如果有一个引脚输出为逻辑0,相当于接地,与之并联的回路"相当于被一根导线短路",所以外电路逻辑电平便为0,只有都为高电平时,与的结果才为逻辑1。 由于浮空输入一般多用于外部按键输入,结合图上的输入部分电路,我理解为浮空输入状态下,IO的电平状态是不确定的,完全由外部输入决定,如果在该引脚悬空的情况下,读取该端口的电平是不确定的。 上拉输入/下拉输入/模拟输入:这几个概念很好理解,从字面便能轻易读懂。 复用开漏输出、复用推挽输出:可以理解为GPIO口被用作第二功能时的配置情况(即并非作为通用IO口使用) 最后总结下使用情况: 在STM32中选用IO模式 (1) 浮空输入_IN_FLOATING ------浮空输入,可以做KEY识别,RX1 (2)带上拉输入_IPU------IO内部上拉电阻输入 (3)带下拉输入_IPD------ IO内部下拉电阻输入 (4) 模拟输入_AIN ------应用ADC模拟输入,或者低功耗下省电 (5)开漏输出_OUT_OD ------IO输出0接GND,IO输出1,悬空,需要外接上拉电阻,才能实现输出高电平。当输出为1时,IO口的状态由上拉电阻拉高电平,但由于是开漏输出模式,这样IO口也就可以由外部电路改变为低电平或不变。可以读IO输入电平变化,实现C51的IO双向功能 (6)推挽输出_OUT_PP ------IO输出0-接GND, IO输出1 -接VCC,读输入值是未知的 (7)复用功能的推挽输出_AF_PP ------片内外设功能(I2C的SCL,SDA) (8)复用功能的开漏输出_AF_OD------片内外设功能(TX1,MOSI,MISO.SCK.SS) STM32设置实例: (1)模拟I2C使用开漏输出_OUT_OD,接上拉电阻,能够正确输出0和1;读值时先GPIO_SetBits(GPIOB, GPIO_Pin_0);拉高,然后可以读IO的值;使用GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0); (2)如果是无上拉电阻,IO默认是高电平;需要读取IO的值,可以使用带上拉输入_IPU和浮空输入_IN_FLOATING和开漏输出_OUT_OD; 虽然现在工作中主要使用的是51系列单片,看到STM32发展如此快,就买了一个板子开始学习,没有自己想的那么简单,只能下班回来学习,有时一个问题自己得琢磨好几晚上才解决,今晚猛然想,何不在一个论坛把自己遇到的问题写出来,看了一下几个论坛,最终绝地写在这个论坛上,学习STM32已经快半个月了,期间遇到好多问题,今晚就先写一下今晚碰到的问题,想到什么就写什么。 1 对NVIC的理解 CM3支持硬件中断嵌套,分为抢占式优先级和亚优先级,使用规则主要有,抢占优先级高级别的可以打断低级别的,同一级别的抢占优先级同时发生时,亚当优先级高的先发生中断,若是相同,则按硬件排列顺序发生。若是有一个亚优先级正在执行中断,同一级别的其它亚优先级发生时,则先挂起,等此中断执行完再执行! 从库函数中找到优先级分组模式: #define NVIC_PriorityGroup_0 ((uint32_t)0x700) /* 0 bits for pre-emption priority 4 bits for subpriority */ #define NVIC_PriorityGroup_1 ((uint32_t)0x600) /* 1 bits for pre-emption priority 3 bits for subpriority */ #define NVIC_PriorityGroup_2 ((uint32_t)0x500) /* 2 bits for pre-emption priority 2 bits for subpriority */ #define NVIC_PriorityGroup_3 ((uint32_t)0x400) /* 3 bits for pre-emption priority 1 bits for subpriority */ #define NVIC_PriorityGroup_4 ((uint32_t)0x300) /* 4 bits for pre-emption priority 0 bits for subpriority */ 从中可以看出第一组只有一个级别,16个亚优先级,我可以这样理解,若是分配成这个组里,不能发生嵌套中断,同时发生中断时,亚优先级高的先发生,若有中断执行时,必须等中断执行完才能执行下一个中断。最后一组正好相反,有15个级别,若是执行一个中断,可以最多嵌套15个中断执行一个中断。看下面的例子: NVIC_InitTypeDef NVIC_InitStructure; //定义中断初始化类型结构体变量 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //配置优先级分组1 2个两个抢占优先级 8个亚优先级 NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //开口外部中断0 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//配置0号抢占式优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//亚优先级配置为0号 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能通道 NVIC_Init(&NVIC_InitStructure); //对外部中断0进行初始化配置 NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; //开口外部中断5到9 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//配置1号抢占式优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//亚优先级配置为1号 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能通道 NVIC_Init(&NVIC_InitStructure); //对外部中断0进行初始化配置 NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn; //ADC1中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//配置1号抢占式优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//亚优先级配置为1号 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能通道 NVIC_Init(&NVIC_InitStructure); //对外部中断0进行初始化配置 从上面的配置来看,外部中断0的优先级最高,可以打断ADC和外部5到9的中断,也就说可以嵌套发生,当ADC中断和外部5到9中断同时发生时,它们的抢占优先级别相同,亚优先级别也相同,因为ADC1硬件排在更靠前,则先发生ADC中断,若是两者任何一个中断正在执行,则等此中断执行完,再去执行另一个中断。 |