美文网首页嵌牛IT观察
STM32识别肌电信号

STM32识别肌电信号

作者: 归去来兮_c94f | 来源:发表于2022-01-20 00:04 被阅读0次

    姓名:杨晶晶 学号:21011210420 学院:通信工程学院

    转载自:https://blog.csdn.net/malele4th/article/details/79361030

    【嵌牛导读】

          运动性肌疲劳是体育界和运动医学界十分关注的课题。目前,许多研究致力于寻找*定和预防肌肉疲劳产生的方法,大多数研究是从全身的生理、生化状况来推断肌肉的功能状况,直接进行局部肌肉的研究还很少。表面肌电信号(sEMG信号)是从皮肤表面通过电极引导、放大、显示和记录下来的神经肌肉系统活动时的生物电信号,信号形态具有较大的随机性和不稳定性。它与肌肉的活动状态和功能状态之间存在着不同程度的关联性,因而能在一定的程度上反映神经肌肉的活动,在康复医学领域的肌肉功能*价以及在体育科学中的疲劳判定、运动技术合理性分析等方面均有重要的实用价值。表面肌电信号采集属无创性,操作简单,病人易接受,有着广泛的应用前景。

    【嵌牛鼻子】明确肌电信号的采集需要的单片机功能,编写代码,对代码进行分析。

    【嵌牛提问】如何用A/D、Timer、DMA采集肌电信号?如何利用STM32比编写程序?

    【嵌牛正文】


    STM32采集肌电信号

    目录

                1.采集方式ADCTimerDMA

                2.采集程序的配置

                3.对采集的sEMG的分析

                4.STM32F407源码


    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。

    原始肌电信号、频率变换后的信号、50Hz陷波后的信号

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

    三种动作识别率

    4.STM32F407源码

    用到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);

    }

    相关文章

      网友评论

        本文标题:STM32识别肌电信号

        本文链接:https://www.haomeiwen.com/subject/fxvfhrtx.html