STM32識別肌電信號

姓名:楊晶晶 學號:21011210420 學院:通信工程學院

轉(zhuǎn)載自:https://blog.csdn.net/malele4th/article/details/79361030

【嵌牛導讀】

? ? ? 運動性肌疲勞是體育界和運動醫(yī)學界十分關(guān)注的課題。目前较屿,許多研究致力于尋找*定和預防肌肉疲勞產(chǎn)生的方法隧魄,大多數(shù)研究是從全身的生理、生化狀況來推斷肌肉的功能狀況隘蝎,直接進行局部肌肉的研究還很少堤器。表面肌電信號(sEMG信號)是從皮膚表面通過電極引導、放大末贾、顯示和記錄下來的神經(jīng)肌肉系統(tǒng)活動時的生物電信號,信號形態(tài)具有較大的隨機性和不穩(wěn)定性整吆。它與肌肉的活動狀態(tài)和功能狀態(tài)之間存在著不同程度的關(guān)聯(lián)性拱撵,因而能在一定的程度上反映神經(jīng)肌肉的活動,在康復醫(yī)學領(lǐng)域的肌肉功能*價以及在體育科學中的疲勞判定表蝙、運動技術(shù)合理性分析等方面均有重要的實用價值拴测。表面肌電信號采集屬無創(chuàng)性,操作簡單府蛇,病人易接受集索,有著廣泛的應用前景。

【嵌牛鼻子】明確肌電信號的采集需要的單片機功能,編寫代碼务荆,對代碼進行分析妆距。

【嵌牛提問】如何用A/D、Timer函匕、DMA采集肌電信號娱据?如何利用STM32比編寫程序?

【嵌牛正文】


STM32采集肌電信號

目錄

? ? ? ? ? ? 1.采集方式ADCTimerDMA

? ? ? ? ? ? 2.采集程序的配置

? ? ? ? ? ? 3.對采集的sEMG的分析

? ? ? ? ? ? 4.STM32F407源碼


1采集方式ADC+Timer+DMA

(1)肌電信號采集板有雙通道盅惜,信號的放大倍數(shù)可調(diào)中剩,采樣頻率可調(diào)

(2)使用STM32的ADC多通道+Timer觸發(fā)+DMA傳輸模式采集肌電信號

(3)通過串口將數(shù)據(jù)實時發(fā)送給上位機。

2采集程序的配置

肌電信號采集的ADC通道配置子程序如下:

(1)初始化ADC通道的引腳復用功能

(2)設置傳輸數(shù)據(jù)的DMA方式

(3)設置ADC通道的采樣頻率抒寂,觸發(fā)模式结啼,掃描模式等

(4)設置定時器和定時器中斷

void ADCInit(void)

{

? ? ADCInit_GPIO();

? ? ADCInit_DMA();

? ? ADCInit_ADC();

? ? ADCInit_Nvic();

? ? ADCInit_Timer();

}

3.對采集的sEMG的分析

張手、握拳屈芜、放松時的肌電信號

肌電信號的采樣頻率是500HZ郊愧,對原始信號進行頻率變換后可以看到50HZ的工頻噪聲干擾較大,采用50HZ的數(shù)字陷波器濾除工頻噪聲干擾沸伏。采集到的肌電信號最主要的能量集中在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);? //設置系統(tǒng)中斷優(yōu)先級分組2

? ? ? ? delay_init(168);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //初始化延時函數(shù)

? ? ? ? uart_init(115200);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //初始化串口波特率為115200

? ? ? ? LED_Init();

? ? ? ? ADCInit();?

? ? ? ? while(1)

