- void ADC_Config(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- ADC_InitTypeDef ADC_InitStructure;
- TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
- TIM_OCInitTypeDef TIM_OCInitStructure;
- DMA_InitTypeDef DMA_InitStructure;
-
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1 , ENABLE );
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
- RCC_ADCCLKConfig(RCC_PCLK2_Div6);
- RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2 , ENABLE);
-
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//Ä£ÄâÊäÈë
- GPIO_Init(GPIOA, &GPIO_InitStructure);
- /* DMA channel1 configuration */
- DMA_DeInit(DMA1_Channel1);
- DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //ADCµØÖ·
- DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&AD_BUF;//ÄÚ´æµØÖ·
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
- DMA_InitStructure.DMA_BufferSize = 500; //»º´æµ¥ÔªµÄ¸öÊý
- 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_InitStructure.DMA_Priority = DMA_Priority_High;
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
- DMA_Init(DMA1_Channel1, &DMA_InitStructure);
- DMA_ITConfig(DMA1_Channel1,DMA_IT_TC,ENABLE);//Æô¶¯ÖжϱêÖ¾
- DMA_Cmd(DMA1_Channel1, ENABLE);
- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
- ADC_InitStructure.ADC_ScanConvMode = DISABLE;
- ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2;
- ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
- ADC_InitStructure.ADC_NbrOfChannel = 1;
- ADC_Init(ADC1, &ADC_InitStructure);
-
- ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_1Cycles5);
-
- ADC_DMACmd(ADC1, ENABLE); //ʹÄÜDMA´«Êä
- ADC_Cmd(ADC1, ENABLE);
-
- ADC_ResetCalibration(ADC1);
- while(ADC_GetResetCalibrationStatus(ADC1));
- ADC_StartCalibration(ADC1);
- while(ADC_GetCalibrationStatus(ADC1));
-
- ADC_SoftwareStartConvCmd(ADC1, ENABLE);
- TIM_TimeBaseInitStruct.TIM_Period = 400; //ÖØÔØÊ±µÄÖµ ´¥·¢Ê±¼ä2000us£¬ÆµÂÊ200Hz
- TIM_TimeBaseInitStruct.TIM_Prescaler = 480 - 1; //·ÖƵϵÊý
- TIM_TimeBaseInitStruct.TIM_ClockDivision = 0; //ʱÖÓ·Ö¸î
- TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; //ÏòÉϼÆÊý
- TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
- TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct); //µ÷Óÿ⺯ÊýдÈë¼Ä´æÆ÷
-
- TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //Ñ¡Ôñ¶¨Ê±Æ÷ģʽΪÂö³å¿í¶Èµ÷ÖÆÄ£Ê½1
- TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
- TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
- TIM_OCInitStructure.TIM_Pulse = 200;
- TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
-
- TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low;
- TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
- TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;
- TIM_OC2Init(TIM2, &TIM_OCInitStructure);
- TIM_Cmd(TIM2, ENABLE);
- TIM_CtrlPWMOutputs(TIM2, ENABLE); //¿ØÖÆTIM2 PWMÊä³ö
- }
程序用DMA自动转换ADC,并且用半中断传输技术,避免传输数据时,数据被DMA改写。关键在这两个量DMA1_FLAG_TC1,DMA1_FLAG_HT1,即DMA缓存到一半时,触发中断,把这一半数据处理并对发送。另一半缓存自动转换,不影响前面的一半。
if(DMA_GetFlagStatus(DMA1_FLAG_TC1)==SET) //»ñÈ¡±ê־룬ÅжÏÊÇ·ñ´«ÊäÍê³É
{
GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);
IIR_filter_H(); //Â˲¨Æ÷º¯Êý
USB_USART_SendData('$');//ÒÔ×Ö½Ú·½Ê½,·¢Ë͸øUSB
for(i=0;i<250;i++)
{
Send_int_Data=(int)(Send_float_Data[i+250]*1000); //floatת»»Îªint
//Send_int_Data=0x0650;
int_to_char(Send_int_Data,Send_char_data); //intתcharÐÍÊý×é
for(j=0;j<4;j++)
{
USB_USART_SendData(Send_char_data[j]);
}
}
USB_USART_SendData('*');//ÒÔ×Ö½Ú·½Ê½,·¢Ë͸øUSB
USB_USART_RX_STA=0;
GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET);
DMA_ClearFlag(DMA1_FLAG_TC1); //´¦ÀíÍêÊý¾Ý,Çå³þ±ê־λ
}
if(DMA_GetFlagStatus(DMA1_FLAG_HT1)==SET) //»ñÈ¡±ê־룬ÅжÏÊÇ·ñ´«ÊäÍê³É
{
GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);
IIR_filter_L(); //Â˲¨Æ÷º¯Êý
USB_USART_SendData('$');//ÒÔ×Ö½Ú·½Ê½,·¢Ë͸øUSB
for(i=0;i<250;i++)
{
Send_int_Data=(int)(Send_float_Data*1000); //floatת»»Îªint
//Send_int_Data=0x0650;
int_to_char(Send_int_Data,Send_char_data); //intתcharÐÍÊý×é
for(j=0;j<4;j++)
{
USB_USART_SendData(Send_char_data[j]);
}
}
USB_USART_SendData('*');//ÒÔ×Ö½Ú·½Ê½,·¢Ë͸øUSB
USB_USART_RX_STA=0;
GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET);
DMA_ClearFlag(DMA1_FLAG_HT1); //´¦ÀíÍêÊý¾Ý,Çå³þ±ê־λ
}
3、单片机信号处理
数字信号处理发展很快,其中数字滤波器解决了模拟滤波器滤波效果差的缺陷,从前面图中可以看出,上位机显示的心电波形比示波器的波形要清晰很多,原因就是在单片机中加入了数字滤波器,滤除50Hz工频信号。程序里用的是IIR 50Hz陷波器,IIR滤波器是单片机数字信号处理中用的比较多的,对于IIR滤波器,冲激响应理论上应会无限持续,其输出不仅取决于当前和过去的输入信号值,也取决于过去的信号输出值。常用的工频陷波器主要有IIR和FIR两种,其中FIR具有良好的线性相位,但是在同等滤波效果的情况下,IIR的阶数要比FIR少很多,一个两阶的IIR滤波器的效果FIR要付出100多阶的代价,阶数大意味着运算量大,对于一个MCU单片机来说这是得不偿失的,所以采用IIR滤波器来实现工频滤波。滤波器设计不得不提到MATLAB,他是很好的软件,能很方便的制作数字滤波器。只需填入几个参数可以实现
单片机程序如下
- ADC_ConvertedValueLocal =(float) (AD_BUF[i+250]>>2)/4096*3.3; // ¶Áȡת»»µÄADÖµ
- x0=ADC_ConvertedValueLocal; //ÊäÈëÐźÅ
- w0[0]=IIR_50Notch_A[0]*x0-IIR_50Notch_A[1]*w0[1]-IIR_50Notch_A[2]*w0[2];
- y0=IIR_50Notch_B[0]*w0[0]+IIR_50Notch_B[1]*w0[1]+IIR_50Notch_B[2]*w0[2];
- Send_float_Data[i+250]=y0;
- w0[2]=w0[1];
- w0[1]=w0[0];
- w1[2]=w1[1];
- w1[1]=w1[0];
4、C#解析单片机上传的数据
C#解析单片机上传的数据用列表的方法解决数据包连包的问题。C#接收一包数据SerialPort事件有事要产生一次或者多次事件,导到一包数据分成两包数据。这里用List型变量,每次通知有数据到来,就把数据加入list,处理完移除list。
- private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
- {
- int num = serialPort1.BytesToRead;
- byte[] received_buf = new byte[num];
- byte[] nreceived_buf = new byte[1002];
- int[] show_buf = new int[1002];
- float show_data = 1;
- int i;
- serialPort1.Read(received_buf, 0, num);
- m_buffer.AddRange(received_buf);
-
- if (m_buffer.Count != 0)
- {
- int HeadIndex = m_buffer.FindIndex(o => o == '
- 5、C#波形绘制
- C#波形绘制用到双缓冲技术
- [code]public Form1()
- {
- this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint |
- ControlStyles.AllPaintingInWmPaint,
- true);//开启双缓冲
- this.UpdateStyles();
- InitializeComponent();
- System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;
- TablePen.DashStyle = System.Drawing.Drawing2D.DashStyle.DashDotDot;
- SearchAndAddSerialToComboBox(serialPort1, comboBox1);
- }
);
if (HeadIndex == -1)
{
m_buffer.Clear();
}
else if (HeadIndex != 0) //不为开头移掉之前的字节
{
if (HeadIndex > 1)
m_buffer.RemoveRange(0, HeadIndex);
}
if ((HeadIndex == 0) &&(m_buffer.Count > 1002))
{
m_buffer.CopyTo(0, nreceived_buf, 0, 1002);
m_buffer.RemoveRange(0, 1002);
uart_count = uart_count + 1000;
for (i = 0; i < 1000; i++)
{
show_data = (((long)(((nreceived_buf[i+1]-0x30)*1000)+ (nreceived_buf[i + 2] - 0x30) * 100) + ((nreceived_buf[i + 3] - 0x30) * 10) + (nreceived_buf[i +4] - 0x30))/1);
show_buf[0] = (int)show_data-300;
DataList.Add(show_buf[0]);//链表尾部添加数据
i++; i++; i++;
}
Invalidate(); //刷新显示
sb.Clear();
try
{
//因为要访问UI资源,所以需要使用invoke方式同步ui
this.Invoke((EventHandler)(delegate
{
textBox1.Clear();
textBox1.AppendText(uart_count.ToString("F2"));
listBox1.Items.Add(DateTime.Now.ToString() +" " +uart_count.ToString() + " " + System.Text.Encoding.Default.GetString(nreceived_buf));
listBox1.SelectedIndex = listBox1.Items.Count - 1;
listBox1.SelectedIndex = -1;
}
)
);
}
catch (Exception ex)
{
//响铃并显示异常给用户
System.Media.SystemSounds.Beep.Play();
MessageBox.Show(ex.Message);
}
}
}
}[/code]
5、C#波形绘制
C#波形绘制用到双缓冲技术
- public Form1()
- {
- this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint |
- ControlStyles.AllPaintingInWmPaint,
- true);//开启双缓冲
- this.UpdateStyles();
- InitializeComponent();
- System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;
- TablePen.DashStyle = System.Drawing.Drawing2D.DashStyle.DashDotDot;
- SearchAndAddSerialToComboBox(serialPort1, comboBox1);
- }