发 帖  
原厂入驻New

stm32实现心电监测-原理图单片机程序C#上位机程序

2019-12-7 19:55:55  686 stm32 单片机
分享
2
本帖最后由 gandonggandong 于 2019-12-24 08:26 编辑

十年前接触生物电子让我对电子产生浓厚的兴趣,让我感到电子科技的博大精深无所不能。最近用STM32和C#实现心电监测,分享给大家一起探讨,我也把这些技术资料整理下。
原理图
心电前端采集电路采用仪表放大器,仪表放大器对于共模干扰有很强的抑制力,适合做心电采集前端电路。传输部分采用USB实现虚拟串口和上位机对接,具体电路如下所示

PCB

上位机程序


单片机采用stm32内部AD采集,关键程序如下
  1. #include "sys.h"
  2. #include "delay.h"
  3. #include "LED.h"                           
  4. #include "usb_lib.h"
  5. #include "hw_config.h"
  6. #include "usb_pwr.h"         
  7. #include "usb_prop.h"
  8. #include "bsp_usart.h"
  9. #include "bsp_adc.h"

  10. extern char USB_TX_data[512],USB_RX_data[512];
  11. extern u8 USB_Tx_Counter,USB_Rx_Counter,USB_TX_flag,USB_RX_flag;
  12. extern char U1_TX_data[512],U1_RX_data[512];
  13. extern u8 U1_Tx_Counter,U1_Rx_Counter,U1_TX_flag,U1_RX_flag;

  14. extern short        AD_BUF[1024];
  15. extern unsigned int tiM3_count;
  16. extern char TIM3_flag;

  17. void RCC_HSI_Configuration(void)
  18. {
  19.     RCC_DeInit();//??? RCC?????????

  20.     RCC_HSICmd(ENABLE);//??HSI  
  21.     while(RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET)//??HSI????
  22.     {
  23.     }

  24.     IF(1)
  25.     {
  26.         FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
  27.         FLASH_SetLatency(FLASH_Latency_2);
  28.         
  29.         RCC_HCLKConfig(RCC_SYSCLK_Div1);   
  30.         RCC_PCLK1Config(RCC_HCLK_Div2);
  31.         RCC_PCLK2Config(RCC_HCLK_Div1);
  32.         RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_12);
  33.         RCC_PLLCmd(ENABLE);//??PLL???????,????????
  34.         while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
  35.         {
  36.         }
  37.         RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);  
  38.         while(RCC_GetSYSCLKSource() != 0x08)
  39.         {
  40.         }
  41.     }
  42. }
  43. int main(void)
  44. {         
  45.         u16 i;
  46.         u16 temp;
  47.         u8 usbstatus=0;       
  48.         RCC_HSI_Configuration();
  49.         delay_init();                     //ÑÓʱº¯Êý³õʼ»¯          
  50.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);         //ÉèÖÃNVICÖжϷÖ×é2:2λÇÀÕ¼ÓÅÏȼ¶£¬2λÏìÓ¦ÓÅÏȼ¶
  51.         LED_Init();
  52.         USART1_Config();
  53.         ADC_Config();
  54.        
  55.         delay_ms(10000);
  56.         USB_Port_Set(0);        
  57.         delay_ms(50);
  58.         USB_Port_Set(1);       
  59.         Set_USBclock();   
  60.         USB_Interrupts_Config();   
  61.         USB_Init();       
  62.        
  63.         while(1)
  64.         {
  65.                 if(TIM3_flag==1)
  66.                 {
  67.                         GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);
  68.                         TIM3_flag=0;
  69.                         temp=ADC_GetConversionValue(ADC1);
  70.                         USB_USART_SendData(0x55);//ÒÔ×Ö½Ú·½Ê½,·¢Ë͸øUSB
  71.                         USB_USART_SendData(0xaa);//ÒÔ×Ö½Ú·½Ê½,·¢Ë͸øUSB
  72.                         USB_USART_SendData(temp>>8);//ÒÔ×Ö½Ú·½Ê½,·¢Ë͸øUSB
  73.                         USB_USART_SendData(temp);//ÒÔ×Ö½Ú·½Ê½,·¢Ë͸øUSB
  74.                         USB_USART_RX_STA=0;
  75.                         GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET);
  76.                 }
  77.                 if(USB_RX_flag==1)
  78.                 {
  79.                         USB_RX_flag=0;
  80.                 }
  81.                 if(usbstatus!=bDeviceState)//USBÁ¬½Ó״̬·¢ÉúÁ˸ıä.
  82.                 {
  83.                         usbstatus=bDeviceState;//¼Ç¼ÐµÄ״̬
  84.                 }
  85.         }
  86. }
