一、GPIO基础
问题:GPIO指的是什么?是不是引脚越多越好?
General Purpose Input Output通用的输入输出接口;其实引脚够用就好,选择适用数目合理的芯片便可。
STM8单片机GPIO所支持的模式:
1、输入模式及特点
悬浮输入/浮空输入模式
特点:
输入阻抗高,可以用于模拟信号转换为数字信号的场合适用,常用于ADC的类型,保持完整的模拟信号特征
上拉输入模式
特点:
R1(30-80K,55K是常用值),将引脚上不确定的电平信号通过一个上拉电阻到高电平,电阻同时起限流的作用。
- 其中R越大,其作用越小,称为弱上拉;
- 其中R越小,其作用越大,称为强上拉;
保护二极管
特点:保护二极管的作用是放置从外部管教输入的电压过高(D1发挥作用降压)或者过低(D2发挥作用调节接近0V)从而导通压降比较低的锗二极管或者肖特基势垒二极管作为保护二极管。
2、输出模式及特点
推挽/互补输出
特点:
使用两个三极管或者是MOS管构成。推挽输出又简称PP结构,导通损耗小,导通耗损较小,效率较高。
开漏输出
特点:
要确定的高电平状态需要外接上拉电阻,该模式适合于做电流型驱动,其吸收电流能力较强(一般20mA以内)。驱动电流大多数都是由外部电源经过上拉电阻提供给引脚连接的电路或器件的,单片机内部几乎不需要提供驱动电流。且其可以实现线与逻辑。
真正的开漏输出模式
特点:
该引脚内部电路中不存在上拉至数字电源的P沟道MOS管,也没有与数字电源连接的保护二极管。内部电路固定,这样的引脚是不能被配置为推挽模式输出的。
二、引脚标识及配置
LQFP 薄型四方扁平式封装
- 描述符号的含义解释
O1:慢速输出,最高可以达到2MHz
O2:快速输出,最高可以达到10MHz
O3:可编程输出,复位后和慢速一致
O4:可编程输出,复位后和快速一致
- 芯片引脚特点
分布如下:
- 与GPIO功能有关的5大常用的寄存器
每一个GPIO端口都分配有:一个输出数据寄存器,一个输入数据寄存器,一个数据方向寄存器和两个控制寄存器。
- GPIO模式配置表及模式状态(可以查数据手册)
需要注意 :
- 输出均没有上拉电阻
- 输入是没有输出的所以没有P沟道
- 对于真正的开漏来说P沟道没有,连保护的上拉二极管Vdd也没有
三、配置寄存器的位运算操作
(按位或|,按位与&,按位取反~,左移>>,右移<<)
需要注意,||是逻辑或,&&是逻辑与,其结果只有01,不是位操作。
位或运算,也就是置1操作,将某一个位变成1
eg:
PB_DDR |= 0x44; PB_DDR |= (0x1<<3);
位与运算,也就是清0操作,将某一个位变成0
eg:
PB_DDR &= 0x44; PB_DDR &= ~(0x1<<3);
取值运算,读取端口状态并赋值给变量“x”
eg:
x = (PB_IDR >> 2) & 0x01; //读取PB2端口状态
配置PB组端口高4位为2MHz斜率开漏输出模式,低四位为10MHz斜率推晚输出模式,让端口输出“01010111”电平状态
PB_DDR = 0xFF; PB_CR1 = 0x0F; PB_CR2 = 0x0F; PB_ODR = 0x57
其中,由于IAR为我们编写了一个五千多行的头文件,可以支持直接操作相关位,比较的方便,而STVD是不支持的,也就是两个软件代码进行移植的时候需要进行更改。IAR头文件多带来的遍历:
/*--------------------------------------------------------- * Port B bit fields *-------------------------------------------------------*/ #ifdef __IAR_SYSTEMS_ICC__ #define PB_ODR_ODR0 PB_ODR_bit.ODR0 #define PB_ODR_ODR1 PB_ODR_bit.ODR1 #define PB_ODR_ODR2 PB_ODR_bit.ODR2 #define PB_ODR_ODR3 PB_ODR_bit.ODR3 #define PB_ODR_ODR4 PB_ODR_bit.ODR4 #define PB_ODR_ODR5 PB_ODR_bit.ODR5 #define PB_ODR_ODR6 PB_ODR_bit.ODR6 #define PB_ODR_ODR7 PB_ODR_bit.ODR7 #define PB_IDR_IDR0 PB_IDR_bit.IDR0 #define PB_IDR_IDR1 PB_IDR_bit.IDR1 #define PB_IDR_IDR2 PB_IDR_bit.IDR2 #define PB_IDR_IDR3 PB_IDR_bit.IDR3 #define PB_IDR_IDR4 PB_IDR_bit.IDR4 #define PB_IDR_IDR5 PB_IDR_bit.IDR5 #define PB_IDR_IDR6 PB_IDR_bit.IDR6 #define PB_IDR_IDR7 PB_IDR_bit.IDR7 #define PB_DDR_DDR0 PB_DDR_bit.DDR0 #define PB_DDR_DDR1 PB_DDR_bit.DDR1 #define PB_DDR_DDR2 PB_DDR_bit.DDR2 #define PB_DDR_DDR3 PB_DDR_bit.DDR3 #define PB_DDR_DDR4 PB_DDR_bit.DDR4 #define PB_DDR_DDR5 PB_DDR_bit.DDR5 #define PB_DDR_DDR6 PB_DDR_bit.DDR6 #define PB_DDR_DDR7 PB_DDR_bit.DDR7 #define PB_CR1_C10 PB_CR1_bit.C10 #define PB_CR1_C11 PB_CR1_bit.C11 #define PB_CR1_C12 PB_CR1_bit.C12 #define PB_CR1_C13 PB_CR1_bit.C13 #define PB_CR1_C14 PB_CR1_bit.C14 #define PB_CR1_C15 PB_CR1_bit.C15 #define PB_CR1_C16 PB_CR1_bit.C16 #define PB_CR1_C17 PB_CR1_bit.C17 #define PB_CR2_C20 PB_CR2_bit.C20 #define PB_CR2_C21 PB_CR2_bit.C21 #define PB_CR2_C22 PB_CR2_bit.C22 #define PB_CR2_C23 PB_CR2_bit.C23 #define PB_CR2_C24 PB_CR2_bit.C24 #define PB_CR2_C25 PB_CR2_bit.C25 #define PB_CR2_C26 PB_CR2_bit.C26 #define PB_CR2_C27 PB_CR2_bit.C27 #endif
具体操作:
四、流水灯与按键
1、左移流水灯
void LED_Left_Play2(void) { u8 x = 0xFE; u8 i = 0; for(i = 0; i <4; i++){ PI_ODR = x; delay_ms(20); x = x << 1; x |= 0x01; } }
2、右移流水灯
void LED_Right_Play2(void) { u8 x = 0xF7; u8 i = 0; for(i = 0; i < 4; i++){ PI_ODR = x; delay_ms(20); x = x >> 1; x |= 0x80; } }
3、来回循环
void LED_Left_Play(void) //左移流水灯 { PI_ODR_ODR0 = 0; PI_ODR_ODR3 = 1; delay_ms(20); PI_ODR_ODR1 = 0; PI_ODR_ODR0 = 1; delay_ms(20); PI_ODR_ODR2 = 0; PI_ODR_ODR1 = 1; delay_ms(20); PI_ODR_ODR3 = 0; PI_ODR_ODR2 = 1; } void LED_Right_Play(void) //右移流水灯 { PI_ODR_ODR3 = 0; PI_ODR_ODR0 = 1; delay_ms(20); PI_ODR_ODR2 = 0; PI_ODR_ODR3 = 1; delay_ms(20); PI_ODR_ODR1 = 0; PI_ODR_ODR2 = 1; delay_ms(20); PI_ODR_ODR0 = 0; PI_ODR_ODR1 = 1; }
4、流水灯数组设计
void LED_MyDesign(void) { u8 MyLed[8] = {0xFE,0xF7,0xFE,0xF7,0xFD,0xFB,0xFD,0xFB}; u8 MyLed2[8] = {0xF6,0xF9,0xF6,0xF9,0xFA,0xF5,0xFA,0xF5}; u8 MyLed3[9]={0xF6,0xF9,0xF6,0xF9,0xFF,0xF0,0xFF,0xF0,0xFF}; u8 i; for(i = 0; i < 9; i++){ PI_ODR = MyLed3[i]; delay_ms(20); } for(i = 0; i < 8; i++){ PI_ODR = MyLed[i]; delay_ms(20); } for(i = 0; i < 8; i++){ PI_ODR = MyLed2[i]; delay_ms(20); } }
5、按键控制Led灯
#define KEY8 PA_IDR_IDR1 //输入量 #define LED0 PI_ODR_ODR0 //输出量 void Key_For_Led(void) //PA1控制K8 { //键盘按键控制LED灯,PA1控制LED0 PI_DDR_DDR0 = 1; PI_CR1_C10 = 1; PI_CR2_C20 = 0; LED0 = 1; //熄灭 PA_DDR_DDR1 = 0; PA_CR1_C11 = 1; PA_CR2_C21 = 0; while(1){ if(KEY == 0){ //按键按下 delay_ms(5); //消抖 if(KEY == 0){//再次确认按键按下 LED = !LED; //控制LED灯 } while(!KEY); //如果一直处于按下则等待,很关键,需等待到按键松开再启动新一轮的循环 } // while(!KEY); //错误的地方,应该为按键按下的时间段中,所以应该包含在if语句中 } } void delay_ms(u32 time) { u32 i,j; while(time--){ for(i = 0; i < 10; i++){ for(j = 0; j < 100; j++); } } }
6、调试总代码
#include "iostm8s208mb.h" #define u8 uint8_t #define u16 uint16_t #define u32 uint32_t typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned long uint32_t; #define KEY8 PB_IDR_IDR1 //输入量 #define LED0 PI_ODR_ODR0 //输出量 void Key_For_Led(void); void GPIO_Init(void); void LED_light(void); void LED_Right_Play(void); void LED_Left_Play(void); void LED_Right_Play2(void); void LED_Left_Play2(void); void LED_MyDesign(void); void delay_ms(u16 time); u32 count ; int main() { //GPIO_Init(); /* 闪烁灯 PI_ODR_ODR2 = 0; PI_ODR_ODR1 = 0; while(count--){ LED_light(); } */ /* 花样流水灯 while(1) LED_MyDesign(); count = 3; while(count--) LED_Right_Play2(); count = 3; while(count--) LED_Left_Play2(); count = 3; while(count--){ //来回移动 LED_Left_Play(); LED_Right_Play(); } */ Key_For_Led(); } void GPIO_Init(void) //GPIO初始化 { /* PI_DDR_DDR2 = 1; PI_CR1_C12 = 1; PI_CR2_C22 = 0; PI_DDR_DDR1 = 1; PI_CR1_C11 = 1; PI_CR2_C21 = 0; PI_DDR |= 0x09; PI_CR1 |= 0x09; PI_CR2 &= 0xF6; */ PI_DDR |= 0x0F; PI_CR1 |= 0x0F; PI_CR2 &= 0xF0; PI_ODR |= 0x0F; } void Key_For_Led(void) //PA1控制K8 { //键盘按键控制LED灯,PA1控制LED0 PI_DDR_DDR0 = 1; PI_CR1_C10 = 1; PI_CR2_C20 = 0; LED0 = 1; //熄灭 PB_DDR_DDR1 = 0; PB_CR1_C11 = 1; PB_CR2_C21 = 0; while(1){ if(KEY == 0){ //按键按下 delay_ms(5); //消抖 if(KEY == 0){//再次确认按键按下 LED = !LED; //控制LED灯 } while(!KEY); //如果一直处于按下则等待 } } } void LED_MyDesign(void) { u8 MyLed[8] = {0xFE,0xF7,0xFE,0xF7,0xFD,0xFB,0xFD,0xFB}; u8 MyLed2[8] = {0xF6,0xF9,0xF6,0xF9,0xFA,0xF5,0xFA,0xF5}; u8 MyLed3[9]={0xF6,0xF9,0xF6,0xF9,0xFF,0xF0,0xFF,0xF0,0xFF}; u8 i; for(i = 0; i < 9; i++){ PI_ODR = MyLed3[i]; delay_ms(20); } for(i = 0; i < 8; i++){ PI_ODR = MyLed[i]; delay_ms(20); } for(i = 0; i < 8; i++){ PI_ODR = MyLed2[i]; delay_ms(20); } } void LED_light(void) //闪烁灯 { PI_ODR &= 0xF6; delay_ms(20); PI_ODR |= 0x09; delay_ms(20); } void LED_Right_Play2(void) { u8 x = 0xF7; u8 i = 0; for(i = 0; i < 4; i++){ PI_ODR = x; delay_ms(20); x = x >> 1; x |= 0x80; } } void LED_Left_Play2(void) { u8 x = 0xFE; u8 i = 0; for(i = 0; i <4; i++){ PI_ODR = x; delay_ms(20); x = x << 1; x |= 0x01; } } void LED_Left_Play(void) //左移流水灯 { PI_ODR_ODR0 = 0; PI_ODR_ODR3 = 1; delay_ms(20); PI_ODR_ODR1 = 0; PI_ODR_ODR0 = 1; delay_ms(20); PI_ODR_ODR2 = 0; PI_ODR_ODR1 = 1; delay_ms(20); PI_ODR_ODR3 = 0; PI_ODR_ODR2 = 1; } void LED_Right_Play(void) //右移流水灯 { PI_ODR_ODR3 = 0; PI_ODR_ODR0 = 1; delay_ms(20); PI_ODR_ODR2 = 0; PI_ODR_ODR3 = 1; delay_ms(20); PI_ODR_ODR1 = 0; PI_ODR_ODR2 = 1; delay_ms(20); PI_ODR_ODR0 = 0; PI_ODR_ODR1 = 1; } void delay_ms(u16 time) { u8 i,j; while(time--){ for(i = 0; i < 50; i++){ for(j = 0; j < 20; j++); } } }