传统的病房呼叫系统往往采用有线连接,安装复杂,维护成本高昂。在无线技术日益成熟的今天,基于单片机的病房呼叫系统设计提供了一种低成本、易部署的解决方案。本文将深入探讨如何利用单片机实现一套稳定可靠的无线呼叫系统,并分享实际项目中的经验与教训。
系统架构设计:硬件选型与软件框架
硬件选型:兼顾性能与成本
- 主控芯片:STM32F103C8T6 (小蓝板):STM32F103C8T6 是一款基于 ARM Cortex-M3 内核的 32 位单片机,具有成本低廉、资源丰富的特点。它提供了足够的 GPIO 接口、定时器、UART 等外设,可以满足呼叫系统的需求。
- 无线通信模块:NRF24L01:NRF24L01 是一款 2.4GHz 无线收发模块,具有传输距离远、功耗低、成本低的优势。它采用 SPI 接口与单片机通信,易于集成。
- 显示模块:OLED 屏幕:OLED 屏幕具有对比度高、视角广、功耗低的特点,可以用于显示病床号、呼叫状态等信息。
- 按键:用于病人触发呼叫。
- 电源:采用稳压电源供电,确保系统稳定运行。
软件框架设计:模块化与事件驱动
软件框架采用模块化设计,将系统划分为以下几个模块:
- 按键检测模块:负责检测按键状态,触发呼叫事件。
- 无线通信模块:负责数据的发送和接收,采用 NRF24L01 库进行驱动。
- 显示模块:负责在 OLED 屏幕上显示信息,采用 SSD1306 驱动。
- 事件处理模块:负责处理各种事件,例如按键按下、收到呼叫请求等。
- 主循环:负责轮询各个模块,处理事件。
核心代码实现:基于 STM32 和 NRF24L01
按键检测代码
// 按键引脚定义
#define KEY_PIN GPIO_Pin_0
#define KEY_PORT GPIOA
// 按键检测函数
uint8_t Key_Scan(void)
{
if(GPIO_ReadInputDataBit(KEY_PORT, KEY_PIN) == RESET)
{
delay_ms(10); // 软件消抖
if(GPIO_ReadInputDataBit(KEY_PORT, KEY_PIN) == RESET)
{
while(GPIO_ReadInputDataBit(KEY_PORT, KEY_PIN) == RESET); // 等待按键释放
return 1; // 按键按下
}
}
return 0; // 没有按键按下
}
NRF24L01 通信代码
// NRF24L01 初始化
void NRF24L01_Init(void)
{
// 初始化 SPI 接口
SPI_InitTypeDef SPI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; // SCK, MISO, MOSI
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; // CE
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; // CSN
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //降低波特率,提高可靠性
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
SPI_Cmd(SPI1, ENABLE);
// 其他初始化代码...
}
// 发送数据
void NRF24L01_TxPacket(uint8_t *tx_buf)
{
// 设置为发送模式
NRF24L01_Write_Reg(WRITE_REG + CONFIG, 0x0E); // Enable CRC, 16-bit CRC, Power up, PTX
// 写入数据
NRF24L01_Write_Buf(WR_TX_PLOAD, tx_buf, TX_PLOAD_WIDTH);
// 启动发送
CE_HIGH;
delay_us(10); // 至少 10us
CE_LOW;
// 等待发送完成(中断或查询)
}
OLED 显示代码
// OLED 初始化
void OLED_Init(void)
{
// 初始化 I2C 接口
// 其他初始化代码...
OLED_WR_Byte(0xAE,OLED_CMD); //--display off
OLED_WR_Byte(0x00,OLED_CMD); //---set low column address
OLED_WR_Byte(0x10,OLED_CMD); //---set high column address
OLED_WR_Byte(0x40,OLED_CMD); //--set start line address Set Mapping RAM Display Start Line (0x00~0x3F)
OLED_WR_Byte(0xB0,OLED_CMD); //--set page address. Set PAGE Address for REDRAWING (0x00~0x07)
OLED_WR_Byte(0x81,OLED_CMD); //--set contrast control register
OLED_WR_Byte(0xCF,OLED_CMD); // Set SEG Output Current Brightness
OLED_WR_Byte(0xA1,OLED_CMD); //--Set SEG/Column Re-map Default =>0000 0000
OLED_WR_Byte(0xA6,OLED_CMD); //--Set COM/Row Scan Direction Default =>0000 0000
OLED_WR_Byte(0xA8,OLED_CMD); //--set multiplex ratio(1 to 64)
OLED_WR_Byte(0x3F,OLED_CMD); //--1/64 duty
OLED_WR_Byte(0xC8,OLED_CMD); //Com scan direction
OLED_WR_Byte(0xD3,OLED_CMD); //-set display offset Shift Mapping RAM Counter (0x00~0x3F)
OLED_WR_Byte(0x00,OLED_CMD); //-not offset
OLED_WR_Byte(0xD5,OLED_CMD); //--set osc division ratio
OLED_WR_Byte(0x80,OLED_CMD); //
OLED_WR_Byte(0xD9,OLED_CMD); //--set pre-charge period
OLED_WR_Byte(0xF1,OLED_CMD); //
OLED_WR_Byte(0xDA,OLED_CMD); //--set com pins hardware configuartion
OLED_WR_Byte(0x12,OLED_CMD);
OLED_WR_Byte(0xDB,OLED_CMD); //--set vcomh
OLED_WR_Byte(0x40,OLED_CMD); //Set SEG Output Current Brightness
OLED_WR_Byte(0x20,OLED_CMD); //-Set Page Addressing Mode (0x00/0x01/0x02)
OLED_WR_Byte(0x02,OLED_CMD); //
OLED_WR_Byte(0x8D,OLED_CMD); //--enable charge pump
OLED_WR_Byte(0x14,OLED_CMD); //--set(0x10) disable
OLED_WR_Byte(0xA4,OLED_CMD); // Disable Entire Display On (0xa4/0xa5) //disable all pixels to ON
OLED_WR_Byte(0xA6,OLED_CMD); // Disable Inverse Display Mode //normal display
OLED_WR_Byte(0xAF,OLED_CMD); //--turn on oled panel
}
// 显示字符串
void OLED_ShowString(uint8_t x,uint8_t y,uint8_t *chr,uint8_t Char_Size)
{
unsigned char j=0;
while (chr[j]!='\0')
{
OLED_ShowChar(x,y,chr[j],Char_Size);
x+=8;
if(x>120){x=0;y+=2;}
j++;
}
}
实战避坑经验:从原型到稳定运行
- 电源稳定性:单片机系统对电源质量要求较高,建议使用稳压电源,并增加滤波电容,防止电源波动导致系统不稳定。
- 无线干扰:2.4GHz 频段容易受到 Wi-Fi、蓝牙等设备的干扰,可以通过调整 NRF24L01 的信道和发射功率来降低干扰。
- 通信距离:NRF24L01 的通信距离受环境影响较大,可以在空旷区域进行测试,并适当调整天线方向,以获得最佳通信效果。
- 软件设计:采用模块化设计,提高代码的可读性和可维护性。使用事件驱动机制,提高系统的响应速度。
- 错误处理:在软件中加入错误处理机制,例如检测无线通信是否成功,按键是否有效等,可以提高系统的鲁棒性。 如果呼叫系统需要连接到服务器,可以考虑使用MQTT协议,并使用像阿里云物联网平台这样的服务。服务器端可以考虑使用Java SpringBoot框架来搭建,配合MySQL数据库存储呼叫记录。另外,为了防止恶意呼叫,可以在服务器端增加IP限流策略,可以使用像Redis这样的缓存数据库来实现。同时,可以考虑使用Nginx作为反向代理服务器,提供负载均衡,提高系统的并发连接数。
未来展望
随着物联网技术的不断发展,基于单片机的病房呼叫系统将朝着智能化、网络化的方向发展。例如,可以加入语音交互功能,方便病人进行呼叫。还可以将呼叫系统与医院的信息系统进行集成,实现更加便捷的医疗服务。
冠军资讯
代码一只喵