姓名:楊晶晶 學號: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糕珊。
對采集到肌電信號進行預處理红选、提取特征,輸入到分類模型姆另,得到的正確率如下表所示:
4.STM32F407源碼
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);
}