前言

从3月8号收到板子,到今天算起来,uFUN到手也有两周的时间了,最近利用下班后的时间,做了个心率计,从单片机程序到上位机开发,到现在为止完成的差不多了,实现很简单,uFUN开发板外加一个PulseSensor传感器就行,又开发了配套的串口上位机,实现数据的解析和显示,运行界面如下:

其实PulseSensor官方已经配备的了Processing语言编写的上位机软件,串口协议的,界面还蛮好看,只要按照它的通信协议,就可以实现心跳波形和心率的显示。刚好最近学习了Qt,所以就用这个小软件来练手了。本篇文章是这个小项目的第一篇,介绍一下如何使用DMA方式获取传感器的数据,至于后面几篇文章会写什么,欢迎大家保持关注哈!

传感器介绍

PulseSensor 是一款用于脉搏心率测量的光电反射式模拟传感器。将其佩戴于手指、耳垂等处,利用人体组织在血管搏动时造成透光率不同来进行脉搏测量。传感器对光电信号进行滤波、放大,最终输出模拟电压值。单片机通过将采集到的模拟信号值转换为数字信号,再通过简单计算就可以得到心率数值。

信号输出引脚连接到示波器,看一下是什么样的信号:

可以看出信号随着心跳起伏变化,周期大概为:1.37/2 = 0.685s。计算出心率值为:600 / 0.685 = 87,我的心率在正常范围内(废话!),这个传感器测心率还是可以的。手头上没有传感器的朋友,可以看一下这篇自制心率传感器的教程:手指检测心跳设计——传感器制作篇,这篇文章介绍的使用一个红外发射管和一个红外接收管,外加放大滤波电路,效果还是挺不错的。

AD采集电路的分析

大家在使用ADC接口的时候要注意了,线别插错了。我第一次使用就是测不到电压值,后来用万用表量了一下,才发现是入门指南中引脚功能标示错了,要采集AD电压,输入脚应该接DCIN这个,对应的是PC3-ADC_IN13。如下图。可能是由于原理图版本的迭代,入门指南没有来得及更新吧!手动@管理员 更改一下。

从原理图中可以看出,直流电压采集电路前级采用双T陷波滤波器滤除50Hz工频干扰,后级为运放电路:

关于前级的双T陷波滤波器S域分析,可以参考这篇文章:双T陷波器s域计算分析(纯手算,工程版!)

大学期间学得信号与系统都忘了,所以这部分计算我没有看懂。其实了解电路的S域分析,更有利于理解电路的特性,大家还是要掌握好理论基础。

后面的运放电路,还是大概能看懂的,下面来分析一下直流通路,把电容看作断路:

所有的运放电路分析,就记住两个要点就行了:虚短和虚断。(感觉又回到了大学。。。。)

虚短:理解成短路,运放处于线性状态时,把两输入端视为等电位,即运放正输入端和负输入端的电压相等,即U+ = U-。

虚断:理解成断路,运放处于线性状态时,把两输入端视为开路,即流入正负输入端的电流为零。

总结一句话:虚短即U+=U-;虚断即净输入电流为0。

好了,有了这两把利器,我们来看一下这部分电路的分析,直流通路可进一步简化为:

很明显,可计算出

U+ = 0.5 * VCC = 1.65v

应用虚短:

U- = U+ = 1.65v

应用虚断,即没有电流流入运放,根据串联电流相等:

以上三式联立,可得:

Uo = 3.368 - 1.205*Ui

即:

Ui  = 3 - 0.83 * Uo

只要得到单片机采集到的电压值Uo,就可以反推出实际的传感器电压值Ui。

通过使用示波器测量Ui和Uo的波形,近似可以认为是反向的,但是明显可以看出,Uo的峰值比Ui的峰值小一点。

而且通过绘制Ui = 3 - 0.83 * UoUi = 3.3 - Uo的曲线,也可以看出,两条直线几乎重合,即输入和输出近似为反向。

DMA简介

DMA,即直接存储器,用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须

CPU任何干预,通过DMA数据可以快速地移动。这就节省了CPU的资源来做其他操作。STM32共有两个DMA控制器有12个通道(DMA1有7个通道,DMA2有5个通道),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。

关于DMA通道和外设的对应,可以查看STM32参考手册,心率传感器使用的PC3-ADC_IN13,对应的是DMA1的通道1

STM32 DMA程序配置

