TA的每日心情 | 奋斗 2015-1-1 23:27 |
---|
签到天数: 37 天 连续签到: 1 天 [LV.5]常住居民I
|
心电图液晶动态显示
首先这个题目是暑假前做的,本来想上传整个工程文件的,不知道在哪上传,下面主要main函数里面的代码,还有很多我以前一步步调试的过程,仔细看代码的话不难发现,很多重复注释的地方,都是我纠结了好久才调试成功的。余下的很多头文件我就不一一上传了,又是一中午都没睡觉,哎,为了我的Atmel SAM4S开发板,嘿嘿
首先,人体的心电波形是十分微弱的而且外界环境还有很多工频(50HZ)信号占主要的杂波(噪声),要想得到稳定、清晰的心电图波形,实验中最主要的硬件模块就是限波模块,限波目的主要就是可能将工频信号降到最低,限得越低越好,所以实验中为了达到理想中的效果,我一共用了2级限波电路级联,当然了,你要是追求完美三级限波也不为过。
强调一下,限波模块要是做好了,整个硬件电路也就基本完成了一半,所以在你限波模块单独测试时效果不理想,建议还是停下来检查一下再继续吧。
这个题目的整体方案采用的是多级滤波网络,大致电路框图如下:
电路设计完成有必要用仿真软件仿真一下,确保正确后在动工,因为涉及到很多运放,所以用仿真软件TINA是在合适不过了。
硬件调试通过后,现在就剩下写程序配置了.
下面是我程序里的主体函数,基本上每个函数都有注释,看懂应该很容易,我就说一下我写程序时的步骤吧。
其实也很简单,你要在液晶上显示,首先工作就是驱动液晶,实现刷屏、在液晶上打点画线。
在就是AD采样了,这个很重要,得好好看一下stm32的固件库函数和硬件引脚功能电路,记得我在这里调了好长时间,程序永远都是0error,0warning.可是就是出不来波形。如果搞清楚stm32的AD采样功能引脚和看懂相关API固件库,这个也应该是小case!这里特别要注意信道的选择。
最后就是键盘控制部分了,这个跟51差不多,一个键盘扫描,你要实现什么功能的话无非就是中断和在固件库里面调用含所需要的参数的函数。
恩,自己也是个新手,正在电子世界里摸打滚爬,算不上什么经验,就当是自己完成一个项目的总结吧,最后写的不怎么好,肯定有很多不足之处,还请各位网友哥们见谅!
是通过朋友才知道有这么好一个网站,有着这么一大群志同道合的电子发烧友,本人今年大三,以后争取每完成一个项目都来发表一下关于做项目经验总结,也希望能认识更多的电子爱好者!
#include <stm32f10x_lib.h>#include "lcd.h"#include "key.h"//#include "GB3232.h"// ×××××××××××画线×××××××××××××××void LCD_DrawPoint(u8 x,u8 y,u16 color) struct IO_port{ GPIO_TypeDef *GPIO_X; u16 GPIO_Pin;}; static struct IO_port Key_output[]={{GPIOA,GPIO_Pin_0},{GPIOA,GPIO_Pin_1},{GPIOA,GPIO_Pin_2},{GPIOA,GPIO_Pin_3}}; //结构体数组static struct IO_port Key_input[]={{GPIOC,GPIO_Pin_4},{GPIOC,GPIO_Pin_5},{GPIOC,GPIO_Pin_6},{GPIOC,GPIO_Pin_7}};u8 key[4][4] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2,u16 color){ u16 t; int xerr=0,yerr=0,delta_x,delta_y,distance; int incx,incy,uRow,uCol; delta_x=x2-x1; //计算坐标增量 delta_y=y2-y1; uRow=x1; uCol=y1; if(delta_x>0)incx=1; //设置单步方向 else if(delta_x==0)incx=0;//垂直线 else {incx=-1;delta_x=-delta_x;} if(delta_y>0)incy=1; else if(delta_y==0)incy=0;//水平线 else{incy=-1;delta_y=-delta_y;} if( delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴 else distance=delta_y; for(t=0;t<=distance+1;t++ )//画线输出 { lcd_point(uRow,uCol,color);//画点 xerr+=delta_x ; yerr+=delta_y ; if(xerr>distance) { xerr-=distance; uRow+=incx; } if(yerr>distance) { yerr-=distance; uCol+=incy; } } }//*********************RCC_Configuration**************************************void RCC_Configuration(){ ErrorStatus HSEStartUpStatus;//定义外部高速晶体启动状态枚举变量 RCC_DeInit();//复位RCC外部设备寄存器到默认值 RCC_HSEConfig(RCC_HSE_ON);//打开外部高速晶振 HSEStartUpStatus=RCC_WaitForHSEStartUp();//等待外部高速时钟准备好 if(HSEStartUpStatus==SUCCESS)//外部高速时钟已经准备好 { FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); FLASH_SetLatency(FLASH_Latency_2); RCC_HCLKConfig(RCC_SYSCLK_Div1);//配置AHB(HCLK)时钟=SYSCLK RCC_PCLK2Config(RCC_HCLK_Div1);//配置APB2(PCLK2)时钟=AHB时钟 RCC_PCLK1Config(RCC_HCLK_Div2);//配置APB1(PCLK1)时钟=AHB 1/2时钟 RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);//配置PLL时钟=外部高速晶体时钟*9 RCC_PLLCmd(ENABLE);//使能PLL时钟 while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET)//等待PLL时钟就绪 {} RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);//配置系统时钟=PLL时钟 while(RCC_GetSYSCLKSource()!=0x08)//检查PLL时钟是否作为系统时钟 {} } RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOG | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOF | RCC_APB2Periph_ADC1,ENABLE); }//***********d******************GPIO_Configuration*************************************************void GPIO_Configuration(void){ //矩形键盘配置 GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;//高8位-下拉输入 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //C0采样输入 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN ;//模拟输入 GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //低8位-推挽输出 GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_SetBits( GPIOA, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3); GPIO_SetBits(GPIOC, GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7); //流水灯配置 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOE,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin=GPIO_Pin_All; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOD,&GPIO_InitStructure); // 数据传输位 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOG, &GPIO_InitStructure);} /*配置DMA*/void ADC1_Configuration(void) { ADC_InitTypeDef ADC_InitStructure; ADC_DeInit(ADC1); //将外设 ADC1 的全部寄存器重设为缺省值 /* ADC1 configuration ------------------------------------------------------*/ ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式 ADC_InitStructure.ADC_ScanConvMode =ENABLE; //模数转换工作在扫描模式 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //模数转换工作在连续转换模式 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发转换关闭 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐 ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目 ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器 RCC_ADCCLKConfig(RCC_PCLK2_Div8); /* ADC1 regular channel11 configuration */ //设置指定ADC的规则组通道,设置它们的转化顺序和采样时间 //ADC1,ADC通道x,规则采样顺序值为y,采样时间为239.5周期 //ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5 ); //ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5 ); //ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_239Cycles5 ); //ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_239Cycles5 ); ADC_RegularChannelConfig(ADC1, ADC_Channel_10,1, ADC_SampleTime_55Cycles5 ); //ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 6, ADC_SampleTime_239Cycles5 ); //ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 7, ADC_SampleTime_239Cycles5 ); //ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 8, ADC_SampleTime_55Cycles5 ); //ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 9, ADC_SampleTime_239Cycles5 ); //ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 10, ADC_SampleTime_239Cycles5 ); //ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 11, ADC_SampleTime_239Cycles5 ); //ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 12, ADC_SampleTime_239Cycles5 ); // 开启ADC的DMA支持(要实现DMA功能,还需独立配置DMA通道等参数) ADC_DMACmd(ADC1, ENABLE); /* Enable ADC1 */ ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1 /* Enable ADC1 reset calibaration register */ ADC_ResetCalibration(ADC1); //复位指定的ADC1的校准寄存器 /* Check the end of ADC1 reset calibration register */ while(ADC_GetResetCalibrationStatus(ADC1)); //获取ADC1复位校准寄存器的状态,设置状态则等待 /* Start ADC1 calibaration */ ADC_StartCalibration(ADC1); //开始指定ADC1的校准状态 /* Check the end of ADC1 calibration */ while(ADC_GetCalibrationStatus(ADC1)); //获取指定ADC1的校准程序,设置状态则等待 ADC_SoftwareStartConvCmd(ADC1, ENABLE); } void DMA_Configuration(void){ /* ADC1 DMA1 Channel Config */ DMA_InitTypeDef DMA_InitStructure; DMA_DeInit(DMA1_Channel1); //将DMA的通道1寄存器重设为缺省值 DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR; //DMA外设ADC基地址 DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_GetConversionValue; //DMA内存基地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //内存作为数据传输的目的地 DMA_InitStructure.DMA_BufferSize = 1; //DMA通道的DMA缓存的大小 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度为16位 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为16位 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环缓存模式 DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA通道 x拥有高优先级 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输 DMA_Init(DMA1_Channel1, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道 DMA_Cmd(DMA1_Channel1, ENABLE); }//void TIME_Configuration(viod)//{// TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;// RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);// TIM_DeInit(TIM2);// TIM_TimeBaseInitStruct.TIM_Prescaler = 19;// TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;// TIM_TimeBaseInitStruct.TIM_Period = 399;// TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;// //TIM_TimeBaseInitStruct.TIM_RepetitionCounter = ;// TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);// TIM_ClearFlag(TIM2, TIM_FLAG_Update);// TIM_ITConfig(TIM2,TIM_FLAG_Update,ENABLE);// TIM_Cmd(TIM2,ENABLE);//}void delay_us(u16 t){ u16 i,j; for(i=0;i<t;i++) for(j=0;j<5;j++);}void delay_ms(u16 T){ u16 i,j; for(i=0;i<T;i++) for(j=0;j<6000;j++);}u8 KEY_Scaning(void){ u8 i,j; //定义k位局部静态变量 u8 key_value=0; GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3); for(i=0;i<4;i++) { GPIO_ResetBits(Key_output.GPIO_X,Key_output.GPIO_Pin); for(j=0;j<4;j++) { if(GPIO_ReadInputDataBit(Key_input[j].GPIO_X,Key_input[j].GPIO_Pin)==0) { delay_ms(10); if(GPIO_ReadInputDataBit(Key_input[j].GPIO_X,Key_input[j].GPIO_Pin)==0) { key_value=key[j]; } while(GPIO_ReadInputDataBit(Key_input[j].GPIO_X,Key_input[j].GPIO_Pin)==0); //等待按键释放 } } GPIO_SetBits(Key_output.GPIO_X,Key_output.GPIO_Pin); } return key_value; } //矩形键盘扫描int jianpan_scan(){ u16 temp,key; while(1) { //【1】 GPIO_Write(GPIOA, 0x000e); temp = GPIO_ReadInputData(GPIOC)+ 0x000e; temp = temp&0xf0; if(temp == 0x00f0) return -1; else { delay_ms(10);//延时5ms去抖动 if(temp == 0x00f0)//没有键按下 return -1; } //【2】 GPIO_Write(GPIOA, 0x000e); temp = GPIO_ReadInputData(GPIOC)+ 0x000e ;//读取一次按键输入 temp = temp&0xf0; switch(temp) { case 0x00e0: key=0; break; case 0x00d0: key=1; break; case 0x00b0: key=2; break; case 0x0070: key=3; break; } //【3】 GPIO_Write(GPIOA, 0x000d); temp = GPIO_ReadInputData(GPIOC)+ 0x000d ;//读取一次按键输入 temp = temp&0xf0; switch(temp) { case 0x00e0: key=4; break; case 0x00d0: key=5; break; case 0x00b0: key=6; break; case 0x0070: key=7; break; } //【4】 GPIO_Write(GPIOA, 0x000b); temp = GPIO_ReadInputData(GPIOC) + 0x000b ;//读取一次按键输入 temp = temp&0xf0; switch(temp) { case 0x00e0: key=8; break; case 0x00d0: key=9; break; case 0x00b0: key=10; break; case 0x0070: key=11; break; } //【5】 GPIO_Write(GPIOA, 0x0007); temp = GPIO_ReadInputData(GPIOC) + 0x0007;//读取一次按键输入 temp = temp&0xf0; switch(temp) { case 0x00e0: key=12; break; case 0x00d0: key=13; break; case 0x00b0: key=14; break; case 0x0070: key=15; break; } return key; } } static u16 V[400];u16* Change( u16 c )//移动并加入新数据{ u16 i;// static u16 a[400]; for( i = 399 ; i > 0 ; i--) { V = V[i-1]; } V[ 0 ]=c; return V;}int main(){ u16 Voltage[400]; u16 i=0,ADCVal=0; u16 New = 0 ; u16 *ADC_Val = &New , j , k , Tem = 0,stop_y ; j=0; RCC_Configuration(); // TIME_Configuration(); GPIO_Configuration(); DMA_Configuration(); ADC1_Configuration(); lcd_init();// color_test(); LCD_DrawLine(120,0,120,399,Black); LCD_DrawLine(0,200,239,200,Black); //心电图流动显示// while(1)// {// // delay_ms(20);// Tem = ADC_GetConversionValue(ADC1);// New = (u16)( Tem* 33 / 4096); //获得新数据 并放大10倍// j++; //记录采样个数//// if( ( j >= 2 ) && ( j <= 400) ) //2个到400个点之间只画 j 个点// { // k=j;// ADC_Val = Change( New ); // LCD_DrawLine( *(ADC_Val+k-1)+100 , k-1 , *(ADC_Val+k)+100 , k , Red );// // }//// else if( j > 400 ) //超过400个点每次都要画400个点// {// for( k=1 ; k<400 ; k++ )// {// ADC_Val = Change( New ); // LCD_DrawLine( *(ADC_Val+k-1)+100 , k-1 , *(ADC_Val+k)+100 , k , Red );// }// delay_ms(300);// lcd_clear(White);// }// else // 第一个点// {// // ADC_Val = Change( New ); // lcd_point( *ADC_Val+100,0 , Red );// }// // } //}// while(1)// { // // delay_ms(10); // ADCVal = ADC_GetConversionValue(ADC1);// Voltage = (u16)(ADCVal* 33*5/ 4096); //求的结果扩大了10倍,方便下面求出小数 // //可以用来显示频率 lcdshow_number(100,200,Voltage,Blue,Red); // lcd_point(ADCVal , 0 , Black ); // if(i>0)// {// lcd_point(Voltage+30 , i , Black );// LCD_DrawLine(Voltage[i-1]+ 80, i-1 ,Voltage+80 , i , Red);// }// i++;//// if(i==399) // {// lcd_clear(White);// i=0;// }// }// // } //键盘暂停 显示速率 动态显示 while(1) { k = jianpan_scan(); if( k==0 ) { static cishu ; cishu++;//按下次数 stop_y = i;//记录下断点的坐标 ADCVal = ADC_GetConversionValue(ADC1); Voltage = (u16)(ADCVal* 33*10/ 4096); i++; //存满余下采样点 while(jianpan_scan()!=0); if(i==399) { while(cishu%2==0 )//格点存够400点仍在暂停 则等待第二次按下 即【暂停】 { k = jianpan_scan(); if( k==0 ) cishu++; //继续检测按键 好跳出循环 if(stop_y>0 ) { // lcd_point(Voltage[stop_y]+30 , stop_y , Black ); LCD_DrawLine(Voltage[stop_y-1]+ 80, stop_y-1 ,Voltage[stop_y]+80 , stop_y, Red); } stop_y++; } } } else //暂停键没有按下 { delay_ms(10); ADCVal = ADC_GetConversionValue(ADC1); Voltage = (u16)(ADCVal* 33*10/ 4096); //求的结果扩大了10倍,方便下面求出小数 if(i>0) {// lcd_point(Voltage+30 , i , Black ); LCD_DrawLine(Voltage[i-1]+ 80, i-1 ,Voltage+80 , i , Red); } i++; if(i==399) { lcd_clear(White); i=0; } } } } |
|