基于STM32F4的心电监护仪

这篇具有很好参考价值的文章主要介绍了基于STM32F4的心电监护仪。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

从题目中可以看出该课题来源于2020年省电赛A题的无线运动传感器节点的设计,该作品得过湖北省电赛二等奖,同时也是我本科毕业设计,这里我把自己做的关于心电部分的工作进行一次总结,也对我的大学四年进行一次总结。

一、硬件设计

  1. 处理器板子的选择

本研究的处理器模块选择正点原子公司的STM32F4最小系统板子,如图1所示,该最小系统板子搭载STM32F407ZGT6芯片,并具有 192KB的SRAM、1024KB的FLASH、丰富的定时器资源(12个16位定时器,2个32位定时器)、112个通用I/O口、2个DMA控制器以及1个FSMC接口,其中通过FSMC接口可以使得刷屏的速度可达3300W像素/秒,另外该板子还外扩了1M字节的SRAM芯片,更加有利于该处理器驱动4.3寸的LCD,这样极大加快心电监测仪的刷屏速度,而且STM32F407ZGT6这款芯片还集成FPU和DSP指令,可以加快数字滤波器的处理速度,而且该最小系统板子还将FSMC接口和其他IO口一并引出。
基于STM32F4的心电监护仪

  1. 心电采集板—ADS1292R模块的介绍

本研究最重要的地方便是心电采集板,关于心电信号的采集板的芯片选择TI公司的ADS1292R,外围电路参考TI公司所给的原理图和建议绘制,如图所示。
基于STM32F4的心电监护仪
关于ADS1292R的外围电路的介绍和使用,这里推荐这篇博文,ADS1292R的使用

  1. 温度模块----LMT70

温度检测模块采用LMT70温度传感器。其优点是:超小型、高精度、低功耗的模拟温度传感器。而缺点是:接触式的温度传感器,测体表温度存在一定误差。但是考虑到温度测量的精度以及测量方便,最终选择LMT70作为测温传感器,同时选择采用ADS1118具有PGA、电压基准、16位的高精度ADC对LMT70数据的温度模拟量进行采集。
基于STM32F4的心电监护仪

  1. 系统不带屏幕的外观

基于STM32F4的心电监护仪

二、GUI的设计

本系统为了更好的人机交互,采用4.3寸触摸屏搭载开源图形库LVGL,一方面将显示波形和数据与心电信号的采集和处理隔离开,另一方面是为了交互的方便和美观,系统运行界面如下图所示。整体界面主要有菜单、返回、图表、数据栏、导联状态灯以及开启心电采集按钮
基于STM32F4的心电监护仪
系统的菜单是通过LVGL的roller控件绘制的,roller里面选项的事件则是通过回调函数的形式调用。由于选中roller的选项,LVGL就会返回选项的值,因此我自己设计了函数指针数组来注册回调函数,并且将这个选中的序号通过数组方式调用函数,代码如下所示:

void (*oper_fuc[4])();//函数指针数组
void Menuitem_Init(void)
{
    oper_fuc[0]=send_type_server;
    oper_fuc[1]=Set_chart_div_line;
    oper_fuc[2]=clear_step;
	oper_fuc[3]=smooth_filter;
}
static void roller_event_handler(lv_obj_t * obj, lv_event_t event)
{
    static unsigned char count=0;
	 if(event==LV_EVENT_VALUE_CHANGED)
    {
        count=lv_roller_get_selected(obj);
    }
    if(event==LV_EVENT_CLICKED)
    {
        oper_fuc[count]();
    }
}

roller_event_handler是选中roller中的事件函数,在事件函数里面来回调选项的处理函数。roller中总共写了4个选项,分别为
send_type选择发送类型(支持发送到本地显示或者串口发送给上位机)、set_div_line是否设置图表的等分线、
clear_step清除界面上的数据、
smooth_filter是否进行平滑滤波
基于STM32F4的心电监护仪
本系统还设计导联状态指示灯,前面讨论过ADS1292R可以检测电联的脱落状态,因而这里用LVGL的led控件作为导联的状态指示,当检测到导联接入人体,led控件就会点亮。设计了红心周期性跳动,当检测到导联接入人体后,红心就会周期性跳动,当心电数据采样开始后,红心随着心率值的改变而跳动着。同时还设计了采样开始/停止按钮,可以随时暂停和开始采样心电信号。
除了以上看得见的设计之外,还创建了四个周期性的任务,任务优先级从高到低分别为:更新数据栏里的数据、更新导联状态、检查心电信号的纵轴范围、系统状态的检查。

