转自嵌入式ARM
学号16020311003 姓名:杨虎成
【嵌牛导读】多尝试自己动手用嵌入式解决问题
【嵌牛鼻子】嵌入式在大部分电气中有很大的作用
【嵌牛提问】嵌入式系统在电子元件中如何工作
【嵌牛正文】
智能插座远程控制电器很好玩!如果你有嵌入式单片机基础,你也可以自己动手做一个...
——————————————————
自己DIY用的零件清单(大部分都是手头拆机的):
1)STC51系列单片机(淘宝上几块钱就能买到,自己纯手工焊了一块板子)
2)中央空调控制面板上拆的继电器,有四路,刚好可以控制四个插座
3)蓝牙POS机(淘宝上一毛钱包邮很多,我这款叫“开店宝”),用于做蓝牙串口透传
4)废旧插座一个
由于普通插座都是共火线和共零线,为了实现四路单独控制,需要改造插座,将四个插孔的火线分开,然后与四路继电器相连,
整个控制流:手机APP –> 蓝牙POS –> 51单片机 –> 继电器 –> 插座
手机APP通过蓝牙发送数据到蓝牙POS,蓝牙POS作为一个蓝牙透传设备,将控制指令数据通过串口发送给单片机,单片机通过GPIO口来控制继电器,最终实现对插座的控制。(其实该蓝牙POS是个STM32单片机,可以无需51单片机,直接将STM32的GPIO引出来去控制,如果你动手能力强的话,鉴于自己动手能力一般,焊接不好那么细的芯片引脚,所以干脆拿了个51单片机来中转一下)。
下面奉上代码:
插座的通断是通过单片机来控制,结合蓝牙POS机做透传,即可直接通过蓝牙来控制继电器。
51单片机代码比较简单,放出完整代码。
#include <stc89c5xrc.h>
#include <string.h>
#define FOSC 22118400L //定义晶振频率
#define BAUD 115200 //定义波特率
#define SMOD 1
#define RX_BUFF_SIZE 64
#define ERR_CODE_RSP 0xD0 //错误码
/*定义四路开关控制引脚*/
sbit PLUG_1 = P2^4 ;
sbit PLUG_2 = P2^7 ;
sbit PLUG_3 = P2^6 ;
sbit PLUG_4 = P2^5 ;
unsigned char rx_buff[RX_BUFF_SIZE] = {0}; //串口接收缓冲区
volatile unsigned char rx_count = 0; //接收计数器
volatile unsigned char pos = 0; //读取计数器,记录当前读取字节数
volatile unsigned char msg_code = 0x00; //存储控制码
volatile unsigned char status_code = 0x70; //存储状态码
/** 串口发送 **/
void byte_send(unsigned char ch)
{
SBUF = ch ;
while(!TI);
TI = 0;
}
/** 判断缓存是否有数据可读取 **/
unsigned char is_rx_buffer_empty(void)
{
return !(rx_count - pos);
}
/** 读缓冲区一个字节 **/
unsigned char uart_read(void)
{
unsigned char c;
c = rx_buff[pos++];
if(rx_count == pos){
rx_count = pos = 0;
}
return c;
}
/** 消息处理 **/
/** 简单起见,就用了一个字节做控制码
bit0 ~ bit3: 分别对应四路开关,置0为关,置为1为开
bit4 ~ bit8: 作为参数类型(0x06:查询开关状态 0x07:设置开关状态)
**/
void msg_process(void)
{
if(is_rx_buffer_empty()) //如果设计成多个字节作为控制码,此处要改为while循环
return;
msg_code = uart_read();
switch((msg_code & 0xF0) >> 4)
{
case 0x6: //Get the status
msg_reply(status_code);
break;
case 0x7: //Set and get status
PLUG_1 = msg_code & 0x01 ;
PLUG_2 = (msg_code & 0x02) >> 1 ;
PLUG_3 = (msg_code & 0x04) >> 2 ;
PLUG_4 = (msg_code & 0x08) >> 3 ;
status_code = msg_code; //保存本次处理结果
msg_reply(status_code);
break;
default: //Err code
msg_reply(ERR_CODE_RSP);
}
}
/** 消息响应 **/
void msg_reply(unsigned char msg)
{
//byte_send(':');//unknow reason
delay(1500);
byte_send(msg);
byte_send(':'); //将':'作为间隔符,蓝牙POS收到该符号,才认为一条数据接收完毕
}
/** 串口初始化 **/
void uart_init(void)
{
SCON = 0x50 ;
TMOD = 0x20 ;
PCON |= 0x80 ; //set smod
TH1 = TL1 = 256 - FOSC*(SMOD+1)/32/12/BAUD ;
TR1 = 1;
ES = 1 ;
EA = 1 ;
}
/** 串口中断处理 **/
void serial() interrupt 4 using 1
{
if(RI)
{
RI = 0;
if(rx_count > RX_BUFF_SIZE)
rx_count = 0;
rx_buff[rx_count++] = SBUF ;
}
}
/** 主函数 **/
void main(void)
{
P2 = 0x00;
uart_init();
while(1)
{
msg_process();
}
}
消息处理函数,一定要放在主循环中,有的开发者习惯在中断响应函数中做消息处理,容易导致串口数据丢失。中断中一定只做简单的处理,本代码中只是将中断接收到的数据存到数组中。
测试:
假设当前开关状态为: 0101
用串口助手输入16进制:
输入:0x60 返回:0x75 0x3A //查询当前开关状态,0x3A为’:’对应16进制ASCII码
输入:0x77 返回:0x77 0x3A //设置开关0、开关1、开关2 开,开关3关闭
操作成功后,会听见继电器开启、闭合的声音,可以用万用表测量是否导通或者切断。