STM32F103学习3:通过器件参考手册和具体程序学习I/O输出操作(MDK软件仿真+硬件实验)

首先还是贴一下这个LED例程的部分代码:

 1 int main()
 2 {
 3     
 4     Stm32_Clock_Init();//系统时钟设置
 5     RCC->APB2ENR |= 0x00000001; //开启afio时钟
 6    // AFIO->MAPR = (0x00FFFFFF & AFIO->MAPR)|0x04000000;          //关闭JTAG             
 7 
 8     RCC->APB2ENR|=0X0000001c;//先使能外设IO PORTa,b,c时钟
 9     
10     GPIOB->CRH=0X33333333;    //推挽输出
11     GPIOB->CRL=0X33333333;    //推挽输出
12     GPIOC->CRH=0X33333333;    //推挽输出
13     GPIOC->CRL=0X33333333;    //推挽输出
14     GPIOA->CRH=0X33333333;    //推挽输出
15     GPIOA->CRL=0X33333333;    //推挽输出
16 
17      while (1)
18     {            
19     delay_ms(50);
20               
21     GPIOB->ODR=0;               //全部输出0
22     GPIOA->ODR=0;        
23     GPIOC->ODR=0;
24     
25     delay_ms(50);
26     
27     GPIOB->ODR=0xffffffff;     //全部输出1 
28     GPIOA->ODR=0xffffffff;      
29     GPIOC->ODR=0xffffffff;
30     }
31 }

时钟设置先暂时不看了,先看I/O部分的操作

1、I/O输出模式设置

GPIOB->CRH=0X33333333;    //推挽输出

在器件手册中75页找到CRH寄存器列表:

这里可以看到y=8...15意思是能控制8~15腿

以一个管脚为例,0x3等于二进制的0011,

MODEy的1:0位为11,即为输出模式,最大速度为50MHz。(BTW:点一个闪烁的LED灯哪能到得了这么快的速度)

在此基础上,CNFy为00,即为输出模式的推挽输出模式。

 

GPIOB->CRL=0X33333333;    //推挽输出

CRL寄存器的图就不贴了,不同就是这里的y=0...7,能控制0~7腿

GPIOA、GPIOB、GPIOC,所有的管脚都设置为了推挽输出的模式

 

管脚模式这就设置完成了,下面看一下管脚操作:

2.管脚置高、管脚置低

GPIOB->ODR=0;               //全部输出0

结合这一句来看:

GPIOB->ODR=0xffffffff;     //全部输出1 

一会儿全1,一会儿全0,有点《三体》里整个宇宙都同时为你而闪烁的意味呢。。。

 

我们找到ODR寄存器:

发现这个寄存器没法进行单独位的操作,只能以16位的形式操作。

提示说独立位控制的置1置0还是去看看BSRR寄存器吧

y=0~15

给31:16位赋值1则置低该位,给15:0为赋值1则置高该位,但是哪个赋值0的话那个管脚就不会鸟你。而置高是优先的。

(这样就可以让宇宙中的某个星系为你单独的闪烁了!)

 

首先修改代码:

注释掉全部涉及ODR寄存器的内容:

在原理图中找到LED管脚号为:PB8

修改程序为:

GPIOB->BSRR=0X01000000; //PB8置0
GPIOB->BSRR=0X0100;     ////PB8置1

Rebuild一下

 

我们先用软件仿真试试:

在工具栏flash的configure flash tools中,Debug选项卡中,点击Use Simulator,点击OK:

下面点击这个小放大镜进入Debug模式

我们发现整个界面发生了完全的变化

我们使用Analysis windows 查看一下PB8随时间变化的波形以此来判断I/O是否正确的操作了

点击:

 

出现了一个叫逻辑分析仪的界面

这篇帖子是keil的软件逻辑分析仪( logic analyzer)使用教程,我再次不再赘述了:

http://www.eeboard.com/bbs/thread-29726-1-1.html

结果:

成功的实现了I/O端口的翻转

 

但是发现还有个寄存器可以实现BSRR寄存器的高16位的置0功能:端口位清除寄存器(GPIOx_BRR)

我们退出Debug模式修改程序试试:

//    GPIOB->BSRR=0X01000000;     //PB8置0
    GPIOB->BRR=0X0100;     //PB8置0        