三、导联体系的选择

心电信号本质上是测量人体体表的电信号,将电极通过一定的导联体系就可以记录到心电图,因而选择合适的电极是观察心电图至关重要的选择。在医学上常见的导联体系分别为标准12导联体系、Lewis导联、Fontaine导联、Cabrera导联、Nehb导联、frank导联、Mason-Likar导联等。标准12导联体系是医院所使用的,它由3个双极肢体导联、6个单极胸前导联、3个单极加压肢体导联所组成。
该系统的主要目的是实时检测心率和QRS宽度,因此选择的导联应该基于能观察心电中R波较大的原则。因而选择标准12导联中标准肢体导联I(见图左),或者Mason-Likar导联(见图右)
基于STM32F4的心电监护仪

四、心电电极选择

人体的内阻很高,因而心电信号是一个高内阻且幅度很低的信号,如果处理不好就会造成心电信号的衰减,因此就需要从两方面解决:
(1)降低与电极的接触阻抗(2)提高采集电路的输入阻抗。
基于STM32F4的心电监护仪
目前,市面上有三种电极,分别为湿电极、干电极和非接触式电极,这三种电极中湿电极的接触电阻最小,因而对于模拟前端的输入电阻不需要太大。湿电极主要由电极片、Ag/AgCl 涂层、导电胶等物质组成。 医学电极贴片与身体接触的是水凝胶(亲水化合物),“黑色”部分为Ag/AgCl,使用导电金属和导线与仪器连接,实物如图所示。
基于STM32F4的心电监护仪

五、心电信号时域和频域特征

人体的心电信号是一种非平稳、非线性、随机性比较强的微弱生理信号,幅值约为毫伏(mV)级,频率在0.05-100Hz之间。心电信号的每一个心跳循环由一系列有规律的波形组成,它们分别是P波、QRS复合波和T波,而这些波形的起点、终点、波峰、波谷、以及间期分别记录着心脏活动状态的详细信息
基于STM32F4的心电监护仪
心电信号各个波段的详细说明如下:
基于STM32F4的心电监护仪
心电各个波段的功率谱如下:
基于STM32F4的心电监护仪
心电信号的噪声分析如下:
基于STM32F4的心电监护仪
读者想对心电信号进一步了解可以参考如下链接:http://www.mythbird.com/ecgxin-hao-te-zheng/。

六、软件设计

6.1、系统总体设计

系统先从硬件初始化开始,其中包括串口初始化、触摸屏初始化、外部SRAM初始化、ADS1292R初始化、LMT70初始化、LVGL心跳定时器初始化。
其次就是LVGL初始化,主要是一些主题和变量的初始化。然后创建系统的UI界面和一些定时的任务。
最后初始化心电数据缓存、 数字低通滤波器初始化、心率数据缓存初始化。
完成以上的初始化,系统便进入主循环,等待心电数据输入缓存中出现数据,随后开始滤波,将滤波之后的数据写入心电输出缓存中,然后轮询LVGL的任务和触摸屏扫描。就这样不停地循环。其中心电输入缓存中的数据是通过中断从ADS1292R的输出引脚中读取,而心电输出缓存则是原始数据经过低通处理后的数据,等待LVGL显示任务的到来并显示在触摸屏上。系统总体框图和软件框图如下所示
基于STM32F4的心电监护仪
基于STM32F4的心电监护仪

6.2、系统总体设计

在前面讨论过心电信号频谱和噪声,因而要对心电信号进行滤波,为了同时实现心电信号的实时滤波和心电波形实时显示,所以有必要设计一个缓存区来解决这个难题。这里我打算用我自己设计的两个循环队列解决这个难题。
基于STM32F4的心电监护仪
为了使得在滤波的时候,心电数据依然能够采集,设计两个循环队列,如上图所示,其中IN_Buffer和OUT_Buffer的每个矩形框表示25x4个字节的空间,这取决一次需要多少字节的数据滤波。这里一次滤波需要25个int型的数据,因而每个缓存需要25x4字节。图中的蓝色填充表示缓存区中填满了数据,每次读完数据之后都需要切换缓存区,且IN_Buffer和OUT_Buffer的读写操作相反,即IN_Buffer的读操作是OUT_Buffer的写操作,程序框图如下图所示。
基于STM32F4的心电监护仪
图上所示的三个程序均是并行处理的,
程序1是通过外部中断的服务函数调用的
程序2则是在UI画图程序里面通过定时器周期性的调用
程序3则是在主程序中的滤波函数里面调用
程序1代码如下(ADS1292R采用中断方式读取数据):