获取ADC通道的电压值主要有两种方式,一种是直接使用ADC,然后在需要使用的地方,先启动AD转换,然后读取AD值。另一种更好的方式是使用DMA方式,就是先定义一个保存AD值的全局变量,而全局变量是对应内存中的一个地址的。只要初始时,把DMA和ADC配置好了,DMA会自动把获取到的AD值,存入这个地址中,我们在需要的时候,直接读取这个值就可以了。

0.定义一个全局变量

必须是全局变量,用于存放AD值。

uint16_t ADC_ConvertedValue;

1.配置GPIO和使能时钟

使能外设对应的时钟,注意时钟总线的不同:

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOC, ENABLE);

引脚配置成模拟输入模式:

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //设置为模拟输入
GPIO_Init(GPIOC, &GPIO_InitStructure);

2.配置DMA

配置ADC对应的DAM1通道1:

DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(ADC1->DR)); //设置源地址
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_ConvertedValue; //设置内存地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // 设置传输方向
DMA_InitStructure.DMA_BufferSize = 1;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //循环模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High; //高优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure); DMA_Cmd(DMA1_Channel1, ENABLE); //使能DMA1通道1

3.配置ADC

由于只有1个通道,不需要配置成扫描模式:

ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE ;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);

PC3对应ADC输入通道13,注意采样周期不能太短:

ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 1, ADC_SampleTime_55Cycles5);
ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
ADC_SoftwareStartConvCmd(ADC1, ENABLE);

4.主程序调用

DMA和ADC配置好之后,只需要初始化一次。然后就可以随时获取电压值了。

int main(void)
{
float Sensor_Voltage;
float Uo_Voltage;
delay_init();
UART1_Config(115200);
ADC1_Init();
while(1)
{
Uo_Voltage = ADC_ConvertedValue * 3.3 / 4096;
Sensor_Voltage = 3.3 - Uo_Voltage; //近似值
// Sensor_Voltage = 3 - 0.83 * Uo_Voltage; //实际传感器输出电压值
ANO_SendFloat(0xA1, Sensor_Voltage);
delay_ms(10);
}
}

为了方便查看数据的波形,这里直接使用了匿名上位机来显示电压值的波形。

函数实现

//匿名上位机,波形显示一个浮点型数据ANO_SendFloat(0xA1, ad);
void ANO_SendFloat(int channel, float f_dat)
{
u8 tbuf[8];
int i;
unsigned char* p; for(i = 0; i <= 7; i++)
tbuf[i] = 0; p = (unsigned char*)&f_dat;
tbuf[0] = 0x88;
tbuf[1] = channel; //0xA1
tbuf[2] = 4;
tbuf[3] = (unsigned char)(*(p + 3)); //取float类型数据存储在内存中的四个字节
tbuf[4] = (unsigned char)(*(p + 2));
tbuf[5] = (unsigned char)(*(p + 1));
tbuf[6] = (unsigned char)(*(p + 0)); for(i = 0; i <= 6; i++)
tbuf[7] += tbuf[i]; //校验和
printf("%s", tbuf);
}

实际的显示

没有调试器,如何下载程序呢?可以参考我之前发的一篇帖子:【uFUN开发板评测】如何使用串口来给uFUN开发板下载程序,详细介绍了如何通过串口来给uFUN开发板下载程序。

匿名上位机的帧格式配置

实际的显示效果:

总结

传感器数据的获取,只是心率计实现的第一步,传感器放置位置的不同,波形的振幅也会不同,所以,对获得数据的处理、分析,才是最关键的部分。

资料下载

参考资料

uFUN评测系列文章


欢迎大家关注我的个人博客

或微信扫码关注我的公众号

