目录
串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方 式的扩展接口。串行接口(Serial Interface)是指数据一位一位地顺序传送。其特点是通信线路简 单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成 本,特别适用于远距离通信,但传送速度较慢
通信允许数据在两个方向上同时传输,它在能力上相当于两个单工通信方式的结合。全双工指可以同时(瞬时)进行信号的双向传输(A→B且B→A)。指A→B的同时B→A,是瞬时同步的。通信允许数据在两个方向上同时传输,它在能力上相当于两个单工通信方式的结合。全双工指可以同时(瞬时)进行信号的双向传输(A→B且B→A)。指A→B的同时B→A,是瞬时同步的。
TTL是Transistor-Transistor Logic,即晶体管-晶体管逻辑的简称,它是计算机处理器控制的设备 内部各部分之间通信的标准技术。TTL电平信号应用广泛,是因为其数据表示采用二进制规定, +5V等价于逻辑”1”,0V等价于逻辑”0”。
字符帧也叫数据帧,一帧数据由起始位、数据位、奇偶校验位和停止位组成,对于异步通信,要能够进行数据的正确传送,通信双方必须先约定好传送数据的速率和传送数据的组织格式,即波特率和字符帧的格式。


总之,使用UART串口协议传输数据时,需要规定双方的传输速率(波特率)一致,数据格式(起始位,数据位,奇偶校验为、停止位)也要保持一致。例如传递字符a时,字符a的一帧信息由起始位、数据位(字符a)、奇偶校验位(可选)、停止位组成。

串口数据缓冲寄存器(SUBF寄存器):
电源控制寄存器PCON

串行控制寄存器SCON




IE寄存器

IP寄存器、IPH寄存器

工作模式1:


工作模式2:


工作模式3:


PC机串口调试助手(STC-ISP)发送字符o时让51单片机的蜂鸣器叫,发送字符c时让51单片机的蜂鸣器停止叫。
#include "reg52.h"
#include <intrins.h>
sfr AUXR = 0x8E;
sbit buzzer = P1^2;
char cmd;
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void uartInit()
{
AUXR = 0x01;
PCON &= 0x7F; //配置波特率正常
SCON = 0x50; //配置串口选择工作方式1,允许串口接收数据
//配置定时器1为8位自动重装模式
TMOD &= 0x0F;
TMOD |= 0x20;
//给定时器1,9600波特率初值
TH1 = 0xFD; //定时器1初值
TL1 = 0xFD; //定时器1重装值
ET1 = 0; //不允许定时器1产生中断
TR1 = 1; //开启定时器1
EA = 1; //开启总中断
ES = 1; //开启串口中断
}
void sendByte(char mydata)
{
SBUF = mydata; //向串口发送一帧信息
while(!TI); //等待硬件置位
TI = 0; //TI软件清0
}
void sendString(char *str)
{
while(*str != '\0'){
sendByte(*str);
str++;
}
}
void main()
{
buzzer = 1;
uartInit();
while(1){
sendString("jiangxiaoya\r\n"); //发送心跳包,确保串口通信没有中断
Delay1000ms();
}
}
void myUart() interrupt 4
{
//接收数据后,RI硬件置位产生的中断
if(RI){
RI = 0; //RI软件清0
cmd = SBUF; //获取从pc端接收到的数据
if(cmd == 'o'){
buzzer = 0;
}
if(cmd == 'c'){
buzzer = 1;
}
}
//接收数据后,TI硬件置位产生的中断
if(TI);
}
PC机串口调试助手(STC-ISP)发送字符串open时让51单片机的蜂鸣器叫,发送字符串close时让51单片机的蜂鸣器停止叫。
#include "reg52.h"
#include <intrins.h>
sfr AUXR = 0x8E;
sbit buzzer = P1^2;
char cmd[12] ;
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void uartInit()
{
AUXR = 0x01;
PCON &= 0x7F; //配置波特率正常
SCON = 0x50; //配置串口选择工作方式1,允许串口接收数据
//配置定时器1为8位自动重装模式
TMOD &= 0x0F;
TMOD |= 0x20;
//给定时器1,9600波特率初值
TH1 = 0xFD; //定时器1初值
TL1 = 0xFD; //定时器1重装值
ET1 = 0; //不允许定时器1产生中断
TR1 = 1; //开启定时器1
EA = 1; //开启总中断
ES = 1; //开启串口中断
}
void sendByte(char mydata)
{
SBUF = mydata; //向串口发送一帧信息
while(!TI); //等待硬件置位
TI = 0; //TI软件清0
}
void sendString(char *str)
{
while(*str != '\0'){
sendByte(*str);
str++;
}
}
void main()
{
buzzer = 1;
uartInit();
while(1){
sendString("jiangxiaoya\r\n"); //发送心跳包,确保串口通信没有中断
Delay1000ms();
}
}
void myUart() interrupt 4
{
static int i = 0;
//接收数据后,RI硬件置位产生的中断
if(RI){
RI = 0; //RI软件清0
//获取从pc端接收到的数据
cmd[i] = SBUF;
i++;
if(i == 12){
i = 0;
}
//strstr():字符串包含函数,不包含返回NULL
if(strstr(cmd,"en") != NULL){
buzzer = 0;
i = 0;
memset(cmd,'\0',12);
}
if(strstr(cmd,"os") != NULL){
buzzer = 1;
i = 0;
memset(cmd,'\0',12);
}
}
//接收数据后,TI硬件置位产生的中断
if(TI);
}
HC-08模块直接接线使用即可,可以使用STC-ISP软件发送AT指令:AT+NAME=名字,来修改蓝牙名字,跟STC-ISP里的串口助手使用方法一样。

