sEMG项目总结(3)STM32采集肌电信号

时间:2021-07-18 04:32:02

STM32采集肌电信号

目录

1采集方式ADC+Timer+DMA

(1)肌电信号采集板有双通道,信号的放大倍数可调,采样频率可调
(2)使用STM32的ADC多通道+Timer触发+DMA传输模式采集肌电信号
(3)通过串口将数据实时发送给上位机。
sEMG项目总结(3)STM32采集肌电信号

2采集程序的配置

肌电信号采集的ADC通道配置子程序如下:
(1)初始化ADC通道的引脚复用功能
(2)设置传输数据的DMA方式
(3)设置ADC通道的采样频率,触发模式,扫描模式等
(4)设置定时器和定时器中断

void ADCInit(void)
{ 
    ADCInit_GPIO();
    ADCInit_DMA();
    ADCInit_ADC();
    ADCInit_Nvic();
    ADCInit_Timer();  
}

3对采集的sEMG的分析

张手、握拳、放松时的肌电信号
sEMG项目总结(3)STM32采集肌电信号

肌电信号的采样频率是500HZ,对原始信号进行频率变换后可以看到50HZ的工频噪声干扰较大,采用50HZ的数字陷波器滤除工频噪声干扰。采集到的肌电信号最主要的能量集中在20-200HZ。
sEMG项目总结(3)STM32采集肌电信号

对采集到肌电信号进行预处理、提取特征,输入到分类模型,得到的正确率如下表所示:
sEMG项目总结(3)STM32采集肌电信号

4STM32F407源码

sEMG项目总结(3)STM32采集肌电信号
这里只用到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);   //设置系统中断优先级分组2
        delay_init(168);                                  //初始化延时函数
        uart_init(115200);                                //初始化串口波特率为115200

        LED_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触发ADC
    ADC_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,通道1

    ADC_DMARequestAfterLastTransferCmd(ADC3, ENABLE);  //连续使能DMA
    ADC_DMACmd(ADC3, ENABLE);                          //使能ADC_DMA
    ADC_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*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模式
    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;

         //判断是否更新TIM2
         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); 
}