#循循渐进学51单片机#UART串口通信#not.10

1、能够理解UART串口通信的基本原理和通信过程。

1)串行通信的初步认识

并行通信:通信时数据的各个位同时传送,可以实现字节为单位通信,但是通信线占用资源太多,成本高。

串行通信:一次只能发送一位,要发送8次才能发送一个字节。


2、通过IO口模拟UART串口通信把通信的底层操作原理弄明白。

首先是对通信的波特率的设定,在这里我们配置的波特率是9600,那么串口调试助手也得是9600。配置波特率的时候,我们用的是定时器T0的模式2。模式2中,不再是TH0代表高8位,TL0代表低8位了,而只有TL0在进行计数,当TL0溢出后,不仅仅会让TF0变1,而且还会将TH0中的内容重新自动装到TL0中。这样有一个好处,就是我们可以把想要的定时器初值提前存在TH0中,当TL0溢出后,TH0自动把初值就重新送入TL0了,全自动的,不需要程序中再给TL0重新赋值了,配置方式很简单,大家可以自己看下程序并且计算一下初值。

      波特率设置好以后,打开中断,然后等待接收串口调试助手下发的数据。接收数据的时候,首先要进行低电平检测while (PIN_RXD),若没有低电平则说明没有数据,一旦检测到低电平,就进入启动接收函数StartRXD()。接收函数最开始启动半个波特率周期,初学可能这里不是很明白。大家回头看一下,如果在数据位电平变化的时候去读取,因为时序上的误差以及信号稳定性的问题很容易读错数据,所以我们希望在信号最稳定的时候去读数据。除了信号变化的那个沿的位置外,其它位置都很稳定,那么我们现在就约定在信号中间位置去读取电平状态,这样能够保证我们读的一定是正确的。

      一旦读到了起始信号,我们就把当前状态设定成接收状态,并且打开定时器中断,第一次是半个周期进入中断后,对起始位进行二次判断一下,确认一下起始位是低电平,而不是一个干扰信号。以后每经过1/9600秒进入一次中断,并且把这个引脚的状态读到RxdBuf里边。等待接收完毕之后,我们再把这个RxdBuf加1,再通过TXD引脚发送出去,同样需要先发一位起始位,然后发8个数据位,再发结束位,发送完毕后,程序运行到while (PIN_RXD),等待第二轮信号接收的开始。

#include <reg52.h>sbit PIN_RXD = P3^0;  //½ÓÊÕÒý½Å¶¨Òå
sbit PIN_TXD = P3^1;  //·¢ËÍÒý½Å¶¨Òåbit RxdOrTxd = 0;  //ָʾµ±Ç°×´Ì¬Îª½ÓÊÕ»¹ÊÇ·¢ËÍ
bit RxdEnd = 0;    //½ÓÊÕ½áÊø±êÖ¾
bit TxdEnd = 0;    //·¢ËͽáÊø±êÖ¾
unsigned char RxdBuf = 0;  //½ÓÊÕ»º³åÆ÷
unsigned char TxdBuf = 0;  //·¢ËÍ»º³åÆ÷void ConfigUART(unsigned int baud);
void StartTXD(unsigned char dat);
void StartRXD();void main()
{EA = 1;   //¿ª×ÜÖжÏConfigUART(9600);  //ÅäÖò¨ÌØÂÊΪ9600while (1){while (PIN_RXD);    //µÈ´ý½ÓÊÕÒý½Å³öÏֵ͵çƽ£¬¼´ÆðʼλStartRXD();         //Æô¶¯½ÓÊÕwhile (!RxdEnd);    //µÈ´ý½ÓÊÕÍê³ÉStartTXD(RxdBuf+1); //½ÓÊÕµ½µÄÊý¾Ý+1ºó£¬·¢ËÍ»ØÈ¥while (!TxdEnd);    //µÈ´ý·¢ËÍÍê³É}
}
void ConfigUART(unsigned int baud)
{TMOD &= 0XF0;TMOD |= 0X02;TH0 = 256 -(11059200/12)/baud;
}
void   StartRXD()
{TL0 = 256 - ((256-TH0)>>1);ET0 = 1;TR0 = 1;RxdEnd = 0;RxdOrTxd = 0;
}
void StartTXD(unsigned char dat)
{RxdBuf = dat;TL0 = TH0;ET0 = 1;TR0 = 1;PIN_RXD = 0;TxdEnd = 0;RxdOrTxd = 1;
}
void InterruptTimer0() interrupt 1
{static unsigned char cnt = 0;if(RxdOrTxd){cnt++;if(cnt <= 8){PIN_TXD = TxdBuf & 0x01;TxdBuf >>= 1;}else if(cnt ==9){PIN_TXD = 1;}else{cnt = 0;TR0 = 1;TxdEnd = 1;			}}else{if(cnt == 0){if(!PIN_RXD){RxdEnd = 0;cnt++;}else{TR0 = 0;}}else if(cnt <= 8){RxdBuf >>= 1;if(PIN_RXD){RxdBuf |= 0x80;}cnt++;}else{cnt = 0;TR0 = 0;       if (PIN_RXD)    {RxdEnd = 1; }}}
}