复制代码
  1. void ADC_Config(void)
  2. {
  3.         GPIO_InitTypeDef  GPIO_InitStructure;
  4.         NVIC_InitTypeDef   NVIC_InitStructure;
  5.         ADC_InitTypeDef   ADC_InitStructure;
  6.         TIM_TimeBaseInitTypeDef                        TIM_TimeBaseInitStruct;
  7.        
  8.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1  , ENABLE );
  9.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
  10.         RCC_ADCCLKConfig(RCC_PCLK2_Div6);
  11.         RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM3 , ENABLE);
  12.        
  13.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
  14.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//Ä£ÄâÊäÈë
  15.         GPIO_Init(GPIOA, &GPIO_InitStructure);

  16.         ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;  
  17.         ADC_InitStructure.ADC_ScanConvMode = ENABLE;  
  18.         ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
  19.         ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;  
  20.         ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  21.         ADC_InitStructure.ADC_NbrOfChannel = 1;  
  22.         ADC_Init(ADC1, &ADC_InitStructure);  
  23.        
  24.         ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_239Cycles5);
  25.        
  26.         ADC_Cmd(ADC1, ENABLE);  
  27.        
  28.         ADC_ResetCalibration(ADC1);  
  29.         while(ADC_GetResetCalibrationStatus(ADC1));
  30.         ADC_StartCalibration(ADC1);  
  31.         while(ADC_GetCalibrationStatus(ADC1));
  32.        
  33.         ADC_SoftwareStartConvCmd(ADC1, ENABLE);

  34.         TIM_DeInit(TIM3);        //½«ÍâÉèTIM3¼Ä´æÆ÷ÖØÉèΪȱʡֵ  
  35.         TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1 ;    //ÉèÖÃÁËʱÖÓ·Ö¸î(Tck_tim)
  36.         TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up ;   //Ñ¡ÔñÁ˼ÆÊýÆ÷ģʽ(TIMÏòÉϼÆÊýģʽ)  
  37.         TIM_TimeBaseInitStruct.TIM_Period = 999 ;       //É趨¼ÆÊýÆ÷×Ô¶¯ÖØ×°Öµ,È¡Öµ·¶Î§0x0000~0xFFFF   
  38.         TIM_TimeBaseInitStruct.TIM_Prescaler = 47 ;    //ÉèÖÃÓÃÀ´×÷ΪTIM3ʱÖÓƵÂʳýÊýµÄÔ¤·ÖƵֵΪ(7199+1),È¡Öµ·¶Î§0x0000~0xFFFF
  39.         TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct ) ;      

  40.         TIM_CleaRFlag(TIM3, TIM_FLAG_Update);         //Çå³ýTIM3µÄ´ý´¦Àí±ê־λ   
  41.         TIM_ITConfig(TIM3, TIM_IT_Update,ENABLE);     //ʹÄÜTIM3ÖжϠ 
  42.         TIM_Cmd(TIM3, ENABLE);         //ʹÄÜTIM3ÍâÉè  
  43.        
  44.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);    //NVIC_Group:ÏÈÕ¼ÓÅÏȼ¶2룬´ÓÓÅÏȼ¶2λ  
  45.   NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;    //ÅäÖÃΪTIM3ÖжϠ 
  46.   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  //ÏÈÕ¼ÓÅÏȼ¶Îª1  
  47.   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;   //´ÓÓÅÏȼ¶Îª2  
  48.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;      //ʹÄÜÖжÏͨµÀ  
  49.   NVIC_Init(&NVIC_InitStructure);
  50. }
复制代码