void EXTI9_5_IRQHandler(void)
{
    if(EXTI->IMR&EXTI_Line5 && ADS_DRDY==0)//数据接收中断
    {
        ADS1292_Read_Data(ads1292_Cache);//数据存到9字节缓冲区
        Update_ECG_Data(ads1292_Cache);
        Cheack_lead_stata(ads1292_Cache);
		if(state_pcb.SampleStartFlag==true)
			WriteAdsInBuffer(ecg_info.ecg_data);//数据写入缓存区
    } 
	EXTI_ClearITPendingBit(EXTI_Line5);
}

程序2代码如下(LVGL的心跳在定时器中周期调用,同时程序2也在其中运行,主要从滤波后的数据缓存中取出数据进行波形显示):

void Wave_show(void)
{
    int value=0;
    if(ReadEcgOutBuffer(&value)!=0) {
        if(ecg_graph.send_type==GRAPH) {
         ecg_graph.y_pose=Transf_EcgData_To_Vert(value,ecg_graph.sacle);
			  chart_add_data(ecg_graph.y_pose);
            set_data_into_heart_buff(ecg_graph.y_pose);
        } else if(ecg_graph.send_type==USART) {
            //EcgSendByUart(value);
			printf("%d\r\n",(int)alg(value/200));
        }
    }
}
//定时器3中断服务程序
void TIM3_IRQHandler(void)
{ 	static u8 show_cnt=0;   		  			    
	if(TIM3->SR&TIM_IT_Update)//溢出中断
	{	show_cnt++;
		lv_tick_inc(1);//lvgl的1ms心跳
		if(show_cnt==3){
		show_cnt=0;
		 Wave_show();
		}
	}				   
	TIM3->SR = (uint16_t)~TIM_IT_Update;
}

程序3代码如下(在滤波函数中调用,用于承上启下,即从IN缓存中取出数据,滤波之后写入OUT缓存中):

void arm_fir_f32_lp(void)
{
	float32_t *inputf32, *outputf32;
	if(ReadAdsInBuffer() && WriterEcgOutBuffer()){//指针定位成功
		/* 初始化输入输出缓存指针 */
		inputf32 = (float32_t *)InFifoDev.rp;
		outputf32 =(float32_t *)OutFifoDev.wp;
		
	  /* 实现FIR滤波 */
		arm_fir_f32(&S, inputf32, outputf32, BLOCK_SIZE);
		//my_memcpy(OutFifoDev.wp,InFifoDev.rp,BLOCK_SIZE*4);
		InFifoDev.state[InFifoDev.read_front]=Empty;
		InFifoDev.read_front=(InFifoDev.read_front+1)%PACK_NUM;//切换读缓存块
	
		OutFifoDev.state[OutFifoDev.writer_rear]=Full;
		OutFifoDev.writer_rear=(OutFifoDev.writer_rear+1)%PACK_NUM;//切换写缓存块
	}

}

关于缓存切换代码如下:

static void WriteAdsInBuffer(int date)
{
	static u8 cnt=0;
	if(InFifoDev.state[InFifoDev.writer_rear]==Empty){//缓存块可写
		InFifoDev.wp=&AdsInBuffer[InFifoDev.writer_rear*(BLOCK_SIZE)];//将写指针定位写缓存块
		InFifoDev.wp[cnt++]=date;
		if(cnt==BLOCK_SIZE){
			cnt=0;
			InFifoDev.state[InFifoDev.writer_rear]=Full;
			InFifoDev.writer_rear=(InFifoDev.writer_rear+1)%PACK_NUM;//切换写缓存块
			
		}
	}
}

//定位读指针
//成功则返回1,不成功则返回0
u8 ReadAdsInBuffer(void)
{
	if(InFifoDev.state[InFifoDev.read_front]==Full){//缓存块可读
		InFifoDev.rp=&AdsInBuffer[InFifoDev.read_front*(BLOCK_SIZE)];//将读指针定位读缓存块
		return 1;
	}
	return 0;
}