wifi模块通过usb转ttl接入pc端,通过安信可串口调试助手修改esp8266的波特率,让它跟单片机的通信速度保持一致。
连接后发送AT指令修改esp8266波特率:

esp8266的上网模式:


上述AT指令对应的响应

用51单片机使用esp8266作为客户端连入服务器
#include "reg52.h"
#include <intrins.h>
#include <string.h>
sfr AUXR = 0x8E;
sbit buzzer = P1^2;
char mybuf[24];
//相关AT指令
char RESET[] = "AT+RST\r\n"; //重启esp8266
code char LJWL[] = "AT+CWJAP=\"haozige\",\"123456789000\"\r\n"; //连接WiFi
code char LJFWQ[] = "AT+CIPSTART=\"TCP\",\"192.168.97.198\",8880\r\n"; //连接TCP服务器
char TCMS[] = "AT+CIPMODE=1\r\n"; //开启透传模式
char SJCS[] = "AT+CIPSEND\r\n"; //开启数据传输
int AT_OK_flag = 0;
int AT_CONNECT_NET_flag = 0;
void Delay500ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 4;
j = 129;
k = 119;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay5000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 36;
j = 5;
k = 211;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void uartInit()
{
AUXR = 0x01;
PCON &= 0x7F; //配置波特率正常
SCON = 0x50; //配置串口选择工作方式1,允许串口接收数据
//配置定时器1为8位自动重装模式
TMOD &= 0x0F;
TMOD |= 0x20;
//给定时器1,9600波特率初值
TH1 = 0xFD; //定时器1初值
TL1 = 0xFD; //定时器1重装值
ET1 = 0; //不允许定时器1产生中断
TR1 = 1; //开启定时器1
EA = 1; //开启总中断
ES = 1; //开启串口中断
}
void sendByte(char mydata)
{
SBUF = mydata; //向串口发送一帧信息
while(!TI); //等待硬件置位
TI = 0; //TI软件清0
}
void sendString(char *str)
{
while(*str != '\0'){
sendByte(*str);
str++;
}
}
void main()
{
int mark = 0;
buzzer = 1;
uartInit();
Delay5000ms(); //等待esp8266上电
sendString(LJWL);
while(!AT_CONNECT_NET_flag); //等待WiFi连接成功
//while(!AT_OK);
AT_OK_flag = 0;
sendString(LJFWQ);
while(!AT_OK_flag); //等待esp8266连接TCP服务器
AT_OK_flag = 0;
sendString(TCMS);
while(!AT_OK_flag); //等待esp8266开启透传模式
AT_OK_flag = 0;
sendString(SJCS);
while(!AT_OK_flag); //等待esp8266与tcp开启数据传输
while(1){
sendString("jiangxiaoya\r\n"); //发送心跳包,确保串口通信没有中断
Delay1000ms();
}
}
void myUart() interrupt 4
{
static int i = 0;
char tmp;
//接收数据后,RI硬件置位产生的中断
if(RI){
RI = 0; //RI软件清0
//获取从pc端接收到的数据
tmp = SBUF;
if(tmp == 'W' || tmp == 'O' || tmp == 'L' || tmp == 'F'){
i = 0;
}
mybuf[i] = tmp;
i++;
//连接wifi成功后,esp8266会返回WIFI GT IP
if(mybuf[0] == 'W' && mybuf[1] == 'I'){
AT_CONNECT_NET_flag = 1;
memset(mybuf,'\0',24);
}
//esp8266连接TCP服务器或者其他AT指令,esp8266会返回OK
if(mybuf[0] == 'O' && mybuf[1] == 'K'){
AT_OK_flag = 1;
memset(mybuf,'\0',24);
}
//TCP服务器发送数据给esp8266的灯控指令
//开
if(mybuf[0] == "L" && mybuf[1] == "1"){
buzzer = 0;
memset(mybuf,'\0',24);
}
//关
if(mybuf[0] == "L" && mybuf[1] == "2"){
buzzer = 1;
memset(mybuf,'\0',24);
}
//WiFi连接失败后,会返回FALL
if(mybuf[0] == 'F' && mybuf[1] == 'A'){
for(i=0;i<5;i++){
buzzer = 0;
Delay500ms();
buzzer = 1;
Delay500ms();
}
sendString(RESET);
memset(mybuf,'\0',24);
}
if(i == 24){
i = 0;
}
}
//接收数据后,TI硬件置位产生的中断
if(TI);
}