3、学会通过配置寄存器,实现串口通信的基本操作过程。

UART模块介绍

      IO口模拟串口通信,让大家了解了串口通信的本质,但是我们的单片机程序却需要不停的检测扫描单片机IO口收到的数据,大量占用了单片机的运行时间。这时候就会有聪明人想了,其实我们并不是很关心通信的过程,我们只需要一个通信的结果,最终得到接收到的数据就行了。这样我们可以在单片机内部做一个硬件模块,让它自动接收数据,接收完了,通知我们一下就可以了,我们的51单片机内部就存在这样一个UART模块,要正确使用它,当然还得先把对应的特殊功能寄存器配置好。

      51单片机的UART串口的结构由串行口控制寄存器SCON、发送和接收电路三部分构成,先来了解一下串口控制寄存器SCON。如表11-1表11-2所示。

                                      SCON——串行控制寄存器的位分配(地址0x98、可位寻址)

7

6

5

4

3

2

1

0

符号

SM0

SM1

SM2

REN

TB8

RB8

TI

RI

复位值

0

0

0

0

0

0

0

0

    

                                                   SCON——串行控制寄存器的位描述

符号

描述

7

SM0

这两位共同决定了串口通信的模式0~模式3共4种模式。我们最常用的就是模式1,也就是SM0=0,SM1=1,下边我们重点就讲模式1,其它模式从略。

6

SM1

5

SM2

多机通信控制位(极少用),模式1直接清零。

4

REN

使能串行接收。由软件置位使能接收,软件清零则禁止接收。

3

TB8

模式2和3中要发送的第9位数据(很少用)。

2

RB8

模式2和3中接收到的第9位数据(很少用),模式1用来接收停止位。

1

TI

发送中断标志位,当发送电路发送到停止位的中间位置时,TI由硬件置1,必须通过软件清零。

0

RI