//定位读指针
u8 WriterEcgOutBuffer(void)
{
	if(OutFifoDev.state[OutFifoDev.writer_rear]==Empty){//缓存块可写
		OutFifoDev.wp=&EcgOutBuffer[OutFifoDev.writer_rear*(BLOCK_SIZE)];//将读指针定位读缓存块
		return 1;
	}
	return 0;
}

//成功则返回1,不成功则返回0
u8 ReadEcgOutBuffer(int32_t *p)
{
	static u8 cnt=0;
	if(OutFifoDev.state[OutFifoDev.read_front]==Full){//缓存块可读
		OutFifoDev.rp=&EcgOutBuffer[OutFifoDev.read_front*(BLOCK_SIZE)];//将写指针定位读缓存块
		*p=OutFifoDev.rp[cnt++];
		if(cnt==BLOCK_SIZE){
			cnt=0;
			OutFifoDev.state[OutFifoDev.read_front]=Empty;
			OutFifoDev.read_front=(OutFifoDev.read_front+1)%PACK_NUM;//切换写读缓存块
		}
		return 1;
	}
	return 0;
}

6.3、心电信号滤波

  1. 工频噪声滤除

滤除工频噪声的数字滤波算法主要有经典滤波器、小波变换、自适应滤波。小波变换能将心电信号进行多层分解,可以使得心电信号与工频噪声分离,但是计算量大,所占用的中间变量也比较多,对于单片机来说,处理的速度也不够快,因而对于系统的实时性这一指标很难实现。自适应滤波能够自动跟踪工频噪声的改变,但是需要增加一个输入信号作为参考,因而增加了系统的复杂性。在前面也讨论过心电信号95%的能量都是集中在0~40Hz,而工频噪声则在50Hz左右,过渡带比较宽,因而可以选择截止频率为40Hz的低通滤波器
该低通滤波器利用MATLAB的FDATOOL生成,只需要选择低通滤波器是FIR结构,选择Blackman-Harris窗函数,滤波器的阶数定为50,选择采样频率为250Hz,截止频率为40Hz,参数如下图所示:
基于STM32F4的心电监护仪
然后利用FDATOOL生成的冲激响应的数组,选择ARM官方的DSP库,调用arm_fir_f32函数,既可以完成一次滤波。但是在这之前,需要调用arm_fir_init_f32进行初始化。
滤波器系数如下:

const float32_t fir32LP[NUM_TAPS] = {
  -7.484454468902e-22,-3.269336712398e-06,-1.365915864079e-05,-5.014073980636e-06,
  6.804735231975e-05,0.0001662336497003,7.965197426322e-05,-0.0003784662837741,
  -0.0008928563387901,-0.0005280588787408, 0.001284875839485, 0.003225662215767,
     0.0022425431358,-0.003157084585057,-0.009028737319977,-0.007219934929014,
   0.006057868257093,  0.02144319498633,  0.01971312591228,-0.009448071870685,
   -0.04806332586811, -0.05291973061693,  0.01224382260678,   0.1388254178822,
     0.2663085232723,   0.3199984843521,   0.2663085232723,   0.1388254178822,
    0.01224382260678, -0.05291973061693, -0.04806332586811,-0.009448071870685,
    0.01971312591228,  0.02144319498633, 0.006057868257093,-0.007219934929014,
  -0.009028737319977,-0.003157084585057,   0.0022425431358, 0.003225662215767,
   0.001284875839485,-0.0005280588787408,-0.0008928563387901,-0.0003784662837741,
  7.965197426322e-05,0.0001662336497003,6.804735231975e-05,-5.014073980636e-06,
  -1.365915864079e-05,-3.269336712399e-06,-7.484454468902e-22
};

static float32_t firStateF32[BLOCK_SIZE + NUM_TAPS - 1];
arm_fir_instance_f32 S;
void arm_fir_Init(void)
{
	arm_fir_init_f32(&S, NUM_TAPS, (float32_t *)&fir32LP[0], &firStateF32[0], BLOCK_SIZE);
}

滤波函数见程序3(往上找)

  1. 基线漂移

