STM32(2)——GPIO
对于初学者而言,最简单的是对芯片上的IO进行操作,我们学习ARM时候,第一个工程就是点亮LED,STM32F103ZET6通用输入输出接口(General-Purpose Inputs/Outputs),每个GPIO都可以由软件配置成输出(推免或开漏)、输入(带或不带上拉或下拉)或复用的外设功能端口。多数GPIO引脚都与数字或模拟的复用外设共用。具体的细节请参考Datasheet。
回到MDK开发平台,现在要在main.c中加入相关代码了。代码清单如下:
#include "stm32f10x_lib.h" int main()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOB, ENABLE); //开启外设时钟
GPIOD->CRL = 0x33333333; //设置端口配置寄存器
GPIOB->CRL = 0x33333333;
while(1)
{
GPIOD->ODR = 0xffffffbf; //设置端口输出寄存器
for(i=0;i<1000000;i++); //延时
GPIOD->ODR = 0xffffffff7;
for(i=0;i<1000000;i++);
GPIOD->ODR = 0x00000000;
GPIOB->ODR = 0xffffffff;
for(i=0;i<1000000;i++);
GPIOB->ODR = 0x00000000;
}
}
上述代码中,#include "stm32f10x_lib.h"包含了开发stm32f10x系列芯片所需的基本头文件,在进行程序编写的时候,务必要包含此头文件。
RCC_APB2PeriphClockCmd()函数是设置外设时钟。ARM与C51单片机不同的是,不用外设的时候,如IO口、ADC、定时器等等,都是禁止时钟的,以达到节能的目的,只有要用到的外设,才开启它的时钟。因此在需要用到GPIOB和GPIOD的时候,我们需要先开启它的时钟,具体用到的是函数库里面的函数:
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
其中,第一个参数需要指示要开启什么端口的时钟,RCC_APB2Periph_GPIOx就是开启GPIOx的时钟,第二个参数需要指示是开启还是关闭,ENABLE/DISABLE。
开启外设时钟之后,然后就开始对GPIO的配置寄存器进行设置了。具体设置方式参考《基于MDK的STM32处理器开发应用》一书中,“7.1 通用IO端口”。While循环里面就是给GPIO的端口输出寄存器赋值,由于我手上这款开发板三个LED灯分别接的是PG13、PG14和PG15,所以只要将G端口相应的位上置1就可以了。
编译之后我们发现编译器报错,Undefined symbol RCC_APB2PeriphClockCmd,是因为我们使用了的RCC_APB2PeriphClockCmd()函数在头文件中声明了,却没有在C文件中定义,这个函数在Keil\ARM\RV31\LIB\ST\STM32F10x\stm32f10x_rcc.c中,将这个文件复制到工程的根目录下,然后在屏幕左边的Workspace中添加进来,就可以了。
其实在MDK的库中,还定义了很多宏,可以避免让我们自己去查找相关资料来设置寄存器的各个位。
对于LED的亮灭可用以下代码进行实现:
#include "stm32f10x.h"
static u8 fac_us=0; //us延时倍乘数
static u16 fac_ms=0; //ms延时倍乘数,在ucos下代表每个节拍的ms数
void delay_init(void);
void delay_ms(u16 nms);
void LED_Init(void);//初始化
int main(void)
{ delay_init(); //初始化延时函数
LED_Init(); //初始化LED端口 while(1)
{
GPIO_ResetBits(GPIOG,GPIO_Pin_13); //亮 等同LED0=0;
GPIO_SetBits(GPIOG,GPIO_Pin_14); //灭 等同LED1=1;
GPIO_SetBits(GPIOG,GPIO_Pin_15); //灭 等同LED2=1;
delay_ms(500); //延时500ms
GPIO_SetBits(GPIOG,GPIO_Pin_13);
GPIO_ResetBits(GPIOG,GPIO_Pin_14);
GPIO_SetBits(GPIOG,GPIO_Pin_15);
delay_ms(500);
GPIO_SetBits(GPIOG,GPIO_Pin_13);
GPIO_SetBits(GPIOG,GPIO_Pin_14);
GPIO_ResetBits(GPIOG,GPIO_Pin_15);
delay_ms(500);
}
}
void LED_Init(void) //LED对应IO初始化
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, ENABLE); //使能GPIO时种
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; //LED0,LED1,LED2对应IO口
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推免输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO速度为50Mhz
GPIO_Init(GPIOG, &GPIO_InitStructure); //初始化GPIO
GPIO_SetBits(GPIOG,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); //GPIOG13,G14,G15设置高,灯灭 void delay_init() //延时函数初始化
{
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
fac_us=SystemCoreClock/8000000;
fac_ms=(u16)fac_us*1000; //每个ms需要的systick时钟数
}
void delay_ms(u16 nms) //延时nms
{
u32 midtime;
SysTick->LOAD=(u32)nms*fac_ms;//时间加载(SysTick->LOAD为24bit)
//SysTick->LOAD为24位寄存器,所以最大延时为:nms<=0xffffff*8*1000/SYSCLK对72M条件下,nms<=1864ms
SysTick->VAL =0x00; //清空计时器÷
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数
do
{
midtime=SysTick->CTRL;
}
while((midtime&0x01)&&!(midtime&(1<<16)));//等待时间到达
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
由于我们使用了GPIO_InitTypeDef类型,所以我们需要找到它的定义,这个定义包含在“…\Keil\ARM\RV31\LIB\ST\STM32F10x\stm32f10x_gpio.c”中,将文件复制到工程根目录下,然后再添加进入工程中,编译才不会报错。
在绝大多数C编译器中,要求所有的变量声明都在执行语句块之前,也就是说如果需要定义的变量需要先在进入main函数一开始就全部定义好,如果执行了某一条语句之后再定义变量的话,就会报错。
部分参考:http://blog.sina.com.cn/s/blog_49cb42490100robb.html
补充(一):
有三种方式可以控制LED亮灭:
1、通过位段操作实现IO口控制;
int main(void)
{ delay_init(); //初始化延时函数
LED_Init(); //初始化LED端口 while(1)
{
GPIO_bits_OUT(GPIOG,13,3,0x0006);
delay_ms(500);
GPIO_bits_OUT(GPIOG,13,3,0x0005);
delay_ms(500);
GPIO_bits_OUT(GPIOG,13,3,0x0003);
delay_ms(500);
}
} /*以下:*GPIOx:对应的IO口,start_bit:并行输出的起始值,bit_size:并行输出的位数*/ void GPIO_bits_OUT(GPIO_TypeDef* GPIOx, u8 start_bit, u8 bit_size,u16 outdata)
{
u8 i=0;
u16 bu1=0;u16 middata=1; if( bit_size>(16-start_bit) )
bit_size=16-start_bit; i=start_bit;
if(i>0)
{
while(i--)
{ bu1+=middata; middata*=2;}
} assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); GPIOx->ODR&=( ( (0xffff<<bit_size) <<start_bit ) |bu1 );
GPIOx->ODR|=(outdata<<start_bit);
}
2、通过位带操作实现IO口控制;
3、通过库函数直接操作实现IO控制;
补充(二):STM32中GPIO的8种工作模式
- 模拟输入 用于模拟量的输入,或者低功耗下省电。
- 下拉输入 端口内下拉电阻电路导通。默认为低电平,外部高电平动作。
- 上拉输入 端口内上拉电阻电路导通,默认为高电平,外部低电平动作。
- 浮空输入 端口内上下MOSFET均不导通,高阻态,输入状态仅由端口决定。
- 开漏输出 端口输出为0时内部下拉电阻电路接地,输出1时端口相当于悬空,即默认只能输出0,如果外部需要输出1,需要外接上拉电阻电路。
- 推挽输出 端口输出为0时内部下拉电阻电路接地,输出1时内部接上拉电阻电路,默认输出为0。
- 复用开漏输出 内部设置同开漏,但被其他外设复用。
- 复用推挽输出 内部设置同推挽,但被其他外设复用。
小补充:
- 上拉电阻就是:将一个不确定信号(高或低电平),通过一个电阻与电源VCC相连,固定在高电平;
- 下拉电阻就是:将一个不确定信号(高或低电平),通过一个电阻与地GND相连,固定在低电平。
- 作为普通的GPIO输入:根据需要配置该引脚为浮空输入、带弱上拉输入或带弱下拉输入,同时不要使能该引脚对应的所有复用功能模块。
- 作为普通的GPIO输出:根据需要配置该引脚为推免输出或开漏输出,同时不要使能该引脚对应的所有复用功能。
- 作为普通模拟输入:配置该引脚为模拟模拟输入模式,同时不要使能该引脚对应的所有复用功能。
- 作为内置外设的输入:根据需要配置该引脚为浮空输入,带弱上拉输入或带弱下拉输入,同时使能该引脚的复用功能。
- 作为内置外设的输出:根据需要配置引脚为复用推挽输出或复用开漏输出,同时使能该引脚的对应的所有复用功能模块。
STM32(2)——GPIO的更多相关文章
- STM32(6)——USART串口的使用
1. 串口的基本概念 在STM32的参考手册中,串口被描述成通用同步异步收发器(USART),它提供了一种灵活的方法与使用工业标准NRZ异步串行数据格式的外部设备之间进行全双工数据交换.USART利用 ...
- 在Mac OS X中使用VIM开发STM32(2)
本文原创于http://www.cnblogs.com/humaoxiao,非法转载者请自重! 在我先前的博文⎣在Mac OS X中使用VIM开发STM32(1)⎤中,我们安装完成了MACVIM,这一 ...
- STM32(12)——CAN
简介: CAN是Controller Area Network,是 ISO 国际标准化的串行通信协议. CAN 控制器根据两根线上的电位差来判断总线电平.总线电平分为显性电平和隐性电平,二者必居其一 ...
- (stm32学习总结)—GPIO位带操作
本章参考资料:<STM32F10X-中文参考手册>存储器和总线构架章节.GPIO 章节,<CM3 权威指南 CnR2>存储器系统章节. 位带简介 位操作就是可以单独的对一个比特 ...
- STM32(HY-SRF05)超声波测距项目
参考资料: https://www.cnblogs.com/qsyll0916/p/6964638.html http://blog.csdn.net/zhangdaxia2/article/deta ...
- STM32(4)——系统时钟和SysTick
1.STM32的时钟系统 在STM32中,一共有5个时钟源,分别是HSI.HSE.LSI.LSE.PLL HSI是高速内部时钟,RC振荡器,频率为8MHz: HSE是高速外部时钟,可接石英/陶瓷谐振器 ...
- (一)GPIO 编程实验 LED 流水灯控制
7个寄存器 是R1-R16.(当然,里面有很多是分几个模式的,所以总共有37个)类似于单片机的R0-R7. GPXCON,GPXDAT等等是另外的寄存器,应该叫,特殊功能寄存器,类似于单片机的P0,P ...
- 在Mac OS X中使用VIM开发STM32(4)
本文原创于http://www.cnblogs.com/humaoxiao,非法转载者请自重! 在上三篇文章中,我们基本搭建好了开发STM32的IDE环境,当然vim.ctags.tagl ...
- arm cortex-m0plus源码学习(三)GPIO
概述: Cortex-m0的integration_kit提供三个GPIO接口,其中GPIO0传输到外部供用户使用,为EXTGPIO:GPIO1是内核自己的信号,不能乱改,会崩掉:GPIO2是一些中断 ...
随机推荐
- jQuery 插件封装的方法
方式1.$.fn.xxx ==>针对元素添加方法: ;(function ($) { $.fn.myPlugin = function () { //你自己的插件代码 }; })(jQuer ...
- 爬虫IP被禁的简单解决方法——切换UserAgent
[转载]Python爬虫之UserAgent 用到的库 https://github.com/hellysmile/fake-useragent
- 使用dsoframer控件出现"Unable to display the inactive document. Click here to reactivate the document."的问题 .
使用如下属性设置: axFramerControl.ActivationPolicy = DSOFramer.dsoActivationPolicy.dsoKeepUIActiveOnAppDeact ...
- 沉淀,再出发:python爬虫的再次思考
沉淀,再出发:python爬虫的再次思考 一.前言 之前笔者就写过python爬虫的相关文档,不过当时因为知识所限,理解和掌握的东西都非常的少,并且使用更多的是python2.x的版本的功能,现 ...
- php懈垢windows通用上传缺陷
#1 实例介绍本案例采用的实例是:U-Mail邮件系统.U-Mail邮件系统文件上传的地方代码是这样的: code 区域 <?php if(ACTION =="attach-uploa ...
- redis缓存机制【转载】
转载自:http://blog.csdn.net/acmman/article/details/53434134 redis的主从模式(1)介绍redis存储数据是在内存中运行的,运行速度比关系型数据 ...
- PhoneGap模仿微信摇一摇功能
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- ZOJ-3279 Ants 树状数组 + 二分
题目链接: https://cn.vjudge.net/problem/ZOJ-3279 题目大意: 有1到n 那个level 每一个level有a[i]只蚂蚁两种操作 p a b 把第a个level ...
- 网络Socket编程(简易qq实现之C/S通信1)
1. 目标:实现两个用户之间的通信,利用的是简单的Socket知识以及简略界面 2. 界面:分为客户端与服务器端(如下图) 3. 基本功能:客户端先向服务器端发送一个消息,这样就可以让客户端与服务器端 ...
- 【原创】纯CSS画黄金梅丽号!
代码如下 <!DOCTYPE html> <!-- saved from url=(0055)http://jadyoap.bj.bcebos.com/ife%2FcssCatAni ...