说明
接着上一章讲。本章我们来讲一讲LED流水灯,循环点亮LED。
在LED章节有的可能没有讲到,本章会对其进行说明,尽量每个函数说一下作用。也会在最后说一下STM32的寄存器,在编程中寄存器是避免不了的东西,寄存器也是非常好理解的,就是羊肉串串成一串而已。其实对于嵌入式来说,大多数开发者都是根据官方提供的例程写的。很少只有手册让个人写的。只有做芯片底层开发的会根据,芯片的逻辑进行代码的编写。但是对于很多开发者来说,都是基于官方提供的芯片demo进行相关的底层配置与开发。
而我们要写的主要是应用层和一些模块底层的开发。而且多数的外设模块都有代码例程,即使没有文档也是较为清楚的。什么样的没有代码的,就是新出的芯片或者传感器,笔者做过恩智浦的一款新出的包括近期做一个芯驰的开发。网上资料少得可怜,只能参考官方例程。特别是芯驰的是去年10月份刚出的,他们的东西会出现各种问题,网上找不到,但是你也不要慌,他们会提供技术支持的,会告诉你怎么做。这些新出的东西,遇到了不要觉得是自己的问题,有可能是他们芯片或者demo的问题,就像近期做芯驰的开发板demo,调试,只有静置一段时间才可以(就是断电,全部断掉,等一会,这就是他们的问题),所以遇到问题不要怕。
对了,我写实战是根据F1的,和F4的本质都差不多。。
1-配置
1static void Led_Cofig(void) 2{ 3/*定义一个GPIO_InitTypeDef类型的结构体*/ 4GPIO_InitTypeDef GPIO_InitStructure; 5/*开启LED相关的GPIO外设时钟*/ 6RCC_APB2PeriphClockCmd(LED_GPIO_CLCK, ENABLE); 7/*选择要控制的GPIO引脚*/ 8GPIO_InitStructure.GPIO_Pin = (LED1_GPIO_PIN | LED2_GPIO_PIN | LED3_GPIO_PIN | LED4_GPIO_PIN | LED5_GPIO_PIN | LED6_GPIO_PIN | LED7_GPIO_PIN | LED8_GPIO_PIN); 9/*设置引脚模式为通用推挽输出*/ 10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 11/*设置引脚速率为50MHz */ 12GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //(指的是内部驱动电路的响应速度,速度越大越快,一般情况是有多个不同的速度,也可根据自己的需要安排) 13GPIO_Init(LED_GPIO_PORT, &GPIO_InitStructure); 14/* 关闭led灯 */ 15GPIO_SetBits(LED_GPIO_PORT, LED1_GPIO_PIN | LED2_GPIO_PIN | LED3_GPIO_PIN | LED4_GPIO_PIN | LED5_GPIO_PIN | LED6_GPIO_PIN | LED7_GPIO_PIN | LED8_GPIO_PIN); 16}
1.1 RCC_APB2PeriphClockCmd()函数
RCC_APB2PeriphClockCmd()函数的功能是使能GPIOx对应的外设时钟,若使能GPIOC时钟,对应的代码如下:
1RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
RCC_APB2Periph_GPIOC是在stm32f10x_rcc.h头文件中定义的。RCC_APB2Periph_GPIOA~RCC_APB2Periph_GPIOG定义的代码如下:
1#define RCC_APB2Periph_GPIOA ((uint32_t)0x00000004) 2#define RCC_APB2Periph_GPIOB ((uint32_t)0x00000008) 3#define RCC_APB2Periph_GPIOC ((uint32_t)0x00000010) 4#define RCC_APB2Periph_GPIOD ((uint32_t)0x00000020) 5#define RCC_APB2Periph_GPIOE ((uint32_t)0x00000040) 6#define RCC_APB2Periph_GPIOF ((uint32_t)0x00000080) 7#define RCC_APB2Periph_GPIOG ((uint32_t)0x00000100)
1.2 GPIO_Init()函数
GPIO_Init()函数的功能是初始化(配置)GPIO的模式和速度,也就是设置相应GPIO的CRL和CRH寄存器值。函数原型如下所示:
1void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct) 2
第一个参数是GPIO_TypeDef类型指针变量,用于确定是哪一个GPIO,GPIOx取值是GPIOA~GPIOG;第二个参数是GPIO_InitTypeDef类型指针变量,用于确定GPIOx的对应引脚以及该引脚的模式和速度等。
1.3 GPIO_ReadInputDataBit ()函数
GPIO_ReadInputDataBit ()函数的功能是读取指定I/O口的对应引脚值,也就是读取IDR(在下面会介绍)寄存器的值。函数原型是:
1uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
第一个参数同GPIO_Init()函数一样,第二个参数是读取GPIOx的对应引脚值。如读取GPIO6(即PC6)引脚代码是:
1GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_6);
1.4 GPIO_ReadInputData ()函数
GPIO_ReadInputData()函数的功能是读取指定I/O口16个引脚的输入值,也是读取IDR寄存器的值。函数原型如下:
1uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
比如读取GPIOC输入端的值:
1tem=GPIO_ReadInputData(GPIOC);
但是如果采用寄存器的话就是:
1tem=GPIOC->IDR
1.5 GPIO_ReadOutputDataBit ()和GPIO_ReadOutputData ()函数对比
这两个函数从字面就能知道一个是对单独的IO进行操作,一个是对多个IO进行操作,下面就对这两个进行分析。
1//读取GPIO某个引脚的特定输出值。 2uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
1例如:读取GPIOC中PC6的值 2GPIO_ReadOutputDataBit(GPIOC.GPIO_Pin_6); 3//读取GPIO端口指定组的值。 4uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx); 5比如读取GPIOC的值: 6GPIO_ReadOutputData( GPIOC);
1.6 GPIO_SetBits ()和GPIO_ResetBits ()函数
上一章已经说过了,本章不在多说。
函数GPIO_SetBits ()和GPIO_ResetBits ()的功能是用来设置指定I/O口的引脚输出高电平和低电平,也就是设置寄存器BSRR、BRR的值。
1.7 GPIO_WriteBit ()和GPIO_Write ()函数
这两个函数和1.5的类型是一样的。非别是对单个IO和一组IO控制,后面遇到这样的不在赘述,默认大家都知道。
GPIO_WriteBit ()函数的功能是向指定I/O口的引脚写0或者写1,也就是向寄存器ODR相应位写0或者写1。函数原型如下:
1void GPIO_WriteBit(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin, BitAction BitVal);
GPIO_Write()函数的功能是向指定I/O口写数据,也就是向寄存器ODR写数据。函数原型如下:
1void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal); 2举例: 3//向PC6写入1 4GPIO_WriteBit(GPIOC, GPIO_Pin_6, 1); 5//向GPIOC写入 0x0FFFC 6GPIO_Write(GPIOC, 0x0FFFC);
在这里需要注意一点:
GPIO_WriteBit()函数是对I/O口的一个引脚进行写操作,可以是写0或者写1;
GPIO_SetBits()函数可以对I/O口的多个引脚同时进行置位。
这点大家一定要知道。
1GPIO_WriteBit(GPIOC,GPIO_Pin_8 , 0); //只能对一个引脚置0或置1 2GPIO_SetBits(GPIOC, GPIO_Pin_2 | GPIO_Pin_4); //可以同时对多个引脚置1
2 循环点亮LED
我设计的是8个流水灯,采用共阳极的接法,与PC1~7相连接。因为32不像51有移位控制语句。
先来看代码吧,这可以说是非常基础的了,为了方便大家理解。
1void LED_Cycle(void) 2{ 3uint16_t temp, i; 4temp = 0x01; 5for (i = 0; i < 8; i++) 6{ 7GPIO_Write(LED_GPIO_PORT, ~temp); 8Delay(300); 9temp = temp << 1; 10} 11for (i = 0; i < 8; i++) 12{ 13GPIO_Write(LED_GPIO_PORT, ~temp); 14Delay(300); 15temp = temp >> 1; 16} 17}
首先我们从GPIOC0开始点亮这样一直循环下去:PC0点亮此位为0,依次类推如下:
1PC0亮:PC输出 0x0001,取反为0xFFFE,控制码就是0xFFFE 2PC1亮:PC输出 0x0002,取反为0xFFFD,控制码就是0xFFFD 3PC2亮:PC输出 0x0004,取反为0xFFFB,控制码就是0xFFFB 4PC3亮:PC输出 0x0008,取反为0xFFF7,控制码就是0xFFF7 5PC4亮:PC输出 0x0010,取反为0xFFEF,控制码就是0xFFEF 6PC5亮:PC输出 0x0020,取反为0xFFDF,控制码就是0xFFDF 7PC6亮:PC输出 0x0040,取反为0xFFBF,控制码就是0xFFBF 8PC7亮:PC输出 0x0080,取反为0xFF7F,控制码就是0xFF7F
然后重复执行。
根据上面的,只需将控制码从GPIOC输出即可。怎样输出呢,就是出事一位为高然后进行取反操作,在对其进行移位,然后进行循环操作(方法不止这一种,可尝试其他方式)。
“GPIO_Write(GPIOC,~temp);”语句将初始控制码0x0001取反(也就是0xFFFE)后,从GPIOC口输出,使得PC为低电平,点亮PC0,其他位为高电平;然后延时一段时间;让控制码移位,获得下一个控制码;然后再对控制码取反后输出到GPIOC口,就这样依次进行。
主函数
1int main() 2{ 3LED_Init(); 4while (1) 5{ 6LED_Cycle(); 7} 8}
今天太晚了,哪天腾出一章用来写各个寄存器,本来是想和库函数穿插这说的。又考虑到,有些人对寄存器不太熟悉,穿插着对其不太友好。今天就到这了,喜欢就动动手一键三连,谢谢。