基线漂移与工频噪声不同,它是由于呼吸和电极滑动变化所异致的,其频率一般低于1Hz左右。常见对于基线漂移滤除的数字算法有高通滤波器、中值滤波、小波变换、形态学滤波、曲线拟合等,其中高通滤波器可能会对心电信号的ST波段产生影响,毕竟基线漂移的频率也在ST波段里面。曲线拟合对较大的基线漂移处理能力较弱,处理的效果与处理数据的长度成正相关,因而不适用实时处理的系统。小波变换计算量大,也不适用实时处理的系统。相比之下,形态学滤波对心电信号的基线漂移滤除效果更好,计算量也比中值滤波小。但是形态学滤波要求数据长度足够长,因而会改变前面的缓存结构,并且在本系统中并未太严重的基线漂移,系统的任务也比较多,多方面权衡之下,选择不处理基线漂移

  1. 肌电噪声的抑制

肌电噪声主要是由于人体肌肉颤抖导致体表的电位发生变化,这种噪声通过电极贴传导至心电模拟前端,并且这种噪声持续时间较短,使得ECG信号波形产生细小的波纹,这种噪声频率分布比较广,前面已经将心电信号通过截止频率为40Hz的低通滤波器,因而需要5点平滑滤波将细小的波纹滤除,为了不影响心电信号的实时处理,因而改进版的平滑滤波器代码如下:

/*
 * 滑动平均值滤波。
 * 每调用一次,就加入一个新数据,并得到当前的滤波值。
 */
float alg(float new_val)
{
    /* 用一个减法,就做了"丢弃最旧的数据,加入最新的数据"这一操作 */
    sum += (new_val - buf[pos]);
    buf[pos] = new_val;
    pos = (pos + 1) % MAX_COUNT;
    /* 个数不足时,cnt是实际个数,个数足够时,cnt最多也只是MAX_COUNT */
    pcnt += (pcnt < MAX_COUNT);
    return sum / MAX_COUNT;
}

6.4、心率和QRS宽度检测

心率和QRS宽度检测作为本系统的算法核心,有了心率值和QRS宽度值才能进一步判断常见的心律失常。心率基本上都是检测两个R波之间的时隙来计算的,常见检测R的算法主要有阈值法、模板法和语句描述法。
而本系统的心率和QRS宽度检测算法是在一起检测的,所采用的算法是幅度阈值检测和差分检测相结合,因为观察心电信号的R波,发现R波是具有窄的脉冲,且脉冲的幅度是心电信号最高的,因而采用幅度和一阶差分共同约束找到R波,同时在找R波的同时还可以估计出QRS的宽度,算法的框图如图
基于STM32F4的心电监护仪
心率检测和QRS宽度检测算法是采用状态机的编程思想,通过R波幅度大且从Q到R一直递增,并且R波到S波的一阶差分值很大,从而将R波定位出来,检测两个R波之前的时间,然后通过如下公式就可以计算出心率:
H R = ( 60 ∗ S a m p l e R a t e ) / c o u n t HR=(60*SampleRate) /count HR=(60SampleRate)/count

而QRS宽度则是由
Q R S = Q R S c n t ∗ 2.2 ∗ 1000 / ( S a m p l e R a t e ) QRS=QRScnt*2.2* 1000/(SampleRate) QRS=QRScnt2.21000/(SampleRate)

上式中的2.2是估计值,因为QRS_cnt是在检测到R波之后才开始计数,并且未到S波谷停止计数,观察QRS波,发现Q到R与R到S近似对称,因而采用2.2这个估计值,这也是实时检测的缺陷,检测的样本不多。
心率算法和QRS宽度检测代码如下:

/**
  * @Brief 测量心率
  * @Call
  * @Param
  * @Note
  * @Retval
  */