基于uFUN开发板的心率计(一)DMA方式获取传感器数据的更多相关文章

  1. 基于uFUN开发板的心率计(三)Qt上位机的实现

    前言 上两周利用周末的时间,分别写了基于uFUN开发板的心率计(一)DMA方式获取传感器数据和基于uFUN开发板的心率计(二)动态阈值算法获取心率值,介绍了AD采集传感器数据和数据的滤波处理获取心率值 ...

  2. 基于uFUN开发板的心率计(二)动态阈值算法获取心率值

    前言 上一篇文章:基于uFUN开发板的心率计(一)DMA方式获取传感器数据,介绍了如何获取PulseSensor心率传感器的电压值,并对硬件电路进行了计算分析.心率计,重要的是要获取到心率值,本篇文章 ...

  3. 基于uFUN开发板和扩展板的联网校准时钟

    项目概述 上周在uFUN试用群里看到管理员说试用活动快结束了,要抓紧完成评测总结,看大家的评测总结也都写了,我也不能落后啊!正好最近做的扩展板到手了,于是赶紧进行调试,做了一个不用校准的时钟,时钟这种 ...

  4. 基于uFUN开发板的RGB调色板

    前言 使用uFUN开发板配合Qt上位机,实现任意颜色的混合,Qt上位机下发RGB数值,范围0-255,uFUN开发板进行解析,然后输出不同占空比的PWM,从而实现通过RGB三原色调制出任意颜色. Qt ...

  5. 千呼万唤始出来——uFUN开发板2.0开箱评测

    前言 今年3月,我参与了面包板社区组织的第一批uFUN开发板评测活动,并有幸能获得试用机会,那是我第一次了解到uFUN这个项目及背后的故事,4月份,uFUN 2.0版本来了,收到了张工送的一块样板,后 ...

  6. 【UFUN开发板评测】小巧而不失精致,简单而不失内涵——uFun开发板开箱爆照

    关于uFun学习板--"满满的爱和正能量" uFun是由@张进东 张工组织发起的一个开源的学习板,设计初衷是为了帮助学生更好的理解电子知识和开发技巧,同时又能对学生毕业找工作有很明 ...

  7. 基于 Arduino 开发板,这款插座是可编程且开源的

    基于 Arduino 开发板,这款插座是可编程且开源的 https://www.oschina.net/news/74861/open-source-socket https://github.com ...

  8. 【RTOS】基于V7开发板的uCOS-III,uCOS-II,RTX4,RTX5,FreeRTOS原版和带CMSIS-RTOS V2封装层版全部集齐

    RTOS模板制作好后,后面堆各种中间件就方便了. 1.基于V7开发板的最新版uCOS-II V2.92.16程序模板,含MDK和IAR,支持uC/Probe https://www.cnblogs.c ...

  9. J2EE Web开发入门—通过action是以传统方式返回JSON数据

    关键字:maven.m2eclipse.JSON.Struts2.Log4j2.tomcat.jdk7.Config Browser Plugin Created by Bob 20131031 l ...

随机推荐

  1. python同步原语--线程锁

    多线程锁是python多种同步原语中的其中一种.首先解析一下什么是同步原语,python因为GIL(全局解析锁)的缘故,并没有真正的多线性.另外python的多线程存在一个问题,在多线程编程时,会出现 ...

  2. matplotlib多plot可视化

    代码: # -*- coding: utf-8 -*- """ Created on Thu Jul 12 16:37:47 2018 @author: zhen &qu ...

  3. Java新建Web应用与配置Tomcat流程

    1. 新版本IDEA新建Web应用后没有web.xml文件,需要通过下图方式添加 2. IDEA中的web文件夹相当于eclipse里的WebRoot文件夹,在里面新建WEB-INF文件夹,再创建cl ...

  4. 自己搭建anki同步服务器

    最近帮孩子找学习的软件,发现了anki 不过同步速度太慢,但发现可以自己搭建同步服务器 具体方法见https://github.com/dsnopek/anki-sync-server 我的安装过程如 ...

  5. .net core 导出Excel(epplus 创建excel )

    [Route("getopenfrequencyexcel")] [HttpGet] public IActionResult GetOpenFrequencyExcel(int ...

  6. Django电商项目---完成购物车页面day4

    创建购物车项目App python manage.py startapp df_cart 初始化项目: manas/urls.py manas/settings.py 创建新文件:df_cart/ur ...

  7. python使用关键字爬取url

    python网路爬虫 --------- 使用百度输入的关键字搜索内容然后爬取搜索内容的url 开发环境:windows7+python3.6.3 开发语言:Python 开发工具:pycharm 第 ...

  8. January 29th, 2018 Week 05th Monday

    Losing all hope was freedom. 彻底绝望就是真正的自由. Losing all the hopes, and we are free to challenge everyth ...

  9. 使用JavaConfig和注解方式实现零xml配置的Spring MVC项目

    1. 引言 Spring MVC是Spring框架重要组成部分,是一款非常优秀的Web框架.Spring MVC以DispatcherServlet为核心,通过可配置化的方式去处理各种web请求. 在 ...

  10. golang的reflection(转)

    作者:BGbiao 链接:https://www.jianshu.com/p/42c19f88df6c 來源:简书 反射reflection 可以大大提高程序的灵活性,使得interface{}有更大 ...