presentation链接:https://h5.vnision.com/#/posts/KlpWj
一、实验目的
(一)选题意义
本选题通过三轴加速度ADXL345芯片得到两个方向的角度偏移,模拟硬件运动。实际可以应用到虚拟画笔控制、遥控无人机、游戏手柄等多重情景中,切实可行。
(二)选题功能
本选题是通过三轴加速度计ADXL345测得重力加速度在X、Y、Z方向的分加速度,通过分加速度计算出芯片在X、Y方向的倾角,再由数码管显示出来(左边数码管显示X方向的倾角,右边显示Y方向的倾角)
按键key1实现校准后,通过三轴加速器测定出X轴、Y轴的偏移角度。X轴角度数据对应画笔方向左右旋转的的角度大小,即前进的方向。Y轴角度数据控制画笔时速,正角度为加速,负角度为减速。其次在PC上建立画布显示角度映射出的画笔路径,即可通过动态绘图时事观察画笔的路径轨迹。
(三)涉及知识
本选题主要包含五个方面知识:
1.单片机三周加速器数据采集模块:
需要查阅三轴加速度器ADXL345的数据手册、了解相关寄存器的使用、IIC协议、ADXL345读写IIC总线的方式。
2.单片机串口通信模块:
需要了解控制串口中断的寄存器,以及了解PC端的串口通信知识以便于两边协调。
3.时序协调:
当以上单片机的两大模块整合的时候会出现时序的冲突,此时需要设计时序的协调。
4.PC机串口通信模块:
本案例使用的上位机用python实现,在python3.6的环境下运行;使用COM1串口进行通信。
5.PC机Demo画布显示模块:
对获取数据计算之后使用python的turtle模块对数据进行路径映射。
二、实验环境和工具
1.STC单片机
2.数据线
3.ADXL345三轴加速器芯片
4.Python3.6
5.Notepad++
三、测试方法和现象
(一)三轴加速器测试
0.IIC协议
三大串行总线:uart、spi、iic。其中同步:spi异步 :iic,uart同步和异步区别:采集数据是否用的是时钟的沿,如果是时钟沿采数据,同步传输,如果电平采集数据是异步。串口接受数据其实就是一个串转并的过程。
总线信号包含SDA 串行数据线以及SCL串行时钟线。总线空闲状态为:SDA高电平、SCL高电平。
起始位:SCL为高电平期间 SDA出现下降沿
终止位:SCL为高电平期间 SDA出现上升沿
数据传输 :SDA的数据在SCL高电平期间被写入从机。所以SDA的数据变化要发生在SCL低电平期间。
应答:当IIC主机(不一定是发送端还是接受端)将8位数据或命令传出后,会将SDA信号设置为输入,等待从机应答(等待SDA由高电平拉为低电平)若从机正确应答,表明数据或者命令传输成功,否则传输失败,注意,应答信号是数据接收方发送给数据发送方的。
IIC器件地址:每一个IIC器件都有一个器件地址,有的器件地址在出厂时地址就设定好了,用户不可以更改,比如OV7670的地址为0x42。有的器件例如EEPROM,前四个地址已经确定为1010,后三个地址是由硬件链接确定的,所以一个IIC总线最多能连8个EEPROM芯片。
图上开始信号之后,七位地址代表器件地址,第八位代表读或者写,0为写,1代表读,然后跟着响应位。
IIC器件单字节写时序:
IIC器件多字节地址写时序:多字节地址比单字节地址在时序上就多了一块写地址
单字节器件读时序:注意最后产生无应答信号,另外多字节地址读时序跟单字节类似,只不过是多了几个地址字节而已。
1.下载编程
2.Key1校准
3.变换角度观察数码管角度变化
(二)单片机串口测试
1.在C代码中改变主函数中要发送的数据为unsigned char型的1和2连续发送,发送完一组再发送三个0xff
2.下载编程
3.Key1校准
4.打开串口助手串口,打开串口COM1,并使用文本模式和HEX模式并各自观察
文本模式:
HEX模式:
(三)单片机三轴加速器及串口整合测试
1.在C代码中改变主函数中要发送的数据为unsigned char型数据,分别为X轴夹角的十位、X轴夹角个位、Y轴夹角的十位、Y轴夹角的个位。发送完一组再发送三个0xff,注意此时发送速率稍慢,一秒发送一组数据,便于观察数据是否正确。
2.下载编程
3.Key1校准
4.打开串口助手串口,打开串口COM1,并使用文本模式和HEX模式并各自观察,看是否与数码管中显示的数据一样。
HEX模式:
从第一行第一组数据来看是“01 00 01 FF FF FF 00 01 00 01”,可以看出第一组合法数据组是黄色标记部分:“01 00 01 FF FF FF 00 01 00 01”。观察与单片机数码管显示数据相同,测试成功。
文本模式:
使用文本模式在ISP中查看发送的unsigned char型数据的时候会发现是无法显示的。询问老师后,得到回答是因为ISP自身缺陷导致字符无法正常显示。
(四)Python串口测试
1.打开设备管理器,打开端口属性选项,打开高级设置,
设置端口编号为COM1
2.运行python脚本
若打开失败则出现”系统找不到指定文件”的提示,该问题的解决方法在“问题及解决方法”部分有相应的记录。
若成功打开串口则出现“open success”
(五)Python数据处理测试
1.初始版本:
运行python脚本,等待五秒之后(因为脚本里调用sleep函数让系统休眠五秒,以得到稳定数据)会出现一组一组从单片机发送过来的X轴角度的十位、X轴角度的个位、Y轴角度的十位、Y轴角度的个位。
2.改进版本
发现上面得到的数据格式非常奇怪,是b’\x01’的模式,查阅了一下资料发现这是python特有的HEX表达,需要使用“int.from_bytes(data, byteorder = ‘big’)”这个函数来将数据格式转换为int形式。
3.展示版本
当数据处理好格式之后就对四个数据进行还原计算得到X、Y角度值,对应得到左右的角度。Original标识的角度是从单片机上获取的原始数据,actual_angle标识的角度是PC端画笔向左旋转的角度和向右旋转的角度。
(六)Python画图Turtle测试
现在完成所有的功能模块,完整的测试步骤如下:
1.打开ISP下载名为“ceshi1”的HEX文件,下载到实验板上。
2.关闭ISP(一定要这么做,否则ISP会占用COM1导致python脚本无法运行)。
3.打开python脚本,在python3.6环境下运行。
4.若串口打开成功,会出现“open success”的提示,经过五秒之后开始打印。每一行打印每组原始数据和处理过的数据。
5.同时弹出画图窗口,此时先按下Key1进行校准定位。
6.左右摇动单片机,当单片机数码管左边数据为[0,45]范围时候画笔左转,对应左转(45,0)度;当数码管左边数据为(45,90]范围的时候画笔右转,对应右转(0,45)度。
7.前后摇摆单片机,数据越大代表画笔速度越大,短时间会画出长距离。画布上出现画笔的轨迹。对应串口显示的左转角度、右转角度、时速三个数据可以对应观察。
四、问题及解决方法
(一)问题一:时序冲突
1.问题:
当三轴加速器模块与串口通信模块整合在一起的时候测试会发现,ISP串口助手显示特别慢,几乎出不来数字,数码管确是正常显示数据。
2.分析:
针对这个问题再看代码分析,是因为对应数码管扫描的中断0内部函数所占时间过长,猜想是因为三周加速器与IIC总线通信、数码管显示、发送串口信息这三个中有冲突。
3.方法:
最终的解决方法是在ISP中定时器处生成代码,让定时器0定时2ms。每次定时器溢出执行中断函数的时候除了扫描一遍数码管,另外让count_uart变量来记录中断执行次数,即数码管扫描次数。当count_usart达到250次的时候时间一共过去2ms*250=0.5s。
此时标记flag_usart变量为1。
当从中断重新回到主函数之后会判断flag_usart是否变为1,如果是1则说明数码管也已经扫描了很久,可以让它停一下来传递一组数据了。传递完数据之后让标志位flag_usart重新置0。
4.现象:
断重修改之后数码管正常显示,在ISP串口助手中查看可以发现数据正常传送,按照计算的相隔秒数为0.5s发送一组数据。此时可以让传送速率小一些,则可清晰对照数码管上显示数据是否也会出现在串口助手中。
现在数码管与串口助手数据显示几乎同步,达到了预期。
(二)问题二:ISP串口不存在或被占用
1.问题:
2.分析
ISP出现此报错原因有三:
① 没有在电脑设备管理器中设置端口为COM1。先打开设备管理器,打开端口属性选项,打开高级设置,设置端口编号为COM1
② 有可能是因为之前测试python串口脚本的时候测试串口未关,导致COM1被占用,关掉所有占用COM1串口的python运行窗口即可。
③ 有可能是ISP本身串口打开错误,如下图设置即可:
(三)问题三:pyserial包安装报错
1.问题:
对于pyserial包安装出错网上有诸多博客,大多有两个原因:不是在pycharm中安装的第三方库,而是在Windows的cmd中使用pip安装的;
Pycharm中的pip未升级。解决方法主要就是卸载安装包重新装。
但是我发现这些方法对我都不奏效,后来终于发现pycharm下载安装包的地方有一个安装包版本选择,需要对应安装的python版本进行选择。先打开file,打开settings,打开interpretation structure,点击绿色加号之后搜索pyserial包安装时候注意选择版本,如下:
五、心得体会
(一)知识学习
实际上遇到的困难不止上面这些。在这个小学期不仅学到了关于时序的知识(感觉这个好重要),还有自学了串口、python的知识,可谓一石多鸟。
除了知识,还有对新事物的接受力增强、独立性提升。
(二)心态变化
1.小学期之前:
记得在数字系统实验的时候老师就说我们专业的学生会“欺软怕硬”,说实话还是有点担心自己是不是真的能自己完成最后期末压轴的大作业。
2.小学期第一周:
根据我的日志来看(没错我有日志哈哈)在认真地焊板子、测案例、记录。均是一些比较轻松的任务,但是因为没有接触到代码等核心的知识,心里还是有些没有底。
3.小学期第二周:
第二周开始学习工程的代码,我主要学习的代码是前三个案例,主要知道流水灯和数码管是如何协调工作的,那时候觉得这个小小的东西还真是神奇。在周末验收案例的时候,我以极大的热情讲完了自己遇到的bug和费了好大力气才看懂的地方,最终十分哈哈哈超级开心!并开始期待小学期的最重大boss——STC大作业。
4.暑假:
暑假就开始认真思考自己的大作业最重要做啥东西。想学到真东西,又不想从流和别人相似,又想不要好高骛远选题太难实现。因为想学Arduino的时候买了一只工作箱有很多模块资源,所以日志里写了一大堆选题特别辣眼睛…最终选中的“虚拟摩托”的选题。外界器件是徐成男神老师帮我焊接的哈哈,所以想做一个与三周加速器有关的,灵感是游戏厅里面虚拟赛车的游戏手柄,想要实现简单的模拟。但是暑假诱惑太多所以只停留在想法。
5.小学期第三周:
我特别喜欢第三周!指定了下面这两周的计划:
周一 周一查资料,了解我的选题的可行度。确定可行之后梳理出所需要的四个主要模块的知识。开始第一个模块三轴加速器的学习(毫无成果)
周二 周二接着看案例里面三轴加速器的代码,研究是否可以带正负的角度值(实际上这一天终于搞明白因为计算方法的问题所以没有正负QAQ)接着看了一些关于串口的知识,可以在ISP中查看到发送的数据了。
周三 周三将三周加速器与串口通信的C代码进行整合。遇到了时序冲突的问题向学姐请教,获益匪浅。
周四 周四开始学习python3.6在notepad++中的环境配置,并完成串口通信pyserial模块在python中的测试。但是数据计算还是有问题。
周五 周五查阅了python中十六进制特殊的表达符号,并可以成功对数据进行暂时储存,一个必要功能测试成功。用一个中午学习了turtle模块的使用,成功调试出画图界面。至此整个项目完成。
周五完成这件事情之后,有个也做三轴加速器的同学(他精通java)做了一个 QQ飞车的界面,可以和网上联机飞车。还问我要了代码参考,我就给他了。顿时心灰了一大半。自己很努力做出来的东西只能做别人的一个子项目啊。
6.小学期第四周:
上周五做完了STC大作业周末休息,这一周就开始写实验报告并且开始FPGA的样例学习,十分充实。
通过上面五个时期的心态的变化可以看出来我真的通过这次小学期完成了一个小小的里程碑。
从前的我总是习惯依赖于和同学讨论,有什么会直接去问大神。但是这次每个人选题都不一样,即使是一样的每个人也出现各式各样的bug。所以通过这件事情我学会了独立学习,遇到不会的问题我会自己浏览很多博客,不再依赖“大神同学”,才发现自己也很厉害。一开始班级讨论各自选题的时候都说我的难实现,可是我也依靠自己实现了。
还有就是学习接受新事物。之前其实python早就装好了环境,连IDE特装好了的。但是就是觉得没有这个行动力。通过这个小项目又学到了很多python的知识也算是入门了。真的是deadline效率高啊。原来超级怕麻烦,有时候一个课程需要学习一个新的软件比如虚拟机啊、quartus啊、换一个编译器啦我都会觉得是一件不得了的事情。通过这个小学期发现自己也没有这么笨吗~别人认为很难的东西我也做出来了,别人学了一个暑假还不会的python我也略懂一二了。
最后就是永远会有别人比你强,你只要做好自己就好了。本来以为我这个选题会很独特,没想到也有人选了这个选题,还是一个我认识的朋友。他是java大佬,会用java调包什么的,做出来的东西是真的好。但这个时候你要自己相信自己,他是他,我是我。我和我从前对比,不要和别人尤其是大佬对比(当然当做flag可以),这样心里会少很多不开心。
(三)课程认识
给工训老师们鼓掌!
很幸运带我们班的是况老师。每次听讲座的时候,大大小小讲座老师都极其认真地坐在第一排,并极认真地做笔记。一来二去我也不好意思迟到发困走神了。我们班整体到讲座的人也很齐。
每次见到徐成老师,我才觉得这是大学,不是有“大师之谓”,而是有“大师之谓”。从来没有真正理解过“诲人不倦”,但是我在徐老师身上看到了对这个成句的身体力行。
建议可以多一些关于时序冲突时的整合的案例呀!因为同学大多是将两三个案例整合起来成为一个新的项目。总会涉及到时序的冲突,所以如果老师们可以讲解下这里就好啦。
六、实验代码及注释
(一)三轴加速器测试关键代码:
/**************************************
起始信号
**************************************/
void ADXL345_Start()
{
SDA = 1; //拉高数据线
SCL = 1; //拉高时钟线
Delay5us(); //延时
SDA = 0; //产生下降沿
Delay5us(); //延时
SCL = 0; //拉低时钟线
}
/**************************************
停止信号
**************************************/
void ADXL345_Stop()
{
SDA = 0; //拉低数据线
SCL = 1; //拉高时钟线
Delay5us(); //延时
SDA = 1; //产生上升沿
Delay5us(); //延时
}
/**************************************
发送应答信号
入口参数:ack (0:ACK 1:NAK)
**************************************/
void ADXL345_SendACK(bit ack)
{
SDA = ack; //写应答信号
SCL = 1; //拉高时钟线
Delay5us(); //延时
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
/**************************************
接收应答信号
**************************************/
bit ADXL345_RecvACK()
{
SCL = 1; //拉高时钟线
Delay5us(); //延时
CY = SDA; //读应答信号
SCL = 0; //拉低时钟线
Delay5us(); //延时
return CY;
}
/**************************************
向IIC总线发送一个字节数据
**************************************/
void ADXL345_SendByte(BYTE dat)
{
BYTE i;
for (i=0; i<8; i++) //8位计数器
{
dat <<= 1; //移出数据的最高位
SDA = CY; //送数据口
SCL = 1; //拉高时钟线
Delay5us(); //延时
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
ADXL345_RecvACK();
}
/**************************************
从IIC总线接收一个字节数据
**************************************/
BYTE ADXL345_RecvByte()
{
BYTE i;
BYTE dat = 0;
SDA = 1; //使能内部上拉,准备读取数据,
for (i=0; i<8; i++) //8位计数器
{
dat <<= 1;
SCL = 1; //拉高时钟线
Delay5us(); //延时
dat |= SDA; //读数据
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
return dat;
}
/**************************************
单字节写入
**************************************/
void Single_Write_ADXL345(uchar REG_Address,uchar REG_data)
{
ADXL345_Start(); //起始信号
ADXL345_SendByte(SlaveAddress); //发送设备地址+写信号
ADXL345_SendByte(REG_Address); //内部寄存器地址,请参考中文pdf22页
ADXL345_SendByte(REG_data); //内部寄存器数据,请参考中文pdf22页
ADXL345_Stop(); //发送停止信号
}
/**************************************
单字节读取
**************************************/
uchar Single_Read_ADXL345(uchar REG_Address)
{ uchar REG_data;
ADXL345_Start(); //起始信号
ADXL345_SendByte(SlaveAddress); //发送设备地址+写信号
ADXL345_SendByte(REG_Address); //发送存储单元地址,从0开始
ADXL345_Start(); //起始信号
ADXL345_SendByte(SlaveAddress+1); //发送设备地址+读信号
REG_data=ADXL345_RecvByte(); //读出寄存器数据
ADXL345_SendACK(1);
ADXL345_Stop(); //停止信号
return REG_data;
}
/**************************************
连续读出ADXL345内部加速度数据,地址范围0x32~0x37
**************************************/
void Multiple_read_ADXL345(void)
{ uchar i;
//flag=0;//表示正在执行IIC操作
ADXL345_Start(); //起始信号
ADXL345_SendByte(SlaveAddress); //发送设备地址+写信号
ADXL345_SendByte(0x32); //发送存储单元地址,从0x32开始
ADXL345_Start(); //起始信号
ADXL345_SendByte(SlaveAddress+1); //发送设备地址+读信号
for (i=0; i<6; i++) //连续读取6个地址数据,存储中BUF
{
BUF[i] = ADXL345_RecvByte(); //BUF[0]存储0x32地址中的数据
if (i == 5)
{
ADXL345_SendACK(1); //最后一个数据需要回NOACK
}
else
{
ADXL345_SendACK(0); //回应ACK
}
}
ADXL345_Stop(); //停止信号
Delay5ms();
}
/**************************************
初始化ADXL345
**************************************/
void Init_ADXL345()
{
Single_Write_ADXL345(0x31,0x0B); //测量范围,正负16g,13位模式
Single_Write_ADXL345(0x2C,0x08); //速率设定为12.5 参考pdf13页
Single_Write_ADXL345(0x2D,0x08); //选择电源模式 参考pdf24页
// Single_Write_ADXL345(0x2E,0x80); //使能 DATA_READY 中断
// Single_Write_ADXL345(0x1E,0x00); //X 偏移量 根据测试传感器的状态写入pdf29页
// Single_Write_ADXL345(0x1F,0x00); //Y 偏移量 根据测试传感器的状态写入pdf29页
// Single_Write_ADXL345(0x20,0x05); //Z 偏移量 根据测试传感器的状态写入pdf29页
}
(二)单片机串口测试关键代码:
#include <STC15F2K60S2.H>
typedef unsigned char uchar;
typedef unsigned int uint;
typedef unsigned short WORD;
/**************************************
初始化串口
**************************************/
void SerialIni(){
SCON &= 0x2E;
SCON = 0x50;
AUXR &= 0xBE;
AUXR |= 0x00;
TMOD &= 0x0F;
TMOD |= 0x20;
TH1 = 0xfd;
TL1 = 0xfd;
TR1=1;
ES = 1;
EA = 1;
}
/**************************************
传送字符
**************************************/
void write_COM(uchar COM)
{
SBUF=COM;
while(!TI);
TI=0;
}
/**************************************
发送换行
**************************************/
void write_END(void)
{
write_COM(0xFF);
write_COM(0xFF);
write_COM(0xFF);
}
/************延时函数*************/
void Delay5ms()
{
WORD n =10;//560
while (n--);
}
void main(void)
{
Delay5ms();
SerialIni();
while(1){
write_COM('1');
write_COM('2');
write_END();
write_END();
Delay5ms();
Delay5ms();
}
}
//串口中断
void Uart2(void) interrupt 4 using 1
{
unsigned char mydata;
if(RI)
{
RI=0;
mydata = SBUF;
SBUF = ~mydata;
}
else
TI = 0;
}
(三)三轴加速器与串口整合测试关键代码:
主函数:
void main(void)
{
Delay5ms();
delay(500);//上电延时
//选择模式0,推挽模式
P0M0=0xff;
P2M1=0x00;
P2M0=0x08;
SEL3=0;
//初始化
Init_ADXL345(); //三轴加速器初始化
SerialIni(); //串口初始化
Single_Read_ADXL345(0X00); //读取三轴加速器数据
//设置定时器0
AUXR &= 0x7F;
TMOD &= 0xF0;
TL0 = 0x30; //初值低八位
TH0 = 0xF8; //初值高八位
TF0 = 0; //定时器1溢出
TR0 = 1; //设置为1运行
IE=0x92; //设置开关
while(1){
Multiple_Read_ADXL345(); //连续读取数据
display_angle(); //显示XY轴偏移数据
//当数码管扫描了一定时间之后flag_usart为1
if(flag_usart){
write_COM(bai_x);
write_COM(shi_x);
write_COM(bai_y);
write_COM(shi_y);
write_END();
flag_usart = 0;
}
//按键消抖
if(Key1==0)
{
//delay(5);
if(Key1==0)
{
Multiple_Read_ADXL345();
display_angle();
offx=avg_x;
offy=avg_y;
}
while(!Key1) ;
}
}
}
中断0:
void t0int() interrupt 1
{
// TR0=0;
// IE=0x00;
// SEG_Display();
//
// TH0=(65535-500)/256;
// TL0=(65535-500)%256;
// TR0=1;
// IE=0x92;
//flagseg = 1;
count_usart++;
if(count_usart==100){flag_usart = 1;count_usart=0;}
SEG_Display();
}
中断1:
//串口中断函数(不做事)
void Uart2(void) interrupt 4 using 1
{
unsigned char mydata;
if(RI) //如果允许接收
{
RI=0; //接收中断标志位
mydata = SBUF; //接收数据
SBUF = ~mydata; //发送数取反的数据
}
// if(TI) //发送完数据后 RI自动制1
// {
// TI=0;
// SBUF = mydata;
// }
}
传送字节:
/***********传送字节***********/
void write_COM(uchar COM)
{
SBUF=COM;
while(!TI);
TI=0;
}
传送换行:
/***********传送换行*************/
void write_END(void)
{
write_COM(0xFF);
write_COM(0xFF);
write_COM(0xFF);
}
串口初始化:
/*********串口初始化**********/
void SerialIni(){
//定时器1设置,时间是2ms
SCON &= 0x2E;
SCON = 0x50;
AUXR &= 0xBE; //辅助寄存器,增大速率
AUXR |= 0x00; //辅助寄存器
TMOD &= 0x0F;
TMOD |= 0x20; //溢出控制寄存器
TH1 = 0xfd; //初值高八位
TL1 = 0xfd; //初值低八位
TR1=1; //开启定时器1
ES = 1; //串口中断允许位
EA = 1; //总中断允许位
}
(四)Python串口测试代码:
import serial
from time import sleep
def recv(serial):
while True:
data = serial.read_all()
if data == '':
continue
else:
break
sleep(0.02)
return data
if __name__ == '__main__':
serial = serial.Serial('COM5', 9600, timeout=0.5) #设置端口及波特率等参数
if serial.isOpen() :
print("open success")
else :
print("open failed")
while True:
data =recv(serial)
if data != b'' :
print("receive : ",data)
serial.write(data) #数据写回
(五)Python串口及画图代码:
import serial
from time import sleep
import turtle
import time
if __name__ == '__main__':
serial = serial.Serial('COM1', 9600, timeout=0.5)
if serial.isOpen() :
print("open success")
else :
print("open failed")
sleep(2)
cnt=0
turtle.pensize(10)
turtle.pencolor("yellow")
turtle.title('Virtual motorbike') #设置标题栏文字
time_pre=0.0
while True:
#单个数据测试
data =serial.read(1)
num1 = int.from_bytes(data, byteorder = 'big')
data =serial.read(1)
num2 = int.from_bytes(data, byteorder = 'big')
data =serial.read(1)
num3 = int.from_bytes(data, byteorder = 'big')
data =serial.read(1)
num4 = int.from_bytes(data, byteorder = 'big')
data =serial.read(1)
num5 = int.from_bytes(data, byteorder = 'big')
if(num1!=255 and num2!=255 and num3!=255 and num4!=255 and num5==255):
time_flag=time.time()
time_pre=time_flag
t=time_flag-time_pre
# print("receive num1: ",num1)
# print("receive num2: ",num2)
# print("receive num3: ",num3)
# print("receive num4: ",num4)
# print("")
#get angle
angle1=num1*10+num2
angle2=num3*10+num4
#draw roads
actual_angle1=0.0
actual_angle2=0.0
if(0<=angle1<45):
turtle.left(45-angle1)
actual_angle1=45-angle1
else:
turtle.right(angle1-45)
actual_angle2=angle1-45
#show data
print("original(x,y)=(",angle1,",",angle2,")","actual_angle(x,y)=(",actual_angle1,",",actual_angle2,")")
s=3*(80/90)*angle2
turtle.forward(s)