sEMG项目总结(3)STM32采集肌电信号
STM32采集肌电信号
目录
- STM32采集肌电信号
- 目录
- 1采集方式ADCTimerDMA
- 2采集程序的配置
- 3对采集的sEMG的分析
- 4STM32F407源码
1采集方式ADC+Timer+DMA
(1)肌电信号采集板有双通道,信号的放大倍数可调,采样频率可调
(2)使用STM32的ADC多通道+Timer触发+DMA传输模式采集肌电信号
(3)通过串口将数据实时发送给上位机。
2采集程序的配置
肌电信号采集的ADC通道配置子程序如下:
(1)初始化ADC通道的引脚复用功能
(2)设置传输数据的DMA方式
(3)设置ADC通道的采样频率,触发模式,扫描模式等
(4)设置定时器和定时器中断
void ADCInit(void)
{ ADCInit_GPIO();ADCInit_DMA();ADCInit_ADC();ADCInit_Nvic();ADCInit_Timer();
}
3对采集的sEMG的分析
张手、握拳、放松时的肌电信号
肌电信号的采样频率是500HZ,对原始信号进行频率变换后可以看到50HZ的工频噪声干扰较大,采用50HZ的数字陷波器滤除工频噪声干扰。采集到的肌电信号最主要的能量集中在20-200HZ。
对采集到肌电信号进行预处理、提取特征,输入到分类模型,得到的正确率如下表所示:
4STM32F407源码
这里只用到main.c、 adc.c、 adc.h
main.c
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "pwm.h"
#include "adc.h"int main(void)
{ char buff1[5],buff2[5];NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置系统中断优先级分组2delay_init(168); //初始化延时函数uart_init(115200); //初始化串口波特率为115200LED_Init(); ADCInit(); while(1){ if(dateFlag==1) //判断数据是否已经更新完成{ sprintf(buff1,"%.6f,",ch1); //sprintf()打印到字符串中,printf打印到命令行输出printf("%s",buff1);sprintf(buff2,"%.6f,",ch2); printf("%s",buff2);dateFlag=0; } }
}
adc.h
#ifndef __ADC_H
#define __ADC_H
#include "sys.h"
#include "usart.h"#define N 5 //每通道采5次
extern double ch1,ch2; //用来存放采集结果
extern u8 dateFlag; //数据转换完成标志static void ADCInit_GPIO(void);
static void ADCInit_ADC(void);
static void ADCInit_DMA(void);
static void ADCInit_NVIC(void);void ADCInit_Timer(void);
void ADCInit(void);double Get_Adc1(vu16 advalue);#endif
adc.c
#include "adc.h"
#include "delay.h" /* 数据定义 */
vu16 AD_Value[N]; //用来存放ADC转换结果,也是DMA的目标地址
double ch1,ch2; //用来存放采集的结果
u8 dateFlag=0;
u8 UpdataTIM = 0; /** Function : static void ADCInit_GPIO(void)* Description : ADC GPIO初始化*/
static void ADCInit_GPIO(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); //使能GPIOA时钟//ADC通道初始化GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //PA0,PA1 ADC通道GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; //模拟输入GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //不带上下拉GPIO_Init(GPIOF, &GPIO_InitStructure); //初始化
}/** Function : static void ADCInit_ADC(void)* Description : ADC模式初始化*/
static void ADCInit_ADC(void)
{ADC_CommonInitTypeDef ADC_CommonInitStructure;ADC_InitTypeDef ADC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE); //使能ADC3时钟RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3,ENABLE); //ADC3复位RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3,DISABLE); //复位结束 ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; //独立模式ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; //两个采样阶段之间的延迟5个时钟ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; //DMA失能(对于多个ADC通道)ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; //预分频4分频ADC_CommonInit(&ADC_CommonInitStructure); //初始化ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //12位模式ADC_InitStructure.ADC_ScanConvMode = ENABLE; //扫描模式(多通道ADC采集需要用扫描模式) ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //关闭连续扫描ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising; //上升沿触发ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO; //定时器事件2触发ADCADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfConversion = 2; //2个转换在规则序列中 ADC_Init(ADC3, &ADC_InitStructure); //ADC初始化//连续模式下通道的配置ADC_RegularChannelConfig(ADC3, ADC_Channel_4, 1, ADC_SampleTime_15Cycles); //PA0,VIN1,通道0,rank=1,表示连续转换中第一个转换的通道ADC_RegularChannelConfig(ADC3, ADC_Channel_5, 2, ADC_SampleTime_15Cycles); //PA1,VIN2,通道1ADC_DMARequestAfterLastTransferCmd(ADC3, ENABLE); //连续使能DMAADC_DMACmd(ADC3, ENABLE); //使能ADC_DMAADC_Cmd(ADC3, ENABLE); //开启AD转换器
}/** Function : static void ADCInit_DMA(void)* Description : ADC使能DMA模式*/
static void ADCInit_DMA(void)
{DMA_InitTypeDef DMA_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); //时钟使能//DMA设置DMA_InitStructure.DMA_Channel = DMA_Channel_2; //选择通道号DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC3->DR); //外围设备地址,ADC_DR_DATA规则数据寄存器DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)(u16 *)AD_Value; //DMA存储器地址,自己设置的缓存地址DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; //传输方向:外设到存储器DMA_InitStructure.DMA_BufferSize = N*2; //DMA缓存大小,数据传输量N*2DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //DMA模式DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(DMA2_Stream0, &DMA_InitStructure); //初始化DMA2_Stream0,对应为ADC3//设置DMA中断DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TC); //清除中断标志DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE); //传输完成中断 DMA_Cmd(DMA2_Stream0, ENABLE); //使能DMA
}/** Function : void ADCInit_Timer(void)* Description : ADC触发定时器的设置*/
void ADCInit_Timer(void)
{TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //时钟使能 TIM_Cmd(TIM2, DISABLE); //失能时钟TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); //初始化定时器TIM_TimeBaseStructure.TIM_Prescaler = 168-1; TIM_TimeBaseStructure.TIM_Period = 200-1; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up ; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //使能定时器中断 TIM_ARRPreloadConfig(TIM2, ENABLE); //允许TIM2定时重载TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); TIM_Cmd(TIM2, ENABLE); //使能TIM2
}/** Function : void ADCInit_Nvic(void)* Description : 中断初始化*/
static void ADCInit_Nvic(void)
{NVIC_InitTypeDef NVIC_InitStructure;//定时器中断设置NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //定时器TIM2中断通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; NVIC_InitStructure.NVIC_IRQChannelSubPriority =1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化NVIC寄存器//DMA中断设置NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn; //DMA2_Stream0中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0; NVIC_InitStructure.NVIC_IRQChannelSubPriority =1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);
}/** Function : void ADCInit(void)* Description : ADC初始化函数*/
void ADCInit(void)
{ADCInit_GPIO();ADCInit_DMA();ADCInit_ADC();ADCInit_Nvic();ADCInit_Timer();
} /** Function : void TIM2_IRQHandler(void) * Description : TIM2??????*/
void TIM2_IRQHandler(void)
{if(TIM_GetITStatus(TIM2, TIM_IT_Update)) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); }
}/** Function : void DMA2_Stream0_IRQHandler(void) * Description : DMA2_Stream0中断*/
void DMA2_Stream0_IRQHandler(void)
{u16 period = 0;if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0)) //判断DMA传输完成中断 {DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);ch1=Get_Adc1(AD_Value[0]);ch2=Get_Adc1(AD_Value[1]);dateFlag=1;//判断是否更新TIM2if(UpdataTIM){period = 200-1; TIM_ARRPreloadConfig(TIM2, DISABLE); TIM2->ARR = period ; TIM_ARRPreloadConfig(TIM2, ENABLE); }}
}double Get_Adc1(u16 adValue)
{ return (double)(adValue * 3.3 / 4096);
}
sEMG项目总结(3)STM32采集肌电信号相关推荐
- stm32采集交流电压信号_基于STM32的交流电压检测.pdf
第24卷第13期 电子设计工程 2016年7月 V01.24 No.13 Electronic Jul.2016 DesignEngineering 基于STM32的交流电压检测 任宏斌,冷建伟 (天 ...
- sEMG项目总结(5)sEMG分析
sEMG分析 目录 sEMG分析 目录 1肌电信号 2肌电信号采集 3预处理滤波去噪 4特征提取与选择 5分类 1肌电信号 肌电信号的产生 表面肌电信号是由多个运动单元发放的动作电位序列,在皮肤表面呈 ...
- 表面肌电信号sEMG之常用特征
1.均方根(RMS) 该特征是指在一定时间窗口内,sEMG信号的幅度均值的平方根,通常用于评估肌肉收缩的强度和疲劳程度.通常情况下,肌肉受到较大的负荷或疲劳时,RMS会降低,而在肌肉处于较轻负荷或较为 ...
- 【电路设计】肌电信号采集电路分析
最近在开发肌电信号的采集,表面肌电信号是非常微弱的生物信号,正常人体表面肌电信号赋值为0–1.5mV,主要能量频段集中在10–150Hz.电路主要是根据原始信号,设计相应的放大电路.滤波电路, 详细可 ...
- 读论文-----基于单通道表面肌电信号的手势识别 Hand Gestures Recognition Based on One-Channel Surface EMG Signal
Hand Gestures Recognition Based on One-Channel Surface EMG Signal 摘要 本文提出了一个利用OpenBCI采集两种手势信号的数据并解码信 ...
- matlab肌电信号平滑滤波_BCIduino 滤波和频谱计算操作
本文介绍用 python 对航弈生物 BCIduino 放大器脑电/肌电数据进行滤波及频谱计算,介绍如何用 python mne 对 fif 格式保存的脑电数据进行读取和简单的滤波,并用numpy 对 ...
- stm32采集脉冲信号_基于STM32+FPGA的数据采集系统的设计与实现
引言 由于火控系统工作环境特殊,所需采集信号复杂多样,传统的以微控制器或PC为主的采集系统往往难以胜任.针对上述问题,提出了一种基于STM32+FPGA的数据采集系统的设计方案,该方案不仅能够完成对多 ...
- 一种基于肌电信号运动起点、波峰、终点实时自动检测的方法
一种基于肌电信号运动起点.波峰.终点实时自动检测的方法 (⊙o⊙)-,这篇是我写收费文章的第一篇.咱也尝试下知识付费,哈哈. 先看下效果,在给定理想正弦波的情况下,可以准确识别到正弦波的起点.波峰和终 ...
- 【Matlab肌电信号】肌电信号处理【含GUI源码 966期】
一.代码运行视频(哔哩哔哩) [Matlab肌电信号]肌电信号处理[含GUI源码 966期] 二.matlab版本及参考文献 1 matlab版本 2014a 2 参考文献 [1] 包子阳,余继周,杨 ...
最新文章
- 【学习笔记】超简单的多项式反三角函数(含全套证明)
- EOS账户系统(4)账户权限分级
- sort函数——利用函数实现快速排序c++
- 一文整理IEEE问题汇总【IEEE PDF Checker】(更新中)
- 浅谈电商网站开发中用户会话管理机制的设计和实现原理
- PHP vs Node.js vs Nginx-Lua(转)
- ZigBee开发(15)--组网实验点播
- eclipse中outline中图标含义
- [cdq分治][树状数组] Bzoj P3262 陌上花开
- 计算机考试数据库相关知识点,计算机等级考试四级数据库工程师知识点总结
- Bias and Variance with Mismatched Distributions
- STVD环境下开发STM8,如何查看工程占用的Flash、EEPROM、RAM的情况
- [【转】TOMATO DDWRT中几种无线模式的使用
- IntelliJ IDEA 2018.2激活方法及常用快捷键
- SLURM Array Job
- eclipse快捷键使用以及一些小机灵
- Filezilla 连接不上 Error: Connection timed out after 20 seconds of inactivity
- linux编译动态库未定义,GCC链接库的一个坑:动态库存在却提示未定义动态库的函数...
- administrator无法创建新用户的解决
- 马尔可夫模型(MC, HMM, POMDP, MOMDP)
热门文章
- 设半径为R的球面S的球心在定球面x^2+y^2+z^2=a^2(a>0)上,问R取何值时,球面s在定球面内的面积最大?
- 频率(脉冲)信号转直流电压电流信号变换器隔离转换模块0-5KHz/0-10KHz/1-5KHz转0-5V/0-10V/1-5V/0-10mA/0-20mA/4-20mA
- freehand8_在Illustrator和Freehand中创建仿制3D图形
- Vim技能修炼教程(10) - 代码跳转
- 在线视频流播放方法利弊;ffmpeg mp4 faststart;mp4 moov作用
- 金蝶专业版怎么反过账当月_金蝶kis专业版的反过账是怎么操作的?
- 7.2 BPMN Scope
- 2016 Google hosts 持续更新【更新于:2016-04-10】
- 滑块识别,滑块验证码识别平台,验证码识别
- 三、R语言可视化--ggplot2和REmap包绘制地图