项目描述
项目介绍
很高兴参加由硬禾学堂和digikey联合发起的Funpack活动,本活动的初衷是让更多电子技术爱好者接触到来自世界各个厂商的开发板,完成指定任务就免费赠送开发板。
这是Funpack第三季第二期活动,本期活动开发板PIC-IoT WA是一款来自MicroChip的高性能无线开发套件,其支持多种无线通讯协议。
任务描述
- 伸出不同数量的手指在板卡上挥动,识别出有几根手指
硬件介绍
开发板整体概况
PIC-IoT WA开发板基于PIC24FJ128GA705(128 KB 闪存和 16 KB SRAM)的16位XLP微控制器。板载光照和温度传感器,用于加密的ATECC608安全元件,WINC1500 Wi-Fi模组,多功能调试器。另外,开发板引出多个IO口并配备mikroBUS™连接器,可扩展丰富外设。
光照传感器
TEMT6000传感器可以非常灵敏地感知光强的变化,可将光照强度转换为模拟输出信号,与MCU的ADC channel 8(RB12)相连。
设计思路
主要困难
此小节主要分析功能需求,实现该项目会面临的困难。
从任务描述中可以看出,实现该任务有两个核心功能:
- 判断是否有手指挥过开发板,识别手指数量n。
- OLED上展示手指数量信息,并且串口打印。
解决思路
此小节针对面临的困难,提出解决方案(拟使用的传感器及通信协议)。
核心功能一
数据获取:配置ADC,读取光照传感器的ADC采样值。
算法设计:根据随时间变化的光强采样数据,设计算法完成手指数量辨识。
PIC24FJ128GA705的ADC的分辨率可达12位。首先,初始化AD控制寄存器1(AD1CON1)的值为AD1CON1 = 0x8000,使得ADC采样和模数转换由软件触发。然后在主程序不停地进行ADC采样。
手指数量辨识算法的算法设计参考解题思路。
设计状态机,根据ADC采样值的变化趋势特征判识移过开发板上方的手指数量。
处于空闲状态时,当ADC差值小于阈值(负数)时,开始检测手指数,初始化n为1,进入负斜率状态。
处于负斜率状态时,当ADC差值大于阈值(正数)时,转换到正斜率状态,不需要改变n。
处于正斜率状态时,当ADC差值小于阈值(负数)时,进入负斜率状态,增加变化次数(n+1);否则不变化,当零斜率超过一段时间后,恢复为空闲状态,并显示结果。
核心功能二
OLED驱动:使得OLED正常工作。
采用的OLED屏幕是0.91寸的,分辨率是128*32,驱动芯片为SSD1306,使用IIC驱动。根据SSD芯片手册,编写模拟IIC驱动代码,将手指数量信息显示到屏幕上。
IO分配及功能配置
在MCC中,引脚功能配置情况如下图所示。
引脚功能配置
IO分配
软件流程图
此小节主要描述解决方案的软件实现,提出实现项目功能的技术路线。
流程图如图所示。
功能展示
核心代码片段及说明
ADC采样
//...
ADC1_Enable();
ADC1_ChannelSelect(LIGHT_SENSOR);
ADC1_SoftwareTriggerEnable();
//Provide Delay
for(int i=0;i <1000;i++) {}
ADC1_SoftwareTriggerDisable();
while(!ADC1_IsConversionComplete(LIGHT_SENSOR));
conversion = ADC1_ConversionResultGet(LIGHT_SENSOR);
ADC1_Disable();
//...
判识手指数量状态机
typedef enum {
IDLE, // 空闲状态
NEGATIVE, // 负斜率状态
POSITIVE // 正斜率状态
} State;
State currentState = IDLE;
int n = 0; // 变化次数
int z = 0; // 零斜率时长
char n_string[30];
void process_slope(int16_t slope)
{
switch (currentState)
{
case IDLE:
if (slope < -SLOPE_THRESHOLD){
currentState = NEGATIVE;
n = 1; // 开始检测手指数,初始化为1
}
// 对于正斜率和零斜率在空闲状态下不做处理
break;
case NEGATIVE:
if (slope > SLOPE_THRESHOLD){
currentState = POSITIVE;
// 转换到正状态,不需要改变n
}
break; // 对于负斜率和零斜率在负状态下不做处理
case POSITIVE:
if (slope < -SLOPE_THRESHOLD){
currentState = NEGATIVE;
n += 1; // 增加变化次数
}
else if (slope < SLOPE_THRESHOLD){
z += 1;
if (z > 30) { // 如果零斜率时长超过30,结束一次检测
sprintf(n_string, "Detected fingers: %d\r\n", n);
UART1_SendString(n_string);
reset_detection();
}
}
break; // 对于正斜率在正状态下不做处理
}
}
void reset_detection() {
currentState = IDLE;
n = 0;
z = 0;
}
模拟iic驱动OLED,写字节时序
void IIC_WriteByte(uint8_t txd) {
uint8_t i;
IIC_SCL_0;
for(i = 0; i < 8; i++){
(txd & 0x80) ? IIC_SDA_1 : IIC_SDA_0;
txd <<= 1;
__delay_us(4);
IIC_SCL_1;
__delay_us(4);
IIC_SCL_0;
__delay_us(4);
}
// wait ack
IIC_SDA_1; IIC_SCL_1; __delay_us(4); IIC_SCL_0; __delay_us(4);
}
// Send a byte to the command register
void ssd1306_WriteCommand(uint8_t byte) {
IIC_Start();
IIC_WriteByte(SSD1306_SLAVE_ADDR | SSD1306_IIC_WRITE); //Slave address,SA0=0
IIC_WriteByte(0x00); //write command
IIC_WriteByte(byte);
IIC_Stop();
}
// Send data
void ssd1306_WriteData(uint8_t* buffer, size_t buff_size) {
for (unsigned int i = 0; i < buff_size; i++) {
IIC_Start();
IIC_WriteByte(SSD1306_SLAVE_ADDR | SSD1306_IIC_WRITE);
IIC_WriteByte(0x40); //write data
IIC_WriteByte(buffer[i]);
IIC_Stop();
}
}
占用存储空间情况
实现效果
OLED显示识别到的手指数量,当没有手指划过时,板载红色LED变亮,当有物体划过时,板载红色LED变暗。具体演示效果请观看演示视频。
注意:请在光线充足处完成测试。光线较暗处难以保障识别精度。
心得体会
本项目依托PIC-lot WA开发板,完成手指数量识别及显示功能。在此过程中阅读传感器数据手册和平台驱动代码,学习PIC24单片机开发和状态机编写方法,最终顺利完成任务。感谢Funpack活动的举办方提供了一个探索性的平台,感谢微信群中分享经验、答疑解惑的工作人员和工程师们。