void ecg_heart_rate(int data)
{
	int Signal=data;
			
	if(Signal>hr.vmax)
		hr.vmax=Signal;
	if(Signal<hr.vmin)
		hr.vmin=Signal;
	thresh=hr.vmax-(hr.vmax-hr.vmin)/5;
	  
	for( uint16_t i = 0; i <= DATA_NUM_CAL_HR - 2; i++ )
    {
        DataArrayCalHR[i] =	DataArrayCalHR[i + 1];
    }
	 DataArrayCalHR[DATA_NUM_CAL_HR - 1] = Signal;
    Diff_Arrray( DiffDataArrayCalHR, DataArrayCalHR, DATA_NUM_CAL_HR );     //差分
	
	if(hr.flag==StartDetected){
		uint8_t FlagAllDiffRise = true;

            for( uint16_t i = 0; i <= DATA_NUM_CAL_HR - 2; i++ ) //判断波形是否一直上升
            {
                if( DiffDataArrayCalHR[i] <= 0 )
                {
                    FlagAllDiffRise = false;
                    break;
                }
            }
			if(FlagAllDiffRise==true){
				hr.flag=QWave;
			}
	}
    else if(hr.flag==QWave)//已经找Q波
    {
		if(DataArrayCalHR[DATA_NUM_CAL_HR-1]>thresh){
			if(hr.count>125){
				if( hr.firstBeat==true )//如果已经找到 过R波
				{
				hr.rate=(float)60*SAMPLE_RATE/(hr.count);
				hr.count=0;//清除计数
				hr.flag=RWave;
				QRScntflag=true;
				} else if(hr.firstBeat==false) {
				hr.firstBeat=true;
				hr.count=0;//清除计数
				hr.flag=RWave;
				QRScntflag=true;
				}
			}			
		}
   }
	 else if(hr.flag==RWave ){
		if(DiffDataArrayCalHR[0]<-(hr.vmax-hr.vmin)/5){
				hr.flag=SWave;
		}
	}
	else if(hr.flag==SWave){
			if(hr.QRS_cnt<15){
			hr.flag=StartDetected;
			hr.QRS=hr.QRS_cnt*22*100/SAMPLE_RATE;
			hr.QRS_cnt=0;
			QRScntflag=false;
			}else {
			hr.flag=StartDetected;
			hr.QRS=0;
			hr.QRS_cnt=0;
			QRScntflag=false;
			}
	}
		if(hr.count>420){
				hr.firstBeat=false;
				hr.flag=StartDetected;
				hr.vmax=Heart_MIN;
				hr.vmin=Heart_MAX;
				hr.rate=0;
				thresh=0;
				hr.count=0;//清除计数
			}
}

七、实机演示

实机演示

八、总结与展望

本系统因为没有加入操作系统的管理,造成实现的功能较为少,并且数据分析功能因为缺乏强大处理器造成数据分析功能所需要的指标太少,要想对心电信号实现自动化分析,必须对心电信号更多的信息进行提前,而且由于处理器的限制使得一些强大的数字算法是用不了,而且将采集—滤波—显示集成一体化本身就显得笨重,会让每个处理单元相互牵制,严重的会影响系统的采样率,造成一些不必要的误差。所以日后会有针对选择更为强大的处理器,会将采集、滤波、显示分开来。同时为了减少外界噪声,应该选择更为干净的电源和屏蔽外壳,系统也不能以这种模块化的方式拼接在一起,日后会选择画PCB,将所有模块集成在PCB上,在套上屏蔽壳,这样能够最大程度减少外界噪声干扰。并且无线通信模块保留着,但是上位机并未实现,因而日后需要增加这一项功能。总结以上的不足如下:

  1. 使用更为强大的处理器,将采集、滤波、显示分开来。
  2. 所有模块应该集成在PCB上,在增加屏蔽壳。
  3. 开发上位机软件,实现心电数据的无线通信的功能。

代码我会放在github上,链接如下:
https://github.com/lvzhe-speed/STM32_ECG
后面我去考研了,希望能够考上,以后会每一年至少写一篇技术博客,谢谢各位大佬前来斧正,欢迎探讨,一起技术进步。其实我也想借此说一下,不能因为一次失败就否定自己之前的努力,100-1=0这也许是别人对你的评价,但自己不能认同这个错误的式子,人生不仅有加法,也有减法。也希望对别人多一点谅解,每个人都不容易,也许今日之菜鸟,明日之大鹏,总会翱翔九天。文章来源地址https://www.toymoban.com/news/detail-437962.html