? ? ? ? {?

? ? ? ? ? ? ? ? if(dateFlag==1)? //判斷數(shù)據(jù)是否已經(jīng)更新完成

? ? ? ? ? ? ? ? {? ? ? ? ?

? ? ? ? ? ? ? ? ? ? ? ? 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;? ? //用來存放采集結(jié)果

extern u8? ? dateFlag;? ? //數(shù)據(jù)轉(zhuǎn)換完成標志

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"? ?

/* 數(shù)據(jù)定義 */

vu16? AD_Value[N];? //用來存放ADC轉(zhuǎn)換結(jié)果蝶防,也是DMA的目標地址

double ch1,ch2;? ? //用來存放采集的結(jié)果

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);? ? //復位結(jié)束?

? ? 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;? ? //關(guān)閉連續(xù)掃描

? ? ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;? //上升沿觸發(fā)

? ? ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;? ? ? ? //定時器事件2觸發(fā)ADC

? ? ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;?

? ? ADC_InitStructure.ADC_NbrOfConversion = 2;? ? ? ? ? ? ? //2個轉(zhuǎn)換在規(guī)則序列中

? ? ADC_Init(ADC3, &ADC_InitStructure);? ? ? ? ? ? ? ? ? ? //ADC初始化

? ? //連續(xù)模式下通道的配置

? ? ADC_RegularChannelConfig(ADC3, ADC_Channel_4, 1, ADC_SampleTime_15Cycles);? //PA0,VIN1,通道0,rank=1,表示連續(xù)轉(zhuǎn)換中第一個轉(zhuǎn)換的通道

? ? ADC_RegularChannelConfig(ADC3, ADC_Channel_5, 2, ADC_SampleTime_15Cycles);? //PA1,VIN2,通道1

? ? ADC_DMARequestAfterLastTransferCmd(ADC3, ENABLE);? //連續(xù)使能DMA

? ? ADC_DMACmd(ADC3, ENABLE);? ? ? ? ? ? ? ? ? ? ? ? ? //使能ADC_DMA

? ? ADC_Cmd(ADC3, ENABLE);? ? ? ? ? ? ? ? ? ? ? ? ? ? //開啟AD轉(zhuǎn)換器?

}

/*

* 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規(guī)則數(shù)據(jù)寄存器

? ? DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)(u16 *)AD_Value;? ? ? ? //DMA存儲器地址明吩,自己設置的緩存地址

? ? ? DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;? ? ? ? ? ? ? ? ? ? //傳輸方向:外設到存儲器

? ? DMA_InitStructure.DMA_BufferSize = N*2;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //DMA緩存大小间学,數(shù)據(jù)傳輸量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觸發(fā)定時器的設置

*/

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);? ? ? ? ? ? ? ? ? ? ? ? ? ? //根據(jù)指定的參數(shù)初始化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初始化函數(shù)

*/? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

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

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市印荔,隨后出現(xiàn)的幾起案子低葫,更是在濱河造成了極大的恐慌,老刑警劉巖仍律,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘿悬,死亡現(xiàn)場離奇詭異,居然都是意外死亡水泉,警方通過查閱死者的電腦和手機善涨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門窒盐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人钢拧,你說我怎么就攤上這事蟹漓。” “怎么了娶靡?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵牧牢,是天一觀的道長。 經(jīng)常有香客問我姿锭,道長塔鳍,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任呻此,我火速辦了婚禮轮纫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘焚鲜。我一直安慰自己掌唾,他們只是感情好,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般芳绩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上撩扒,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機與錄音吨些,去河邊找鬼搓谆。 笑死,一個胖子當著我的面吹牛豪墅,可吹牛的內(nèi)容都是我干的泉手。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼偶器,長吁一口氣:“原來是場噩夢啊……” “哼斩萌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起屏轰,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤颊郎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后亭枷,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡搀崭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年叨粘,在試婚紗的時候發(fā)現(xiàn)自己被綠了猾编。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡升敲,死狀恐怖答倡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情驴党,我是刑警寧澤瘪撇,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站港庄,受9級特大地震影響倔既,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鹏氧,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一渤涌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧把还,春花似錦实蓬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至艇炎,卻和暖如春酌伊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背冕臭。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工腺晾, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人辜贵。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓悯蝉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親托慨。 傳聞我的和親對象是個殘疾皇子鼻由,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容