上述AT指令对应的响应

用51单片机使用esp8266架设服务器
#include "reg52.h"
#include <intrins.h>
#include <string.h>
sfr AUXR = 0x8E;
sbit buzzer = P1^2;
char mybuf[24];
//相关AT指令
char LYMS[] = "AT+CWMODE=2\r\n"; //配置esp8266为路由模式
char DLLJ[] = "AT+CIPMUX=1\r\n"; //配置esp8266多路连接
char JLFWQ[] = "AT+CIPSERVER=1\r\n"; //建立esp8266服务器
char FSSJ[] = "AT+CIPSEND=0,11\r\n"; //向客户端发送数据
char AT_OK_flag = 0;
char AT_CONNECT_NET_flag = 0;
char Client_Connect_Net_flag = 0;
void Delay500ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 4;
j = 129;
k = 119;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay5000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 36;
j = 5;
k = 211;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void uartInit()
{
AUXR = 0x01;
PCON &= 0x7F; //配置波特率正常
SCON = 0x50; //配置串口选择工作方式1,允许串口接收数据
//配置定时器1为8位自动重装模式
TMOD &= 0x0F;
TMOD |= 0x20;
//给定时器1,9600波特率初值
TH1 = 0xFD; //定时器1初值
TL1 = 0xFD; //定时器1重装值
ET1 = 0; //不允许定时器1产生中断
TR1 = 1; //开启定时器1
EA = 1; //开启总中断
ES = 1; //开启串口中断
}
void sendByte(char mydata)
{
SBUF = mydata; //向串口发送一帧信息
while(!TI); //等待硬件置位
TI = 0; //TI软件清0
}
void sendString(char *str)
{
while(*str != '\0'){
sendByte(*str);
str++;
}
}
void main()
{
int mark = 0;
buzzer = 1;
uartInit();
Delay5000ms(); //等待esp8266上电
sendString(LYMS);
while(!AT_OK_flag); //等待esp8266设置路由模式
AT_OK_flag = 0;
sendString(DLLJ);
while(!AT_OK_flag); //等待esp8266设置多路连接
AT_OK_flag = 0;
sendString(JLFWQ);
while(!Client_Connect_Net_flag); //等待esp8266建立服务器
AT_OK_flag = 0;
while(1){
sendString(FSSJ); //发送心跳包,确保串口通信没有中断
Delay1000ms();
Delay1000ms();
sendString("jiangxiaoya");
Delay1000ms();
Delay1000ms();
}
}
void myUart() interrupt 4
{
static int i = 0;
char tmp;
//接收数据后,RI硬件置位产生的中断
if(RI){
RI = 0; //RI软件清0
//获取从pc端接收到的数据
tmp = SBUF;
if(tmp == 'W' || tmp == 'O' || tmp == 'L' || tmp == '0'){
i = 0;
}
mybuf[i] = tmp;
i++;
//esp8266设置路由模式等其他AT指令,esp8266会返回OK
if(mybuf[0] == 'O' && mybuf[1] == 'K'){
AT_OK_flag = 1;
memset(mybuf,'\0',24);
}
//esp8266建立服务器,esp8266会返回0,CONNECT
if(mybuf[0] == '0' && mybuf[2] == 'C' ){
Client_Connect_Net_flag = 1;
memset(mybuf, '\0' , 24);
}
//esp8266服务器发送数据给客户端的灯控指令
//开
if(mybuf[0] == "L" && mybuf[1] == "1"){
buzzer = 0;
memset(mybuf,'\0',24);
}
//关
if(mybuf[0] == "L" && mybuf[1] == "2"){
buzzer = 1;
memset(mybuf,'\0',24);
}
if(i == 24){
i = 0;
}
}
//接收数据后,TI硬件置位产生的中断
if(TI);
}





