单片机通讯实例与ASCII码
我们进修串口通讯次要是要完成单片机和电脑之间的信息交互,可以用电脑掌握单片机的一些信息,可以把单片机的一些信息情况发给电脑上的软件。下面我们就做一个复杂的例程,完成单片机串口调试助手发送的数据,在我们开辟板上的数码管上显示出来。
#includesbit 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] = { //数码管+自力 LED 显示缓冲区 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; unsigned char T0RH = 0; //T0 重载值的高字节 unsigned char T0RL = 0; //T0 重载值的低字节 unsigned char RxdByte = 0; //串口接纳到的字节 void ConfigTimer0(unsigned int ms); void ConfigUART(unsigned int baud); void main(){ EA = 1; //使能总中缀 ENLED = 0; //选择数码管和自力 LED ADDR3 = 1; ConfigTimer0(1); //设置装备摆设 T0 准时 1ms ConfigUART(9600); //设置装备摆设波特率为 9600 while (1){ //将接纳字节在数码管上以十六进制方式显示出来 LedBuff[0] = LedChar[RxdByte & 0x0F]; LedBuff[1] = LedChar[RxdByte >> 4]; } } /* 设置装备摆设并启动 T0,ms-T0 准时工夫 */ 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; //清零 T0 的掌握位 TMOD |= 0x01; //设置装备摆设 T0 为形式 1 TH0 = T0RH; //加载 T0 重载值 TL0 = T0RL; ET0 = 1; //使能 T0 中缀 TR0 = 1; //启动 T0 } /* 串口设置装备摆设函数,baud-通讯波特率 */ void ConfigUART(unsigned int baud){ SCON = 0x50; //设置装备摆设串口为形式 1 TMOD &= 0x0F; //清零 T1 的掌握位 TMOD |= 0x20; //设置装备摆设 T1 为形式 2 TH1 = 256 - (11059200/12/32)/baud; //盘算 T1 重载值 TL1 = TH1; //初值等于重载值 ET1 = 0; //制止 T1 中缀 ES = 1; //使能串口中缀 TR1 = 1; //启动 T1 } /* LED 静态扫描刷新函数,需在准时中缀中挪用 */ void LedScan(){ static unsigned char i = 0; //静态扫描索引 P0 = 0xFF; //封闭一切段选位,显示消隐 P1 = (P1 & 0xF8) | i; //位选索引值赋值到 P1 口低 3 位 P0 = LedBuff[i]; //缓冲区中索引地位的数据送到 P0 口 if (i < 6){ //索引递增轮回,遍历全部缓冲区 i++; }else{ i = 0; } } /* T0 中缀效劳函数,完成 LED 扫描 */ void InterruptTimer0() interrupt 1{ TH0 = T0RH; //从新加载重载值 TL0 = T0RL; LedScan(); //LED 扫描显示 } /* UART 中缀效劳函数 */ void InterruptUART() interrupt 4{ if (RI){ //接纳到字节 RI = 0; //手动清零接纳中缀标记位 RxdByte = SBUF; //接纳到的数据保管到接纳字节变量中 //接纳到的数据又直接发还,叫作-"echo", //用以提醒用户输出的信息能否已准确接纳 SBUF = RxdByte; } if (TI){ //字节发送终了 TI = 0; //手动清零发送中缀标记位 } }
人人在做这个试验的时分,有个小成绩要留意一下。由于 STC89C52 下载程序是运用了UART 串口下载,下载完程序后,程序运转起来了,可是下载软件最初还会经过串口发送一些额定的数据,所以程序刚下载出来不是显示 00,而能够是其他数据。人人只需把电源开关封闭,从新翻开一次就好了。
仔细的同窗能够会发现,在串口调试助手发送选项和接纳选项处,还有个"字符格局发送"和"字符格局显示",这是什么意思呢?
先抛开我们运用的汉字不谈,那么我们常用的字符就包括了 0~9 的数字、A~Z/a~z 的字母、还有各类标点符号等。那么在单片机零碎外面我们怎样来表现它们呢? ASCII 码(American Standard Code for Information Interchange,即美国信息交换规范代码)可以完成这个任务:我们晓得,在单片机中一个字节的数据可以有 0~255 共 256 个值,我们取个中的 0~127 共 128 个值付与了它别的一层涵义,即让它们辨别来代表一个常用字符,其详细的对应关系如表 11-3 所示。
表 11-3 ASCII 码字符表
ASC | 掌握 | ASCII | 字符 | ASCII | 字符 | ASCII | 字符 |
---|---|---|---|---|---|---|---|
000 值 | NUL 字符 | 032 值 | (space) | 064 值 | @ | 096 值 | |
001 | SOH | 033 | ! | 065 | A | 097 | a |
002 | STX | 034 | " | 066 | B | 098 | b |
003 | ETX | 035 | # | 067 | C | 099 | c |
004 | EOT | 036 | $ | 068 | D | 100 | d |
005 | END | 037 | % | 069 | E | 101 | e |
006 | ACK | 038 | & | 070 | F | 102 | f |
007 | BEL | 039 | ' | 071 | G | 103 | g |
008 | BS | 040 | ( | 072 | H | 104 | h |
009 | HT | 041 | ) | 073 | I | 105 | i |
010 | LF | 042 | * | 074 | J | 106 | j |
011 | VT | 043 | + | 075 | K | 107 | k |
012 | FF | 044 | , | 076 | L | 108 | l |
013 | CR | 045 | - | 077 | M | 109 | m |
014 | SO | 046 | . | 078 | N | 110 | n |
015 | SI | 047 | / | 079 | O | 111 | o |
016 | DLE | 048 | 0 | 080 | P | 112 | p |
017 | DC1 | 049 | 1 | 081 | Q | 113 | q |
018 | DC2 | 050 | 2 | 082 | R | 114 | r |
019 | DC3 | 051 | 3 | 083 | S | 115 | s |
020 | DC4 | 052 | 4 | 084 | T | 116 | t |
021 | NAK | 053 | 5 | 085 | U | 117 | u |
022 | SYN | 054 | 6 | 086 | V | 118 | v |
023 | ETB | 055 | 7 | 087 | W | 119 | w |
024 | CAN | 056 | 8 | 088 | X | 120 | x |
025 | EM | 057 | 9 | 089 | Y | 121 | y |
026 | SUB | 058 | : | 090 | Z | 122 | z |
027 | ESC | 059 | ; | 091 | [ | 123 | { |
028 | FS | 060 | < | 092 | \ | 124 | | |
029 | GS | 061 | = | 093 | ] | 125 | } |
030 | RS | 062 | > | 094 | ^ | 126 | ~ |
031 | US | 063 | ? | 095 | _ | 127 | DEL |
如许我们就在常用字符和字节数据之间树立了逐个对应的关系,那么如今一个字节就既可以代表一个整数又可以代表一个字符了,但它实质上只是一个字节的数据,而我们付与了它分歧的涵义,什么时分付与它哪一种涵义就看编程者的意图了。ASCII 码在单片机零碎中使用十分普遍,我们后续的课程也会常常运用到它,下面我们来对它做一个直不雅的看法,同窗们必定要深入了解其实质。
对比上述表格,我们就可以完成字符和数字之间的转换了,比方照样这个程序,我们发送的时分改成字符格局发送,接纳照样用十六进制接纳,如许接纳和数码管好做一下比照。
我们用字符格局发送一个小写的 a,前往一个十六进制的 0x61,数码管上显示的也是 61,ASCII 码内外字符 a 对应十进制是 97,等于十六进制的 0x61;我们再用字符格局发送一个数字 1,前往一个十六进制的 0x31,数码管上显示的也是 31,ASCII 内外字符 1 对应的十进制是 49,等于十六进制的 0x31。这下人人就该清晰了:所谓的十六进制发送和十六进制接纳,多是按字节数据的真实值停止的;而字符格局发送和字符格局接纳,是按 ASCII 码表中字符方式停止的,但它实践上最终传输的照样一个字节数据。这个表格,当然不需求人人去记住,了解它,用的时分过去查就行了。
通讯的进修,不像前边掌握局部那么直不雅了,通讯局部我们的程序只能取得一个后果,而其进程我们却无法直接看到,所以渐渐的能够人人就会晓得有示波器和逻辑剖析仪这类丈量仪器。假如黉舍试验室或许公司里有示波器或许逻辑剖析仪这类仪器,可以拿过去抓一下串口波形,直不雅的理解一下。假如临时还没有这些仪器,先晓得这么回事,有前提再说。由于工具类装备有的比拟昂贵,有前提可以尽量运用黉舍或许公司的。在这里我用一款简略单纯的逻辑剖析仪把串口通讯的波形抓出来给人人看一下,人人理解一下即可,如图 11-7 所示。
图 11-7 逻辑剖析仪串口数据表示图
剖析仪和示波器的感化,就是把通讯进程的波形抓出来停止剖析。先大约说一下波形的意思。波形右边是低位,左边是高位,上边这个波形是电脑发送给单片机的,下边这个波形是单片机回发给电脑的。以上边的波形为例,右边第一位是肇端位 0,从低位到高位顺次是10001100,次序倒一下,就是数据 0x31,也就是 ASCII 码内外的'1'。人人可以留意到剖析仪在每一个数据位都给标了一个白色的点,表现是数据,肇端位和有数据的时分都没有这个白点。工夫标 T1 和 T2 的差值在左边显示出来是 0.102ms,大约是 9600 分之一,略微有点偏向,在允许规模内即可。经过图 11-7,我们可以明晰的理解了串口通讯的收发的具体进程。
那我们这里再来理解一下,假如我们运用串口调试助手,用字符格局直接发送一个"12",我们在我们的数码管上应当显示什么呢?串口调试助手应当前往什么呢?经由实验发现,我们数码管显示的是 32,而串口调试助手前往十六进制显示的是 31、32 两个数据,如图 11-8所示。
图 11-8 串口调试助手数据显示
我们再用逻辑剖析仪把这个数据抓出来看一下,如图 11-9 所示。
图 11-9 逻辑剖析仪抓取数据
关于 ASCII 码表来说,数字自身是字符而非数据,所以假如发送"12"的话,实践上是是辨别发送了"1"和"2"两个字符,单片机呢,先收到第一个字符"1",在数码管上会显示出 31 这个对应数字,然则立时就又收到了"2"这个字符,数码管霎时从 31 酿成了 32,而我们视觉上呢,是没有方法发现这种疾速变更的,所以我们觉得数码管直接显示的是 32。