到了这里,关于基于STM32F4的心电监护仪的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【stm32开发笔记】基于HAL库的STM32F4添加DSP库

    本文分两种方法添加DSP库:1.CubeMX直接配置ioc添加; 2.KEIL内添加; 简述:补齐全部lib库-添加DSP包-使能DSP勾选-添加头文件及魔术棒配置-测试 1.补齐lib库。( 如果使用直接默认添加的库,是不支持FPU的,所以需要补齐后找到所需的lib文件进行替换,在MX的工程管理栏,选择复制所

    2024年02月16日
    浏览(58)
  • 基于STM32F4的CANOpen移植教程(超级详细)

    本专题相关教程: 基于STM32F4的CANOpen移植教程 基于STM32F4的CANopen快速SDO通信 linux下CANopen for python的使用 基于Linux C的CANopen移植 CANopen补充–时间计算出错 CANopen补充–主站检测节点是否在线 为了在STM32F4上能够运行CANopen(CanFestival),跟着网上的教程操作,发现总是不够详细。

    2024年02月02日
    浏览(46)
  • 关于LWIP用法之HTTPD:基于STM32F4搭建web服务器

    一,STM32CUBEMX配置(使用的是6.4.0版本) 前提是在配置好LWIP的情况下(能ping通你的开发板),使能HTTPD功能。 然后是使能LWIP_HTTPD_CGI, 使能:LWIP_HTTPD_SUPPORT_POST(), 使能 :HTTPD_USE_CUSTOM_FSDATA。 会发现fs.c这个文件的#include HTTPD_FSDATA_FILE,这一句编译报错,解决办法:1) 在KEIL中lwipop

    2023年04月08日
    浏览(38)
  • 金科威GoldWayUT4000监护仪协议对接

    通过网口,成功对接到参数: Sys、Dia、Map、Temp、HR、SPO2、Resp、RR、EtCO2、InCO2等数值。  

    2024年02月15日
    浏览(29)
  • 运动控制器设计——基于FreeModbus在STM32F4平台实现ModbusTCP和ModbusRTU

    本文笔者最近的项目是设计一款运动控制器,MCU使用的是STM32F429,要求是通过Modbus TCP协议实现与示教器通讯,并通过ModbusRTU实现与触摸屏通讯。 本文将介绍在STM32F4上实现 ModbusTCP和ModbusRTU通讯 的过程。笔者才疏学浅,如有错误还请指正。 Modbus协议是典型的主-从通讯结构,链

    2024年02月05日
    浏览(55)
  • GD32F4移植STM32F4

    近期在项目中采用了GD32F407VET6替换原项目中的STM32F407VET6,网传GD的兼容性很好,之前也用F1系统的替换了一下,按照CSND各位大佬的经验一步步改进了代码,测试直接通过,现在也一直在项目中实际应用了,一直没有出问题。 所以这SMT时,嘉立创没有STM的货果断换成了GD,可换时

    2024年02月16日
    浏览(83)
  • 基于STM32F4的FFT+测频率幅值相位差,波形显示,示波器,时域频域分析相关工程

    一入电赛深似海,此话不假,个人感觉很累,但是收获确实多。本人去年参加了国赛,电赛提前半个月就开始着手准备了,只记得那时候不是调试就是在调试的路上,也因此留下了宝贵的我姑且称之为“经验”,作为一名小白,借此机会跟各位老白和小白分享一下。我训练较

    2024年02月03日
    浏览(52)
  • 【STM32】STM32F4 GPIO详解与配置

    GPIO是通用输入输出端口的简称,为STM32可控制的引脚,STM32芯片的GPIO引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。STM32芯片的GPIO被分成很多组,每组有16个引脚,所有的GPIO引脚都有基本的输入输出功能。 其中保护二极管使GPIO能够5V电压容忍。在

    2024年02月07日
    浏览(52)
  • STM32F4Timer

    ref https://blog.csdn.net/zhuxinmingde/article/details/131784852?ops_request_misc=request_id=biz_id=102utm_term=STM32%20%E9%AB%98%E7%BA%A7%EF%BC%8C%E6%99%AE%E9%80%9A%EF%BC%8C%E5%9F%BA%E6%9C%AC%E5%AE%9A%E6%97%B6%E5%99%A8utm_medium=distribute.pc_search_result.none-task-blog-2 all sobaiduweb~default-1-131784852.142 v99 controlspm=1018.2226.3001.4187 1. Timer re

    2024年02月02日
    浏览(46)
  • 【STM32】STM32F4 GPIO口映射与复用

    STM32F4 有很多的内置外设,这些外设的外部引脚都是与 GPIO 复用的。也就是说,一个 GPIO 如果可以复用为内置外设的功能引脚,那么当这个 GPIO 作为内置外设使用的时候,就叫做复用。 这部分知识在《STM32F4 中文参考手册》第七章和芯片数据手册有详细的讲解哪些 GPIO 管脚是

    2024年02月15日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包