【XNUCLEO-F030R8试用体验】之三:mbed开发之数字输入输出 这一节来讲一下数字输入输出。 首先是数字输入输出,上一节的测试例程就是一个数字输入输出的例子,可以看到相比于库函数,mbed的库更加简洁和好用,用户不需要关心底层的代码是怎么实现的,操作了哪些寄存器,只需要关注应用本身,大大提高了开发速度,所以后面所有的程序我们都会用官方提供的库来进行开发。 来看看官方提供的数字输入输出部分的库函数:
mbed中有三个类和数字输入输出相关,分别是DigitalIn(数字管脚输入类)、DigitalOut(数字管脚输出类)、DigitalInOut(数字管脚输入输出类),学过 STM32库函数的话应该明白这三个类提供的方法相当于GPIO库函数的功能,但是更简洁。 看完这些类和方法,我们试着点亮 开发板上的一个LED灯。 还记得第二节用到的Keil mbed工程吗?我们从mbed官网将在线工程导出为keil 4工程,就得到了一个模板,这样就不用自己建立工程,做各种复杂的工程配置了,是不是很简单呢?至于为什么不用SMeshStudio,上节提到过,XNUCLEO的ST-LINK不支持。 现在我们打开一个空的工程,main.cpp内容很简单,如下: #include "mbed.h" int main() { } 注意不同的mbed.h文件所包含的芯片型号是不一样的,具体取决于你从在线工程导出时选择的开发板型号。 现在将main.cpp改成下面这样: #include "mbed.h" DigitalOut myled(LED1); //构造DigitalOut类的一个对象myled,并初始化 int main() { while(1) { myled=1; //点亮LED wait(1); //延时1s myled=0; //熄灭LED wait(1); } } 下载程序到开发板,可以看到LED1每1s闪烁一次。现在我们来分析一下代码。程序首先构造DigitalOut类的一个对象myled,初始化参数为LED1,我们从mbed API看到,构造函数形式为:Di titalOut(PinName Pin);也就意味着LED1是某个引脚名,我们打开PinNames.h文件可以看到下面的定义: typedef enum { PA_0 = 0x00, PA_1 = 0x01, PA_2 = 0x02, PA_3 = 0x03, PA_4 = 0x04, PA_5 = 0x05, PA_6 = 0x06, PA_7 = 0x07, PA_8 = 0x08, PA_9 = 0x09, PA_10 = 0x0A, PA_11 = 0x0B, PA_12 = 0x0C, PA_13 = 0x0D, PA_14 = 0x0E, PA_15 = 0x0F, PB_0 = 0x10, PB_1 = 0x11, PB_2 = 0x12, PB_3 = 0x13, PB_4 = 0x14, PB_5 = 0x15, PB_6 = 0x16, PB_7 = 0x17, PB_8 = 0x18, PB_9 = 0x19, PB_10 = 0x1A, PB_11 = 0x1B, PB_12 = 0x1C, PB_13 = 0x1D, PB_14 = 0x1E, PB_15 = 0x1F, PC_0 = 0x20, PC_1 = 0x21, PC_2 = 0x22, PC_3 = 0x23, PC_4 = 0x24, PC_5 = 0x25, PC_6 = 0x26, PC_7 = 0x27, PC_8 = 0x28, PC_9 = 0x29, PC_10 = 0x2A, PC_11 = 0x2B, PC_12 = 0x2C, PC_13 = 0x2D, PC_14 = 0x2E, PC_15 = 0x2F, PD_2 = 0x32, PF_0 = 0x50, PF_1 = 0x51, PF_4 = 0x54, PF_5 = 0x55, PF_6 = 0x56, PF_7 = 0x57, // Arduino connector namings A0 = PA_0, A1 = PA_1, A2 = PA_4, A3 = PB_0, A4 = PC_1, A5 = PC_0, D0 = PA_3, D1 = PA_2, D2 = PA_10, D3 = PB_3, D4 = PB_5, D5 = PB_4, D6 = PB_10, D7 = PA_8, D8 = PA_9, D9 = PC_7, D10 = PB_6, D11 = PA_7, D12 = PA_6, D13 = PA_5, D14 = PB_9, D15 = PB_8, // Generic signals namings LED1 = PA_5, LED2 = PA_5, LED3 = PA_5, LED4 = PA_5, USER_BUTTON = PC_13, SERIAL_TX = PA_2, SERIAL_RX = PA_3, USBTX = PA_2, USBRX = PA_3, I2C_SCL = PB_8, I2C_SDA = PB_9, SPI_MOSI = PA_7, SPI_MISO = PA_6, SPI_SCK = PA_5, SPI_CS = PB_6, PWM_OUT = PC_7, // Not connected NC = (int)0xFFFFFFFF } PinName; 这里定义了这块开发板所用芯片的所有用户可以使用的引脚,还定义了mbed接口引脚(A0~D15),还定义了功能引脚(LED1~PWM_OUT)。LED1连接的引脚是MCU的PA_5,LED2~LED4引脚也是PA_5。事实上,初始化参数也可以是LED2~LED5,当然也可以是引脚名称。那为什么要把4个LED定义到同一个引脚呢?因为这是我们从mbed官网导出的工程,它是以NUCLEO板为原型定义的,而NUCLEO板上只有一个用户LED(通常是LED1)。我们的XNUCLEO板上有4个用户LED,分别是LED1~LED4,分别接到了不同的引脚,可以看看 电路图。
如果我们初始化的时候参数改为LED2~LED4,那么实际上点亮的还是LED1,这个应该明白了吧? 为了保持程序的兼容性,官方提供的代码我们尽量不要改动,实际在初始化的时候,我们可以用引脚号作为参数。下面的程序初始化开发板上的4个LED,并组成流水灯效果。 #include "mbed.h" DigitalOut myled1(LED1); //初始化LED1 DigitalOut myled2(PC_9); //初始化LED2 DigitalOut myled3(PC_8); //初始化LED3 DigitalOut myled4(PC_5); //初始化LED4 void mode1(void); //模式1函数声明 void blink_mode(DigitalOuts,int n); //闪烁模式函数声明 int main() { while(1) { mode1(); } } //模式1函数,每个LED亮1s,循环点亮 void mode1(void) { myled1 = 1; //wait(1); blink_mode(myled1,5); myled1 = !myled1; myled2 = 1; wait(1); myled2 = !myled2; myled3 = 1; wait(1); myled3 = !myled3; myled4 = 1; wait(1); myled4 = !myled4; } //LED快速闪烁n次 void blink_mode(DigitalOuts,int n) { for(int i=0;i { s = 0; wait_ms(30); s = !s; wait_ms(30); } } 数字输出就讲到这里,接下来看一下数字输入。 数字输入一般用于读取外部数字信息,比如按键、开关的状态。 下面写一个简单的程序,功能是当按键按下的时候,LED1状态改变,代码如下: #include "mbed.h" DigitalOut myled1(LED1); DigitalIn button(USER_BUTTON,PullDown); //这里用到了内部上拉电阻, int main() { myled1 = 1; while(1) { if(button == 0) //检测按键是否按下 myled1 = !myled1; } } 开发板上的按键接在PC_13引脚,在PinNames.h中有定义,电路图中,按键连接了外部上拉电阻,按下时会检测到低电平。如果用户需要外接按键,可以把Pin Mode改为PullUp(内部上拉)、PullDown(内部下拉)、PullNone(悬空)、OpenDrain(开漏)四种模式,这样在外接按键时就可以简化电路设计。 实际上当我们下载程序后发现,有时候按下一次,LED1的状态改变多次,有时没有改变,这主要是由于按键抖动造成的,我们可以考虑加上按键消抖,效果会好很多。下面是按键消抖的代码,在检测时加了10ms的延时。 int main() { myled1 = 1; while(1) { if(button == 0) { wait_ms(10); if(button == 0) myled1 = !myled1; } } } 上面的程序采用查询的方式,比较占用MCU资源,可以改成中断的形式,关于中断的部分我们后面再讲。 下节带来mbed模拟输入输出讲解。
|