前言

从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;  //0xA1tbuf[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开发板下载程序。

匿名上位机的帧格式配置

实际的显示效果:

总结

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

资料下载

  • STM32工程下载:STM32工程
  • STM32参考手册下载:STM32参考手册
  • 匿名上位机下载: 匿名上位机

参考资料

  • PulseSensor官网
  • 手指检测心跳设计——传感器制作篇
  • 玩的就是心跳 —— 使用 PulseSensor 脉搏传感器测量心率
  • 双T陷波器s域计算分析(纯手算,工程版!)

uFUN评测系列文章

  • 【UFUN开发板评测】小巧而不失精致,简单而不失内涵——uFun开发板开箱爆照
  • 如何使用串口来给STM32下载程序
  • STM32串口打印输出乱码的解决办法
  • Keil报错:cannot open source input file "core_cmInstr.h" 解决办法

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

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

gazebo 直接获取传感器数据_基于uFUN开发板的心率计(一)DMA方式获取传感器数据...相关推荐

  1. request[limit]取不到前台的值_基于uFUN开发板的心率计(二)动态阈值算法获取心率值...

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

  2. angularjs 获取复选框的值_基于uFUN开发板的心率计(一)DMA方式获取传感器数据

    前言 从3月8号收到板子,到今天算起来,uFUN到手也有两周的时间了,最近利用下班后的时间,做了个心率计,从单片机程序到上位机开发,到现在为止完成的差不多了,实现很简单,uFUN开发板外加一个Puls ...

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

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

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

    文章目录 前言 IBI和BPM 核心操作 -- 识别一个脉搏信号 问题一:阈值的选取 问题二:特征点识别 算法整体框架与代码实现 总结 基于uFUN开发板的Keil源码下载 uFUN评测系列文章 前言 ...

  5. 基于uFUN开发板的心率计(一)DMA方式获取传感器数据

    前言 从3月8号收到板子,到今天算起来,uFUN到手也有两周的时间了,最近利用下班后的时间,做了个心率计,从单片机程序到上位机开发,到现在为止完成的差不多了,实现很简单,uFUN开发板外加一个Puls ...

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

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

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

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

  8. STM32F103C8T6核心开发板下,采用串口DMA方式向上位机连续发送数据的实例详解

    STM32采用串口DMA方式,用115200bps或更高速率向上位机连续发送数据 文章目录 STM32采用串口DMA方式,用115200bps或更高速率向上位机连续发送数据 前言 一.DMA应用初步 ...

  9. arduino彩灯计时器电路_基于Arduino开发板的倒计时器

    Arduino-based-Countdown-Timer.jpg (54.03 KB, 下载次数: 86) 2018-4-18 17:18 上传 计时器是一种时钟形式,通常用于测量时间间隔.有两种类 ...

最新文章

  1. sql语句按照汉字拼音首字母排序
  2. 8.Java有关变量的面试题
  3. javascript访问ACCESS数据库
  4. 编写自己的Matcher与如何让编译器识别被mock的重载函数
  5. mysql异步非阻塞方式_如何理解swoole异步非阻塞?
  6. TF-卷积函数 tf.nn.conv2d 介绍
  7. liferay 指定默认首页
  8. 企业的最佳选择?开放式混合云大行其道
  9. 7 centos lvs nat配置_centos中lvs安装配置方法详解
  10. 《解决微信内置浏览器返回上一页强制刷新问题方法》
  11. vue项目实现详情页后退缓存之前的数据
  12. IntelliJ IDEA下SVN的配置及使用说明
  13. sourcesafe的七个问题?
  14. 人脸检测FDDB评测的详细步骤
  15. 论文分享 CFUN: Combining Faster R-CNN and U-net Network for Efficient Whole Heart Segmentation
  16. 工程计算和数据科学中常用的9种测量距离
  17. html-2-禁止手机页面放大缩小
  18. 【论文阅读】流量预测
  19. HBuilderX 终端显示空白问题
  20. Shell脚本——业务上线前如何去扫描指定网段的所有IP地址呢?

热门文章

  1. zabbix监控第一台服务器
  2. 实时股票数据获取方式
  3. 2022-02-15
  4. 《安富莱嵌入式周报》第308期:开源带软硬件安全认证的PLC设计,开源功率计,可靠PID实现,PR2机器人设计文件全开源,智能手表设计WASP-OS
  5. 百万点赞怎么来?用Python制作抖音视频原来这么简单!
  6. 远程教育(电大)网考统考计算机应,4月电大远程网络教育计算机应用基础统考题库真题3...
  7. 《梦里花落知多少》的经典语句(怀念)
  8. Java内存溢出与栈溢出
  9. 快速搭建个人博客——保姆级教程
  10. Handler运行机制