接收中断标志位,当接收电路接收到停止位的中间位置时,RI由硬件置1,必须通过软件清零。

  

      前边学了那么多寄存器的配置,相信SCON这个地方,对于大多数同学来说已经不是难点了,应该能看懂并且可以自己配置了。对于串口的四种模式,模式1是最常用的,就是我们前边提到的1位起始位,8位数据位和1位停止位。下面我们就详细介绍模式1的工作细节和使用方法,至于其它3种模式与此也是大同小异,真正遇到需要使用的时候大家再去查阅相关资料就行了。

      在我们使用IO口模拟串口通信的时候,串口的波特率是使用定时器T0的中断体现出来的。在硬件串口模块中,有一个专门的波特率发生器用来控制发送和接收数据的速度。对于STC89C52单片机来讲,这个波特率发生器只能由定时器T1或定时器T2产生,而不能由定时器T0产生,这和我们模拟的通信是完全不同的概念。

      如果用定时器2,需要配置额外的寄存器,默认是使用定时器1的,我们本章内容主要就使用定时器T1作为波特率发生器来讲解,方式1下的波特率发生器必须使用定时器T1的模式2,也就是自动重装载模式,定时器的重载值计算公式为:

      TH1 = TL1 = 256 - 晶振值/12 /2/16 /波特率 

     和波特率有关的还有一个寄存器,是一个电源管理寄存器PCON,他的最高位可以把波特率提高一倍,也就是如果写PCON |= 0x80以后,计算公式就成了:

    TH1 = TL1 = 256 - 晶振值/12 /16 /波特率

      公式中数字的含义这里解释一下,256是8位定时器的溢出值,也就是TL1的溢出值,晶振值在我们的开发板上就是11059200,12是说1个机器周期等于12个时钟周期,值得关注的是这个16,我们来重点说明。在IO口模拟串口通信接收数据的时候,采集的是这一位数据的中间位置,而实际上串口模块比我们模拟的要复杂和精确一些。他采取的方式是把一位信号采集16次,其中第7、8、9次取出来,这三次中其中两次如果是高电平,那么就认定这一位数据是1,如果两次是低电平,那么就认定这一位是0,这样一旦受到意外干扰读错一次数据,也依然可以保证最终数据的正确性。

      了解了串口采集模式,在这里要给大家留一个思考题。“晶振值/12/2/16/波特率”这个地方计算的时候,出现不能除尽,或者出现小数怎么办,允许出现多大的偏差?把这部分理解了,也就理解了我们的晶振为何使用11.0592M了。

      串口通信的发送和接收电路在物理上有2个名字相同的SBUF寄存器,它们的地址也都是0x99,但是一个用来做发送缓冲,一个用来做接收缓冲。意思就是说,有2个房间,两个房间的门牌号是一样的,其中一个只出人不进人,另外一个只进人不出人,这样的话,我们就可以实现UART的全双工通信,相互之间不会产生干扰。但是在逻辑上呢,我们每次只操作SBUF,单片机会自动根据对它执行的是“读”还是“写”操作来选择是接收SBUF还是发送SBUF,后边通过程序,我们就会彻底了解这个问题。

#include <REGX52.H>
void ConfigUART(unsigned int baud);
void main()
{EA =1;
ConfigUART(9600);while(1);}
void ConfigUART(unsigned int baud)
{SCON  = 0x50;TMOD &= 0x0F;TMOD |= 0x02;TH1 = 256 - (11059200/12/32)/baud;TH1 =TL0;ET1 = 0;TR1 = 1;
}
void InterruptUART() interrupt 4
{if(RI){RI = 0;SBUF++;}if(TI){TI = 0;}
}

4、了解字符和数据之间的转换依据和方法。

  我们用字符格式发送一个小写的a,返回一个十六进制的0x61,数码管上显示的也是61,ASCII码表里字符a对应十进制是97,等于十六进制的0x61;我们再用字符格式发送一个数字1,返回一个十六进制的0x31,数码管上显示的也是31,ASCII表里字符1对应的十进制是49,等于十六进制的0x31。这下大家就该清楚了:所谓的十六进制发送和十六进制接收,都是按字节数据的真实值进行的;而字符格式发送和字符格式接收,是按ASCII码表中字符形式进行的,但它实际上最终传输的还是一个字节数据。这个表格,当然不需要大家去记住,理解它,用的时候过来查就行了。

5、完成通过串口控制流水灯流动和停止的程序。