上位机采用C#,C#开发window系统应用程序非常方便,程序如下
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.IO.Ports;
  7. using System.Linq;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. using System.Windows.Forms;

  11. namespace WindowsFormsApplication4
  12. {
  13.     public partial class Form1 : Form
  14.     {
  15.         private StringBuilder sb = new StringBuilder();     //为了避免在接收处理函数中反复调用,依然声明为一个全局变量
  16.         long AD_num = 12;
  17.         long LD_num = 1;
  18.         long MD_num = 11;
  19.         long QP_num = 0;
  20.         int QP_flag = 0;
  21.         long uart_count = 0;
  22.         private const int Unit_length = 32;//单位格大小
  23.         private const int X_End = 1024+512+256+48;//Y轴最大数值
  24.         private const int Y_End = 512+256+128;//Y轴最大数值
  25.         private const int X_Start = 48;//Y轴最大数值
  26.         private const int Y_Start = 128;//Y轴最大数值
  27.         private const int MaxStep = 33;//绘制单位最大值
  28.         private const int MinStep = 1;//绘制单位最小值
  29.         private const int StartPrint = 100;//点坐标偏移量
  30.         private List<int> DataList = new List<int>();//数据结构----线性链表
  31.         private Pen TablePen = new Pen(Color.FromArgb(0x80, 0x00, 0x00));//轴线颜色
  32.         private Pen LinesPen = new Pen(Color.FromArgb(0x00, 0x80, 0x80));//波形颜色
  33.         public Form1()
  34.         {
  35.             this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint |
  36.                            ControlStyles.AllPaintingInWmPaint,
  37.                            true);//开启双缓冲
  38.             this.UpdateStyles();
  39.             InitializeComponent();
  40.             System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;
  41.             TablePen.DashStyle = System.Drawing.Drawing2D.DashStyle.DashDotDot;
  42.             SearchAndAddSerialToComboBox(serialPort1, comboBox1);
  43.         }
  44.         private void SearchAndAddSerialToComboBox(SerialPort MyPort, ComboBox MyBox)
  45.         {                                                               //将可用端口号添加到ComboBox
  46.             string Buffer;                                              //缓存
  47.             comboBox1.Items.Clear();                                        //清空ComboBox内容
  48.             //int count = 0;
  49.             for (int i = 1; i < 30; i++)                                //循环
  50.             {
  51.                 try                                                     //核心原理是依靠try和catch完成遍历
  52.                 {
  53.                     Buffer = "COM" + i.ToString();
  54.                     MyPort.PortName = Buffer;
  55.                     MyPort.Open();                                      //如果失败,后面的代码不会执行
  56.                                                                         // MyString[count] = Buffer;
  57.                     comboBox1.Items.Add(Buffer);                            //打开成功,添加至下俩列表
  58.                     MyPort.Close();                                     //关闭
  59.                 }
  60.                 catch
  61.                 {
  62.                 }
  63.             }
  64.         }
  65.         private void button1_Click(object sender, EventArgs e)
  66.         {
  67.             try
  68.             {
  69.                 //将可能产生异常的代码放置在try块中
  70.                 //根据当前串口属性来判断是否打开
  71.                 if (serialPort1.IsOpen)
  72.                 {
  73.                     //串口已经处于打开状态
  74.                     serialPort1.Close();    //关闭串口
  75.                     button1.Text = "打开串口";
  76.                     button1.BackColor = Color.ForestGreen;
  77.                     comboBox1.Enabled = true;
  78.                     uart_count = 0;
  79.                 }
  80.                 else
  81.                 {
  82.                     //串口已经处于关闭状态,则设置好串口属性后打开
  83.                     comboBox1.Enabled = false;
  84.                     serialPort1.PortName = comboBox1.Text;
  85.                     serialPort1.BaudRate = 115200;
  86.                     serialPort1.DataBits = 8;
  87.                     serialPort1.Parity = System.IO.Ports.Parity.None;
  88.                     serialPort1.StopBits = System.IO.Ports.StopBits.One;
  89.                     serialPort1.Open();     //打开串口
  90.                     button1.Text = "关闭串口";
  91.                     button1.BackColor = Color.Firebrick;
  92.                 }
  93.             }
  94.             catch (Exception ex)
  95.             {
  96.                 //捕获可能发生的异常并进行处理

  97.                 //捕获到异常,创建一个新的对象,之前的不可以再用
  98.                 serialPort1 = new System.IO.Ports.SerialPort();
  99.                 //刷新COM口选项
  100.                 comboBox1.Items.Clear();
  101.                 comboBox1.Items.AddRange(System.IO.Ports.SerialPort.GetPortNames());
  102.                 //响铃并显示异常给用户
  103.                 System.Media.SystemSounds.Beep.Play();
  104.                 button1.Text = "打开串口";
  105.                 button1.BackColor = Color.ForestGreen;
  106.                 MessageBox.Show(ex.Message);
  107.             }
  108.         }

  109.         private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
  110.         {
  111.             int num = serialPort1.BytesToRead;
  112.             byte[] received_buf = new byte[num];
  113.             int[] show_buf = new int[num];
  114.             float show_data = 1;
  115.             
  116.             serialPort1.Read(received_buf, 0, num);           
  117.             if (num == 4)
  118.             {
  119.                 uart_count = uart_count + 4;

  120.                 show_data =  ((long)(received_buf[2] << 8) + (long)(received_buf[3]))*768/4096;

  121.                
  122.                 show_buf[0] = (int)show_data;
  123.                 DataList.Add(show_buf[0]);//链表尾部添加数据
  124.                 Invalidate();            //刷新显示
  125.                 sb.Clear();
  126.                 try
  127.                 {
  128.                     //因为要访问UI资源,所以需要使用invoke方式同步ui
  129.                     this.Invoke((EventHandler)(delegate
  130.                     {
  131.                         textBox1.Clear();
  132.                         textBox1.AppendText(uart_count.ToString("F2"));
  133.                     }
  134.                        )
  135.                     );

  136.                 }
  137.                 catch (Exception ex)
  138.                 {
  139.                     //响铃并显示异常给用户
  140.                     System.Media.SystemSounds.Beep.Play();
  141.                     MessageBox.Show(ex.Message);

  142.                 }
  143.             }
  144.         }
  145.         private void Form1_Paint(object sender, PaintEventArgs e)//画
  146.         {
  147.             String Str = "";
  148.             System.Drawing.Drawing2D.GraphicsPath gp = new System.Drawing.Drawing2D.GraphicsPath();
  149.             e.Graphics.FillRectangle(Brushes.White, e.Graphics.ClipBounds);
  150.             //Draw Y 纵向轴绘制
  151.             for (int i = 0; i <= (X_End - X_Start) / Unit_length; i++)
  152.             {
  153.                 e.Graphics.DrawLine(TablePen, X_Start + i * Unit_length, Y_Start, X_Start + i * Unit_length, Y_End);//画线
  154.                 gp.AddString(i.ToString(), this.Font.FontFamily, (int)FontStyle.Regular, 12, new RectangleF(X_Start + i * Unit_length - 7, Y_End + 4, 400, 50), null);//添加文字
  155.             }
  156.             //Draw X 横向轴绘制
  157.             for (int i = 0; i <= (Y_End - Y_Start) / Unit_length; i++)
  158.             {
  159.                 e.Graphics.DrawLine(TablePen, X_Start, Y_Start + i * Unit_length, X_End, Y_Start + i * Unit_length);//画线
  160.                // if (i == 17)           break;
  161.                 gp.AddString((((12 - i) * Unit_length).ToString() ), this.Font.FontFamily, (int)FontStyle.Regular, 14, new RectangleF(X_Start - 50, Y_Start + i * Unit_length - 8, 400, 50), null);//添加文字
  162.             }
  163.             e.Graphics.DrawPath(Pens.Black, gp);//写文字
  164.             if (DataList.Count - 1 >= (X_End - X_Start))//如果数据量大于可容纳的数据量,即删除最左数据
  165.             {
  166.                 DataList.RemoveRange(0, DataList.Count - (X_End - X_Start) - 1);
  167.             }
  168.             for (int i = 0; i < DataList.Count - 1; i++)//绘制
  169.             {
  170.                 e.Graphics.DrawLine(LinesPen, X_Start + i, Y_End - DataList[i], X_Start + (i + 1), Y_End - DataList[i + 1]);
  171.             }

  172.         }
  173.     }
  174. }
复制代码

相关经验

评论

高级模式
您需要登录后才可以回帖 登录 | 注册

发经验
关闭

站长推荐 上一条 /6 下一条

快速回复 返回顶部 返回列表