STM32采集肌电信号
目录
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);
delay_init(168);
uart_init(115200);
LED_Init();
ADCInit();
while(1)
{
if(dateFlag==1)
{
sprintf(buff1,"%.6f,",ch1);
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];
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);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
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);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3,ENABLE);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC3,DISABLE);
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;
ADC_CommonInit(&ADC_CommonInitStructure);
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 2;
ADC_Init(ADC3, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC3, ADC_Channel_4, 1, ADC_SampleTime_15Cycles);
ADC_RegularChannelConfig(ADC3, ADC_Channel_5, 2, ADC_SampleTime_15Cycles);
ADC_DMARequestAfterLastTransferCmd(ADC3, ENABLE);
ADC_DMACmd(ADC3, ENABLE);
ADC_Cmd(ADC3, ENABLE);
}
/*
* 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_InitStructure.DMA_Channel = DMA_Channel_2;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC3->DR);
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)(u16 *)AD_Value;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = N*2;
DMA_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_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);
DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TC);
DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE);
DMA_Cmd(DMA2_Stream0, ENABLE);
}
/*
* 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);
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
TIM_Cmd(TIM2, ENABLE);
}
/*
* Function : void ADCInit_Nvic(void)
* Description : 中断初始化
*/
static void ADCInit_Nvic(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority =1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
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_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
ch1=Get_Adc1(AD_Value[0]);
ch2=Get_Adc1(AD_Value[1]);
dateFlag=1;
if(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);
}