Rebuild一下,进入Debug模式:

仿真一下,发现结果是一样的!

但是,BSRR提供给了用户一种可能,就是用一个语句,同时置高置低不同的几个端口

 

 

3、在实际的STM32开发中,我们更多的应该是利用库函数的操作

3.5版本的库在这里下载:http://www.st.com/web/en/catalog/tools/PF257890

(现在ST公司推荐使用STM32cube工具开发,有时间我也试试。

我用的MDK4.12自带的是2.0.1的库,折腾了半天,终于设置好了库的模版。

 

再找找库的函数手册,找到了UM0427用户手册的PDF版本。查看一下GPIO部分的内容:

总共17个函数,先重点研究管脚初始化和输出操作的函数吧

(1)GPIO_Init()

功能:根据 GPIO_InitStruct 中指定的参数初始化外设 GPIOx 寄存器

typedef struct
{
  u16 GPIO_Pin;
  GPIOSpeed_TypeDef GPIO_Speed;
  GPIOMode_TypeDef GPIO_Mode;
}GPIO_InitTypeDef;

这样一个表格加一些代码愣愣的杵在这里看着好像有点发晕,有点乱。不如我们先看例子吧,这样好理解一些。

/* Configure all the GPIOA in Input Floating mode */
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);

这下明白了,我们初始化了一个名叫GPIO_InitStructure的结构体,结构体的原型是GPIO_InitTypeDef。

结构体GPIO_InitTypeDef里面有三个成员,一个是GPIO_Pin,管的是初始化哪个管脚。第二个是GPIO_Speed,管的是管脚的最高速度是多少。第三个是GPIO_Mode,管的是I/O管脚的模式是什么,也就是是输出还是输入,是开漏还是推挽。

(具体的GPIO_Pin、GPIO_Speed、GPIO_Mode这三者的值,在库文件手册上有表格,也不一一列举了。把UM0427手册下载下来看就OK了。)

最后,再调用一下GPIO_Init()函数,把GPIOA传进去,再把GPIO_InitStructure的地址传进去。

这样就初始化好了管脚A。

 

我们把LED的例程改改试一试 ;) 试试这样初始化能不能工作。但是还有个管脚时钟设置的问题,在下载到的3.5的库中找到了GPIO的例程,在初始化时添加这一句

  /* GPIOB Periph clock enable */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

初始化PB8管脚为最大速度10MHz推挽输出:

  /* Configure PD8 in output pushpull mode */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOB, &GPIO_InitStructure);

编译,仿真看一下结果,波形没问题,使用库文件初始化成功了

(2)GPIO_SetBits()

(3)GPIO_ResetBits()

这两个函数使用上很简单

/* Set the GPIOA port pin 10 and pin 15 */
GPIO_SetBits(GPIOA, GPIO_Pin_10 | GPIO_Pin_15);
/* Clears the GPIOA port pin 10 and pin 15 */
GPIO_ResetBits(GPIOA, GPIO_Pin_10 | GPIO_Pin_15);

照着把管脚改成PB8就ok了

 (4)GPIO_WriteBit()

示例如下

/* Set the GPIOA port pin 15 */
GPIO_WriteBit(GPIOA, GPIO_Pin_15, Bit_SET);

想置低就把第二个参数改为Bit_RESET
(5)GPIO_Write()

向指定数据端口写入数据

我们看一下函数定义:

/**
  * @brief  Writes data to the specified GPIO data port.
  * @param  GPIOx: where x can be (A..G) to select the GPIO peripheral.
  * @param  PortVal: specifies the value to be written to the port output data register.
  * @retval None
  */
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal)
{
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  
  GPIOx->ODR = PortVal;
}

实际上就是通过写入ODR寄存器直接实现了端口的并行I/O操作

改一下代码: 

    GPIO_Write(GPIOB,0x0100) ;     //PB8置1
    GPIO_Write(GPIOB,0x0000) ;       //PB8置0

 

因为之前的程序灯在闪烁的定时使用的delay延时,下一篇博文学习一下定时器的配置。用定时器触发灯亮灭的转换。

I/O的输入功能因为得连接硬件,放在学习5吧。

posted on 2015-12-29 18:54  深深的河流  阅读(1623)  评论(1编辑  收藏  举报

导航