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采集肌电信号相关推荐

  1. stm32采集交流电压信号_基于STM32的交流电压检测.pdf

    第24卷第13期 电子设计工程 2016年7月 V01.24 No.13 Electronic Jul.2016 DesignEngineering 基于STM32的交流电压检测 任宏斌,冷建伟 (天 ...

  2. sEMG项目总结(5)sEMG分析

    sEMG分析 目录 sEMG分析 目录 1肌电信号 2肌电信号采集 3预处理滤波去噪 4特征提取与选择 5分类 1肌电信号 肌电信号的产生 表面肌电信号是由多个运动单元发放的动作电位序列,在皮肤表面呈 ...

  3. 表面肌电信号sEMG之常用特征

    1.均方根(RMS) 该特征是指在一定时间窗口内,sEMG信号的幅度均值的平方根,通常用于评估肌肉收缩的强度和疲劳程度.通常情况下,肌肉受到较大的负荷或疲劳时,RMS会降低,而在肌肉处于较轻负荷或较为 ...

  4. 【电路设计】肌电信号采集电路分析

    最近在开发肌电信号的采集,表面肌电信号是非常微弱的生物信号,正常人体表面肌电信号赋值为0–1.5mV,主要能量频段集中在10–150Hz.电路主要是根据原始信号,设计相应的放大电路.滤波电路, 详细可 ...

  5. 读论文-----基于单通道表面肌电信号的手势识别 Hand Gestures Recognition Based on One-Channel Surface EMG Signal

    Hand Gestures Recognition Based on One-Channel Surface EMG Signal 摘要 本文提出了一个利用OpenBCI采集两种手势信号的数据并解码信 ...

  6. matlab肌电信号平滑滤波_BCIduino 滤波和频谱计算操作

    本文介绍用 python 对航弈生物 BCIduino 放大器脑电/肌电数据进行滤波及频谱计算,介绍如何用 python mne 对 fif 格式保存的脑电数据进行读取和简单的滤波,并用numpy 对 ...

  7. stm32采集脉冲信号_基于STM32+FPGA的数据采集系统的设计与实现

    引言 由于火控系统工作环境特殊,所需采集信号复杂多样,传统的以微控制器或PC为主的采集系统往往难以胜任.针对上述问题,提出了一种基于STM32+FPGA的数据采集系统的设计方案,该方案不仅能够完成对多 ...

  8. 一种基于肌电信号运动起点、波峰、终点实时自动检测的方法

    一种基于肌电信号运动起点.波峰.终点实时自动检测的方法 (⊙o⊙)-,这篇是我写收费文章的第一篇.咱也尝试下知识付费,哈哈. 先看下效果,在给定理想正弦波的情况下,可以准确识别到正弦波的起点.波峰和终 ...

  9. 【Matlab肌电信号】肌电信号处理【含GUI源码 966期】

    一.代码运行视频(哔哩哔哩) [Matlab肌电信号]肌电信号处理[含GUI源码 966期] 二.matlab版本及参考文献 1 matlab版本 2014a 2 参考文献 [1] 包子阳,余继周,杨 ...

最新文章

  1. 【学习笔记】超简单的多项式反三角函数(含全套证明)
  2. EOS账户系统(4)账户权限分级
  3. sort函数——利用函数实现快速排序c++
  4. 一文整理IEEE问题汇总【IEEE PDF Checker】(更新中)
  5. 浅谈电商网站开发中用户会话管理机制的设计和实现原理
  6. PHP vs Node.js vs Nginx-Lua(转)
  7. ZigBee开发(15)--组网实验点播
  8. eclipse中outline中图标含义
  9. [cdq分治][树状数组] Bzoj P3262 陌上花开
  10. 计算机考试数据库相关知识点,计算机等级考试四级数据库工程师知识点总结
  11. Bias and Variance with Mismatched Distributions
  12. STVD环境下开发STM8,如何查看工程占用的Flash、EEPROM、RAM的情况
  13. [【转】TOMATO DDWRT中几种无线模式的使用
  14. IntelliJ IDEA 2018.2激活方法及常用快捷键
  15. SLURM Array Job
  16. eclipse快捷键使用以及一些小机灵
  17. Filezilla 连接不上 Error: Connection timed out after 20 seconds of inactivity
  18. linux编译动态库未定义,GCC链接库的一个坑:动态库存在却提示未定义动态库的函数...
  19. administrator无法创建新用户的解决
  20. 马尔可夫模型(MC, HMM, POMDP, MOMDP)

热门文章

  1. 设半径为R的球面S的球心在定球面x^2+y^2+z^2=a^2(a>0)上,问R取何值时,球面s在定球面内的面积最大?
  2. 频率(脉冲)信号转直流电压电流信号变换器隔离转换模块0-5KHz/0-10KHz/1-5KHz转0-5V/0-10V/1-5V/0-10mA/0-20mA/4-20mA
  3. freehand8_在Illustrator和Freehand中创建仿制3D图形
  4. Vim技能修炼教程(10) - 代码跳转
  5. 在线视频流播放方法利弊;ffmpeg mp4 faststart;mp4 moov作用
  6. 金蝶专业版怎么反过账当月_金蝶kis专业版的反过账是怎么操作的?
  7. 7.2 BPMN Scope
  8. 2016 Google hosts 持续更新【更新于:2016-04-10】
  9. 滑块识别,滑块验证码识别平台,验证码识别
  10. 三、R语言可视化--ggplot2和REmap包绘制地图