4G模块与51单片机进行通信,发送字符串:open让蜂鸣器叫,发送字符串:close让蜂鸣器不叫
#include "reg52.h"
#include <intrins.h>
#include <string.h>
#define SIZE 12
sfr AUXR = 0x8e;
sbit buzzer = P1 ^ 2;
char cmd[SIZE];
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k)
;
} while (--j);
} while (--i);
}
void UartInit(void) // 9600bps@11.0592MHz
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; // 8位数据,可变波特率
AUXR &= 0xBF; //定时器1时钟为Fosc/12,即12T
AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xFD; //设定定时初值
TH1 = 0xFD; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
EA = 1; //允许总中断
ES = 1; //允许串口中断
}
void main()
{
buzzer = 1;
UartInit();
while(1);
}
void Uart_Handler() interrupt 4
{
static int i = 0;
char tmp;
//接收数据后,RI硬件置位产生的中断
if(RI){
RI = 0; //RI软件清0
//获取从pc端接收到的数据
tmp = SBUF;
if(tmp == ':'){
i = 0;
}
cmd[i++] = tmp;
if(i == SIZE){
i = 0;
}
if(cmd[0] == ':' && cmd[1] == 'o' && cmd[2] == 'p'){
buzzer = 0;
i = 0;
memset(cmd, '\0', SIZE);
}
if(cmd[0] == ':' && cmd[1] == 'c' && cmd[2] == 'l'){
buzzer = 1;
i = 0;
memset(cmd, '\0', SIZE);
}
}
if(TI);
}
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah
我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or
我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c
我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的
我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co
文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,
说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时