目录
一、原理简述
二、系统硬件设计
1.stm32f103核心控制器
2.电机驱动模块
3.LCD显示屏模块
4.注射液滴速检测模块
5.湿度检测模块
6.声光报警模块
7.按键控制模块
三、系统软件设计
1.声光报警模块及电机驱动软件设计
2.LCD显示屏软件设计
3.注射液滴速检测模块软件设计
4.湿度检测模块软件设计
四、实物展示
五、完整原理图
六、完整代码
一、原理简述
目前,医院进行静脉注射治疗都是采用手动控制,需要根据病人的实际情况,手动调节输液的速度,还需要时刻关注注射液的剩余量,及时针对各种不确定情况做出反应。显然,这种静脉注射方式很不方便,有着很大的不确定性。
本设计将采用stm32为核心控制器件,采用红外对管实时测量药液滴速、湿度传感器检测药液瓶中的剩余药液量、步进电机配合传动装置控制液滴速度,医护人员可以精准控制药滴滴速,当检测到液位过低或者滴速过快时,会产生声光报警提示医护人员注意,同时如果患者发现身体有什么不适,也可以手动触发报警器。
二、系统硬件设计
系统硬件的整体框图如下:
本设计主体上共分为七个部分,即stm32核心模块模块、电机驱动模块、LCD显示模块、注射液滴速检测模块、湿度传感器模块、声光报警模块及按键控制模块。通过stm32,协调配合另外六个部分,共同完成智能输液系统的完整功能。
1.stm32f103核心控制器
stm32f103c8t6的实物图如下:
其原理图如下:
stm32f103c8t6为意法半导体生产的一款高性能32位处理器,采用ARM cortex-M3为内核,在稳定运行的情况下,主频可高达72M,是传统51单片机的性能的几十倍,能完成许多复杂的功能。其最小系统主要包括:stm32芯片、复位电路、时钟电路、电源电路、代码烧录电路和boot选择电路。
stm32f103c8t6用着丰富的外设,例如GPIO、USART、ADC、PWM、TIMER、硬件SPI、硬件IIC、USB等。在本设计中,将会使用到的它的GPIO、ADC、EXTI、TIME等。其中,GPIO需要控制声光报警模块、电机驱动模块、LCD显示模块;湿度液位检测通过模数转换(ADC)进行检测;滴速通过外部中断计数,配合TIME,计算出液滴滴速。
2.电机驱动模块
电机驱动模块电路如下所示:
在本设计中,采用一个五线四相5V步进电机控制液滴滴速。驱动电机需要较大电流,无法直接通过GPIO进行控制,因此,需要在GPIO和电机之间增加一个驱动电路,增加GPIO的驱动能力,才可以驱动电机转动,这里选择的是ULN2003.
ULN2003是大电流驱动阵列,多用于单片机、智能仪表、PLC、数字量输出卡等控制电路中。可直接驱动继电器等负载。输入5VTTL电平,输出可达500mA/50V。其内部由七个硅NPN达林顿管组成。 该电路的特点如下: ULN2003的每一对达林顿都串联一个2.7K的基极电阻,在5V的工作电压下它能与TTL和CMOS电路 直接相连,可以直接处理原先需要标准逻辑缓冲器来处理的数据。
3.LCD显示屏模块
本设计采用LCD1602作为人机交互的界面,在显示屏上,将显示当前液滴滴速以及药液瓶中药液剩余情况的显示。其原理图如下:
LCD1602可以显示两行共32个ASCII字符,在上图中,RP为一个电位器,通过调节LCD PIN1和PIN3之间的电阻,可以起到调节LCD背光的效果。RS、RW和EN为LCD的控制线,其中RS为数据/指令控制、RW为读写控制、EN为使能控制,D0~D7为数据口,LCD的控制数据通过这八根线进行传输。其价格低廉,且易于控制。
4.注射液滴速检测模块
注射液滴速检测模块实物图如下:
模块的原理图如下:
LM393是一个运算放大器,起着电压比较器的作用。当槽型光耦之间没有遮挡物时,红外接收管导通,LM393的正向输入端拉到地,此时正向输入端的电压低于反向输入端的电压,D0输出一个低电平,此时LED是亮的。同理,当槽形光耦之间有遮挡物时,LM393的正向输入端与地的电阻非常大,此时改点电位接近VCC,比反向输入端电压高,D0输出高电平,LED是灭的。
其与stm32之间的接口如下:
在PCB上,提供两个接口,一个接口可以直接将传感器插入,另一个接口可以用延长线进行连接,方便接线。模组3.3V供电,其DO引脚接到stm32的PB3上,当有液滴滴过,D0会产生一个上升沿,stm32检测到上升沿后,产生一个外部中断,计数加1,再配合定时器,计算单位时间内的液滴数就可以计算出药液滴速了。
5.湿度检测模块
湿度检测模块的实物图如下:
该模块的原理图和注射液滴速检测模块基本一致,该模块增加了一个电位器,可以调节传感器的灵敏度。其与stm32之间的接口如下:
该模块为5V供电,其AO引脚,接入stm32的PB0,PB0可以复用为外设ADC,外界湿度发生变化怎传感器的阻值产生变化,通过一定的电路转换,可以将这个变化转换成电压的变化,进而被ADC检测采集,以此可以判断药液的剩余量。
6.声光报警模块
声光报警模块原理图如下所示:
上图左边为LED控制电路,右边为蜂鸣器驱动控制电路。LED的控制原理图很简单,LED的负极接stm32的PB5/6/7,当这些GPIO输出低电平时,LED亮,反之,LED则灭。 蜂鸣器通过一个PNP三极管驱动,当PB12输出一个低电平时,蜂鸣器的正极接到3.3V,蜂鸣器响,反之,蜂鸣器则保持安静。
7.按键控制模块
按键用来手动触发报警和调节滴速。用户可以通过按键设置滴速阈值,当系统检测到滴速大于阈值时,会设置电机反转来模拟压缩滴管,减小滴速,反之,则正转电机来模拟放松滴管,增大滴速。其原理图如下:
按键一端连接stm32的GPIO,另外一段连接GND,当有按键按下时,连接stm32的GPIO检测到有低电平输入,判断为按键按下,然后就可以执行对应的操作,如报警、增大或者减少设定阈值等。
三、系统软件设计
首先需要对stm32外设进行初始化,包括GPIO、TIME、EXTI和ADC的初始化,然后对LCD初始化。初始化完成之后,就可以进行主要业务逻辑的实现了,具体实现逻辑如下图:
下面将从四点对软件进行详细介绍。
1.声光报警模块及电机驱动软件设计
在使用蜂鸣器、LED和电机之前,需要将其对应的GPIO进行初始化,将这些GPIO全部设置成推挽输出即可,如下:
void LED_BEEP_MOTOR_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE); //使能PA,PB端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
然后为了方便,针对这些外设,定义对应的宏,如下:
#define LED0 PBout(5)
#define LED1 PBout(6)
#define LED2 PBout(7)
#define BEEP PBout(12)
#define motor1 PBout(8)
#define motor2 PBout(9)
#define motor3 PAout(12)
#define motor4 PAout(15)
接下来就可以直接对这些GPIO的高低电平输出做出控制了,比如,昂LED0亮起来,可以这样:
LED0 = 0;
其余控制类似。
五线四相步进电机的控制原理有需要自行查阅资料,这里只给出驱动代码:
//正转
void F_Rotation(u16 x)
{
u8 j;
for(;x>0;x--)
{
switch(j)
{
case 0:motor1 = 1;motor2 = 0;motor3 = 0;motor4 = 0;break;
case 1:motor1 = 0;motor2 = 1;motor3 = 0;motor4 = 0;break;
case 2:motor1 = 0;motor2 = 0;motor3 = 1;motor4 = 0;break;
case 3:motor1 = 0;motor2 = 0;motor3 = 0;motor4 = 1;break;
}
j++;
if(j>=4) j=0;
delay_ms(2);
}
}
//反转
void B_Rotation(u16 x)
{
u8 j;
for(;x>0;x--)
{
switch(j)
{
case 0:motor1 = 0;motor2 = 0;motor3 = 0;motor4 = 1;break;
case 1:motor1 = 0;motor2 = 0;motor3 = 1;motor4 = 0;break;
case 2:motor1 = 0;motor2 = 1;motor3 = 0;motor4 = 0;break;
case 3:motor1 = 1;motor2 = 0;motor3 = 0;motor4 = 0;break;
}
j++;
if(j>=4) j=0;
delay_ms(2);
}
}
2.LCD显示屏软件设计
同样,在使用LCD显示之前,需要将对应的GPIO进行初始化,如下:
void LCD1602_GPIOInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
然后就需要根据LCD驱动的时许,编写对应的驱动代码,包括写指令和写数据,如下:
void LCD1602_Write_Cmd(u8 cmd)
{
LCD1602_RS=0;
LCD1602_RW=0;
LCD1602_EN=0;
GPIO_WriteLow(GPIOA,cmd);
delay_us(5);
LCD1602_EN=1;
delay_ms(2);
LCD1602_EN=0;
}
void LCD1602_Write_Dat(u8 dat)
{
LCD1602_RS=1;
LCD1602_RW=0;
LCD1602_EN=0;
GPIO_WriteLow(GPIOA,dat);
delay_us(5);
LCD1602_EN=1;
delay_ms(2);
LCD1602_EN=0;
}
接下来,就需要根据显示屏的寄存器,对LCD进行初始化了:
void LCD1602_Init()
{
LCD1602_GPIOInit();
//初始化LCD1602
LCD1602_Write_Cmd(0x38);//设置8位格式,2行,5x7
LCD1602_Write_Cmd(0x0c);//整体显示,关光标,不闪烁
LCD1602_Write_Cmd(0x06);//设定输入方式,文字不动,地址自动+1
LCD1602_Write_Cmd(0x01);//清屏
delay_ms(5);
}
最后就是控制LCD在什么位置显示什么内容:
void LCD1602_Set_Cursor(u8 x, u8 y)
{
if(y==0)
LCD1602_Write_Cmd(0x80+x);
else
LCD1602_Write_Cmd(0x80+0x40+x);;
}
void LCD1602_Show_Str(u8 x,u8 y,u8 *str)
{
if(str != NULL)
{
LCD1602_Set_Cursor(x, y);
while(*str!='\0')
{
LCD1602_Write_Dat(*str++);
}
}
}
例如,我想在第一行第一个显示“LHSMD”这一串字符,代码可以这么写:
LCD1602_Show_Str(0,0,"LHSMD\0");
3.注射液滴速检测模块软件设计
液滴滴速的检测,需要使用到stm32的外部中断以及定时器。这里外部中断使用的时外部中断线3,定时器使用的定时器3,定时时间为1s。
外部中断初始化及中断服务函数如下:
uint16_t drop_count = 0;
//外部中断初始化函数
void EXTIX_Init(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//外部中断,需要使能AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能PORTB,PORTE时钟
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable , ENABLE); /* PB3作为普通GPIO使用 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //设置成下拉输入
GPIO_Init(GPIOB ,&GPIO_InitStructure);//初始化
//GPIOC.5 中断线以及中断初始化配置
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource3);
EXTI_InitStructure.EXTI_Line=EXTI_Line3;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn; //使能按键所在的外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子优先级1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
}
void EXTI3_IRQHandler(void)
{
if(COUNT_OUT==1)
{
drop_count++;
}
EXTI_ClearITPendingBit(EXTI_Line3); //清除EXTI3线路挂起位
}
其中drop_count为液滴滴下数量。
定时器3的初始化及中断服务函数如下:
uint16_t drop_count_speed = 0;
//通用定时器中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
void TIM3_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_ITConfig( //使能或者失能指定的TIM中断
TIM3, //TIM3
TIM_IT_Update ,
ENABLE //使能
);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIMx外设
}
void TIM3_IRQHandler(void)
{
static uint16_t last_drop_count = 0;
if(TIM_GetITStatus(TIM3,TIM_IT_Update) != RESET)
{
drop_count_speed = drop_count-last_drop_count;
last_drop_count = drop_count;
}
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
}
这里的drop_count_speed就是液滴每秒的滴下的数量,也就是滴速,单位滴/s。
4.湿度检测模块软件设计
湿度的采用ADC来检测。
stm32的ADC是12位ADC,stm32f103c8t6一共有三个ADC,每个ADC最多有18个通道,其最大转换速率可达到1Mhz。本设计中使用的ADC1的通道八进行音频音调高低信号的采集。在初始化任何外设之前,都需要先初始化其对应的外设时钟,然后再设置对应的ADC通道、设置ADC采样速率转换方式等。其具体代码如下:
void Adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |RCC_APB2Periph_ADC1 , ENABLE ); //使能ADC1通道时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
//PB0 作为模拟通道输入引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
GPIO_Init(GPIOB, &GPIO_InitStructure);
ADC_DeInit(ADC1); //复位ADC1
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式
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的寄存器
ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
ADC_ResetCalibration(ADC1); //使能复位校准
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1); //开启AD校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
}
初始化完成之后,在后续的代码中就可以使用PB0开始测量工作了。为了避免信号的干扰,使测量的数据更加准确,这里采用多次测量取平均值的方式。具体代码如下:
u16 Get_Adc(u8 ch)
{
//设置指定ADC的规则组通道,一个序列,采样时间
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5周期
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
}
u16 Get_Adc_Average(u8 ch,u8 times)
{
u32 temp_val=0;
u8 t;
for(t=0;t<times;t++)
{
temp_val+=Get_Adc(ch);
delay_ms(5);
}
return temp_val/times;
}
比如,我现在想测量PB0这个通道的ADC值20次,代码可以这么写:
adc_value = Get_Adc_Average(ADC_Channel_8, 20);
四、实物展示
五、完整原理图
原理图如下:
PCB图如下:文章来源:https://www.toymoban.com/news/detail-794509.html
文章来源地址https://www.toymoban.com/news/detail-794509.html
六、完整代码
到了这里,关于基于stm32的智能输液系统设计的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!