【管脚定义】
上一期介绍了简单的数字输入输出,我们知道了mbed API对底层的高度封装。程序开发者想要知道可以使用哪些管脚,例如上一期的Blinky例子中Pin名称LED1的使用,可以查看mbed.org对这款开发板的Nucleo features的介绍,也可以在mbed library目录下targets/hal/对应开发板下面的PinNames.h的头文件下查看:
typedef enum {
... ... ...
// Arduino connector namings
... ... ...
// 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 = PB_3,
// Not connected
NC = (int)0xFFFFFFFF
} PinName;
可以看到LED1,LED2,LED3,LED4其实都是同一个管脚PA_5,所以使用任何一个名称均是对PA_5进行操作。
【二】Analog Input and Output
由于手头没有电位器(可变电阻器)调节输入电压,没有示波器观察输出波形,这部分就只有理论学习。
1. ADC: Analog ---> Digital
即:Voltage ---> Binary Output
对于输入电压范围0~3.3V, 12-bit的ADC,其精度为3.3/(2^12), 约0.8mV。那么最坏情况下的量化误差为0.4mV。采样频率:Nyquist
AnalogIn API:
AnalogIn(PinName pin);
//构造函数,指定作为模拟输入的pin口
float read();
//读取输入电压,返回0.0~1.0之间的float值,其中,0代表0V,1代表3.3V
unsigned short read_u16();
//返回unsigned short in the range [0x0, 0xFFFF],范围在0到65535之间
例:
AnalogIn analog_value(A0);
float meas;
meas = analog_value;//等同于meas=analog_value.read();
2. DAC: Digital--->Analog
AnalogOut API:
AnalogOut(PinName pin);
void write(float value);
//写入 [0.0, 1.0] 范围之间的值,按照百分比设置输出电压,0.25对应输出电压值为0.25×3.3V
void write_u16(unsigned short value);//unsigned short in the range [0x0, 0xFFFF]
【三】Pulse width modulation(PWM)脉宽调制
1. 设置PWM周期period,决定PWM波形的频率。
2. 设置PWM波形的占空比duty-cycle,从而调平均电压。
duty_cycle=100% * (pulse on time) / (pulse period)
PWM API:
PwmOut(PinName pin);
//把管脚pin设成PwmOut输出管脚
void write(float value);
//设置输出 duty-cycle,范围在0到1之间,0表示在一个时钟周期内全部都是低电平,1表示全部都是高电平
float read();
void period(float seconds);
//设置PWM period,即时钟周期,单位秒
void period_ms(int ms);
void period_us(int us);
void pulsewidth(float seconds);
//设定PWM输出的脉宽,相当于设定PWM的输出值,假设时钟周期period是10ms,脉宽是5ms,那就相当于输出占空比(duty-cycle)为0.5,这里的单位是秒
void pulsewidth_ms(int ms);
void pulsewidth_us(int us);
请看下面的例子:
#include "mbed.h"
PwmOut PWM1(LED1); //创建PWM1对LED1端口调制
int main() {
PWM1.period(0.010);
// PWM period = 10 ms 即输出频率为100Hz pulse;
PWM1=0.5;
// duty cycle= 50%,相当于平均电压为3.3×0.5V(或者是理论上的LED1=0.5);
}
【四】GPIO Interrupt
学过微机原理的同学都知道,可以通过查询和中断方式传送数据和处理事件。而查询方式传送数据占用CPU大量时间,所以中断方式判断引脚状态处理事件效率较高。GPIO中断是微处理器中断系统中最简单最常用的中断类型,它可以让用户在某个管脚状态发生特定的变化(边沿触发)时执行相应的代码。需要注意的是,并不是所有的GPIO管脚都具备中断处理能力。
GPIO Interrupt API:
InterruptIn(PinName pin);
//把pin管脚设成中断处理管脚
int read();
//读取管脚的当前状态
void rise(void (*fptr)(void));
//上升沿触发 InterruptFunction
void fall(void (*fptr)(void));
//下降沿触发InterruptFunction
void mode(PinMode pull);
//设置管脚模式,上升沿设置为 Pulldown, 下降沿 PullUp
__disable_irq();//禁止所有可屏蔽中断;
__enable_irq();//允许所有未屏蔽中断;
void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority):
//设置中断的优先级,IRQn表示需要设置的中断号,后面表示优先级,数字越小表示优先级越高。
下面是实例:
#include "mbed.h"
Serial pc(USBTX,USBRX);
InterruptIn btn(USER_BUTTON);
Timer mytimer;
int falltime;
int risetime;
void fallfunc()
{
falltime=mytimer.read_us();
}
void risefunc()
{
risetime=mytimer.read_us();
pc.printf("You press button for %d us \n",risetime-falltime);
}
int main() {
mytimer.start();
btn.fall(&fallfunc);
//按键上升沿读取当前时间
btn.rise(&risefunc);
//下降沿再次读取当前时间并输出时间间隔
while (1);
}
Tip: 使用GPIO中断的时候要确认pin端口默认接上拉电阻还是下拉电阻,如果是上拉电阻,那么默认就是高电平,此时则选择下降沿触发。反之则使用上升沿触发。