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

2019-04-13 14:31发布

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); //设置系统中断优先级分组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); }