#include <reg52.h>sbit ADDR3 = P1^3;
sbit ENLED = P1^4;unsigned char code LedChar[] = { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
unsigned char LedBuff[7] = {  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
unsigned char T0RH = 0;  
unsigned char T0RL = 0; 
unsigned char RxdByte = 0;  
unsigned char flag200ms = 0; 
unsigned char flagLight = 1;void ConfigTimer0(unsigned int ms);
void ConfigUART(unsigned int baud);
void FlowingLight();void main()
{EA = 1;ADDR3 = 1;ENLED = 0;ConfigTimer0(1);ConfigUART(9600);while(1){if(flagLight == 0){LedBuff[6]= 0XFF;}else{if (flag200ms != 0)  {flag200ms = 0;FlowingLight();}}LedBuff[0] = LedChar[RxdByte & 0x0F];LedBuff[1] = LedChar[RxdByte >> 4];}}
void FlowingLight()
{static unsigned char dir = 0;static unsigned char shift = 0x01; LedBuff[6] = ~shift;if(dir == 0){shift = shift << 1; if(shift == 0x80){dir = 1;}}else{shift = shift >> 1;if(shift == 0x01){dir = 0;}}
}
void ConfigTimer0(unsigned int ms)
{unsigned long tmp;  tmp = 11059200 / 12;     tmp = (tmp * ms) / 1000;  tmp = 65536 - tmp;        tmp = tmp + 13;       T0RH = (unsigned char)(tmp>>8);  T0RL = (unsigned char)tmp;TMOD &= 0xF0;   TMOD |= 0x01;  TH0 = T0RH;    TL0 = T0RL;ET0 = 1;        TR0 = 1;        
}
void ConfigUART(unsigned int baud)
{SCON = 0X50;TMOD &=  0X0F;TMOD |= 0X02;TH1 = (11059200/12/32)/baud;TL1 = TH1;ET1 = 0;ES = 1;TR1 = 1;
}
void LedScan()
{static unsigned char i = 0; P0 = 0xFF;            P1 = (P1 & 0xF8) | i;  P0 = LedBuff[i];       if (i < 6)            i++;elsei = 0;
}
void InterruptTimer0() interrupt 1
{static unsigned char tmr200ms = 0;TH0 = T0RH;TL0 = T0RL;LedScan();tmr200ms++;if(tmr200ms >= 200){tmr200ms = 0;flag200ms = 1;}
}
void InterruptUART() interrupt 4
{if(RI){RxdByte = SBUF;SBUF = RxdByte;RI = 0;flagLight = !flagLight;	 }if(TI){TI = 0;}
}


6、完成通过串口实现蜂鸣器鸣叫的程序。

#include <reg52.h>sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
sbit BUZZ  = P1^6;unsigned char code LedChar[] = { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
unsigned char LedBuff[7] = {  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
unsigned char T0RH = 0;  
unsigned char T0RL = 0; 
unsigned char RxdByte = 0;  
unsigned char flagBuzz = 0;void ConfigTimer0(unsigned int ms);
void ConfigUART(unsigned int baud);void main()
{EA = 1;ADDR3 = 1;ENLED = 0;ConfigTimer0(1);ConfigUART(9600);while(1){LedBuff[0] = LedChar[RxdByte & 0x0F];LedBuff[1] = LedChar[RxdByte >> 4];}}void ConfigTimer0(unsigned int ms)
{unsigned long tmp;  tmp = 11059200 / 12;     tmp = (tmp * ms) / 1000;  tmp = 65536 - tmp;        tmp = tmp + 13;       T0RH = (unsigned char)(tmp>>8);  T0RL = (unsigned char)tmp;TMOD &= 0xF0;   TMOD |= 0x01;  TH0 = T0RH;    TL0 = T0RL;ET0 = 1;        TR0 = 1;        
}
void ConfigUART(unsigned int baud)
{SCON = 0X50;TMOD &=  0X0F;TMOD |= 0X20;TH1 = (11059200/12/32)/baud;TL1 = TH1;ET1 = 0;ES = 1;TR1 = 1;
}
void LedScan()
{static unsigned char i = 0; P0 = 0xFF;            P1 = (P1 & 0xF8) | i;  P0 = LedBuff[i];       if (i < 6)            i++;elsei = 0;
}
void InterruptTimer0() interrupt 1
{TH0 = T0RH;TL0 = T0RL;LedScan();if(flagBuzz == 0)BUZZ = 1;    elseBUZZ = ~BUZZ;    
}
void InterruptUART() interrupt 4
{if(RI){RxdByte = SBUF;SBUF = RxdByte;RI = 0;if(RxdByte == 'B'){flagBuzz = !flagBuzz;}}if(TI){TI = 0;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/139412.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CTF 全讲解:[SWPUCTF 2021 新生赛]jicao

文章目录 参考环境题目index.phphighlight_file()include()多次调用&#xff0c;多次执行单次调用&#xff0c;单次执行 $_POST超全局变量HackBarHackBar 插件的获取 $_POST打开 HackBar 插件通过 HackBar 插件发起 POST 请求 GET 请求查询字符串超全局变量 $_GET JSONJSON 数据…

Lua学习笔记:词法分析

前言 本篇在讲什么 Lua的词法分析 本篇需要什么 对Lua语法有简单认知 对C语法有简单认知 依赖Visual Studio工具 本篇的特色 具有全流程的图文教学 重实践&#xff0c;轻理论&#xff0c;快速上手 提供全流程的源码内容 ★提高阅读体验★ &#x1f449; ♠ 一级标题…

基于Spring Boot的网上租贸系统

目录 前言 一、技术栈 二、系统功能介绍 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 本课题是根据用户的需要以及网络的优势建立的一个基于Spring Boot的网上租贸系统&#xff0c;来满足用户网络商品租赁的需求。 本网上租贸系统应用Java技术&#xff0…

IP地址欺骗的危害与后果

IP地址欺骗&#xff0c;也被称为IP地址伪装或IP地址欺诈&#xff0c;是一种网络攻击技术&#xff0c;旨在伪装或隐藏攻击者的真实IP地址。尽管这种技术可能有一些合法的用途&#xff0c;例如保护用户的隐私或绕过地理位置限制&#xff0c;但它也经常被恶意黑客用于不法行为。本…

阿里云 linux 的nginx 配置uni-app的H5前端项目vue,后端接口阿里云。

背景 vue项目调用接口是阿里云的&#xff0c;H5网站也要部署到阿里云&#xff0c;2个不同的服务器&#xff0c;需要做nginx部署与api代理。 1.端口配置 首先当然是买个阿里云服务器&#xff0c;这里是配置是linux系统&#xff0c;配置访问的域名 &#xff0c;再接着&#xf…

centos搭建activemq5.16

下载jdk、activemq&#xff08;我这里都放在在/usr/local&#xff09;之后。。。 在/usr/local/activemq/bin/目录下有一个env文件添加JAVA_HOME 注意activemq.xml里面不能出现中文&#xff0c;注释也不行 接下来在/usr/lib/systemd/system/创建activemq.service文件 # 单元节…

华为云云耀云服务器 L 实例评测|配置教程 + 用 Python 简单绘图

文章目录 Part.I IntroductionChap.I 云耀云服务器 L 实例简介Chap.II 参与活动步骤 Part.II 配置Chap.I 初步配置Chap.II 配置安全组 Part.III 简单使用Chap.I VScode 远程连接华为云Chap.II 简单绘图 Reference Part.I Introduction 本篇博文是为了参与华为“【有奖征文】华…

flowable可使用元素介绍

1. 事件 Events 事件描述图标空启动事件空启动事件未指定触发器&#xff0c;由用户调用的启动事件。定时启动事件定时启动事件在指定时间内创建一次或多次的流程实例。消息启动事件消息启动事件使用具名消息启动流程实例。消息名用于定位指定的启动事件。一个流程定义不得包含…

源码:TMS FlexCel Studio for .NET 7.19

TMS FlexCel Studio for .NET 是100% 托管代码 Excel 文件操作引擎以及 Excel 和 PDF 报告生成&#xff0c;适用于 .NET、Xamarin.iOS、Xamarin.Android、Xamarin.Mac、Windows Phone 和 Windows Store 功能概述 使用 FlexCel Studio for .NET 创建可动态快速读写 Excel 文件的…

星际争霸之小霸王之小蜜蜂(十四)--资本家的眼泪

系列文章目录 星际争霸之小霸王之小蜜蜂&#xff08;十三&#xff09;--接着奏乐接着舞 星际争霸之小霸王之小蜜蜂&#xff08;十二&#xff09;--猫有九条命 星际争霸之小霸王之小蜜蜂&#xff08;十一&#xff09;--杀杀杀 星际争霸之小霸王之小蜜蜂&#xff08;十&#xf…

2023华为杯数学建模D题第三问——区域双碳目标情景设计样例

在第二问建立好预测模型的基础上&#xff0c;如何设计第三问所说的区域双碳路径&#xff0c;以对宏观政策进行指导&#xff01; 采用STIRPA的基本模型对中国碳达峰时间进行预测&#xff0c;对该模型公式两边取对数得到&#xff1a; 其中&#xff1a;P为人口&#xff0c;A为GDP…

01_docker镜像管理:80分钟一口气学完docker+k8s!带你掌握docker+k8s所有核心知识点,全程干货,无废话!

docker镜像的实际使用学习 开发过程中&#xff0c;需要安装很多三方工具&#xff0c;比如etcd、kafka、mysql、nginx等等 1、下载安装Docker工具。 2、获取该软件的Docker镜像&#xff08;基本上&#xff0c;都能搜索到核实的镜像&#xff09;&#xff0c;下载镜像nginx镜像…

研究生选控制嵌入式还是机器视觉好?

研究生选控制嵌入式还是机器视觉好&#xff1f; 我是嵌入式/硬件方向转的算法&#xff0c;现在是公司的算法负责人&#xff0c;如果再让我选一次&#xff0c;我是不会再选嵌入式方 向&#xff0c;嵌入式如果只做技术是没前途的。 你要是有一定自学能力&#xff0c;能自己在学校…

以太坊代币标准ERC20、ERC721

两个概念 ERC(Ethereum Request for Comment) 以太坊意见征集稿EIP(Ethereum Improvement Proposals)以太坊改进提案 ERC和EIP用于使得以太坊更加完善&#xff1b;在ERC中提出了很多标准&#xff0c;用的最多的标准就是它的Token标准; 有哪些标准详细见https://eips.ethereum…

Ant Design分页组件中实现禁止点击当前页按钮的方法

这里需要使用到Ant Design分页组件pagination的一个回调函数onChange onChange函数用来监听鼠标点击事件&#xff0c; 它有两个入参》1. 点击分页按钮时获取到的页码 2. 每页最大显示条数 所以&#xff0c;禁止点击当前分页按钮的核心逻辑是&#xff1a; if {当前页的页…

HBuilder X 未检测到手机或模拟器(安卓端)

解决办法 1、找到HBuilderX安装目录下的D:\HBuilderX\plugins\launcher\tools\adbs。 2、应用程序和应用程序扩展删除掉 3、然后把1.0.31目录下的文件拷贝到D:\HBuilderX\plugins\launcher\tools\adbs里&#xff0c;直接覆盖即可。 4、重启HBuilderX就可以检测到手机了。 注意…

InputAction的使用

感觉Unity中InputAction的使用&#xff0c;步步都是坑。 需求点介绍 当用户长按0.5s 键盘X或者VR left controller primaryButton (即X键)时&#xff0c;显示下一个图片。 步骤总览 创建InputAction资产将该InputAction资产绑定到某个GameObject上在对应的script中&#xf…

Redis延迟双删-架构案例2021(三十二)

数据库设计 某医药销售企业因业务发展&#xff0c;需要建立线上药品销售系统&#xff0c;为用户提供便捷的互联网药品销售服务、该系统除了常规药品展示、订单、用户交流与反馈功能外&#xff0c;还需要提供当前热销产品排名、评价分类管理等功能。 通过对需求的分析&#xf…

【C语言】指针的进阶(四)—— 企业笔试题解析

笔试题1&#xff1a; int main() {int a[5] { 1, 2, 3, 4, 5 };int* ptr (int*)(&a 1);printf("%d,%d", *(a 1), *(ptr - 1));return 0; } 【答案】在x86环境下运行 【解析】 &a是取出整个数组的地址&#xff0c;&a就表示整个数组&#xff0c;因此…

基于物联网的农村地区智能微电网系统(Simulink)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…