智能车硬件系统设计及实现

更新时间:2024-01-25 07:55:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

目 录

第一章 引言..........................................1 第二章 系统总体设计..................................2

2.1系统概述..........................................2 2.2 整车布局.........................................3

第三章 机械设计及实现................................4

3.1转向轮倾角的调整..................................4 3.1.1主销后倾........................................4 3.1.2 主销内倾........................................5 3.1.3 前轮外倾........................................5 3.1.4 前轮前束....................................... 5 3.2 舵机安装.........................................6 3.3 编码器的安装.....................................7 3.4 线性CCD的安装..................................7 3.5 齿轮啮合与差速调整...............................8

第四章 智能车硬件系统设计及实现......................9

4.1 硬件设计方案.....................................9

III

4.2 单片机最小系统板.................................9 4.3 电源稳压电路....................................10 4.4 电机驱动........................................11

4.5 辅助调试模块...................................12

第五章 软件系统设计及实现...........................13

5.1 系统软件流程图.................................. 13 5.2 线性CCD赛道识别处理...........................14 5.2.1 图像预处理.....................................14 5.2.2 边沿检测算法.................................. 14 5.3 控制策略........................................ 14 5.3.1 舵机PD控制....................................14 5.3.2 速度PID控制...................................15

第六章 系统开发及调试工具...........................17

6.1 软件开发工具....................................17 6.2 上位机..........................................18

第七章 车模的主要技术参数...........................19 第八章 结论.........................................20

IV

8.1制作成果........................................20 8.2 问题与思考..................................... 20 8.3 不足与改进..................................... 20 8.4 致谢与总结..................................... 21

参考文献............................................22 附录

附录A:系统原理图................................Ⅰ 附录B:部分程序源代码............................Ⅱ

摘 要

本文针对第九届飞思卡尔智能车比赛,主要介绍了南京信息工程大学FIW队设计的智能车系统。该智能车系统以MK60N512VMD100微控制器为核心控制单元,采用线性CCD摄像头来采集赛道信息,并对采集到的图像进行软件二值化,利用边沿检测算法提取黑色引导线。通过光电编码器检测模型车的实时速度,使用PID控制算法来调节电机转速和舵机转向角,实现对模型车运动速度和运动方向的控制。为了保证智能车运行时的快速性和稳定性,使用了无线模块和蓝牙串口模块,键盘液晶模块作为调试工具,并进行了大量硬件与软件调试。实验结果表明,该系统设计方案确实可行。

关键字: MK60N512VMD100,PID,线性CCD,边沿检测算法;

V

VI

Abstract

According to the Ninth Freescale Smart Car Competition, this article mainly introduces the smart car system designed by the FIW team from Nanjing University of Information Science and Technology. The smart car system uses MK60N512VMD100 micro

controller

as

the

core control

unit, captures

the information of the track by linear CCD camera, obtains two different values by software from the collected images, and uses edge detection gorithm to extract black guide line. We use a special photoelectric encoder as a sensor to detect the real-time speed of the model car, and adjust the motor speed and steering angle through the PID control algorithm to control the speed and direction of the motion. In order to improve the rapidity and stability of the car, we add a wireless module, a bluetooth serial module, a keyboard and a LCD module as debugging tools, and carry out a lot of tests based on hardware and software. The results show that the system design is feasible.

Keywords: MK60N512VMD100, PID, linear CCD, edge detection algorithm;

IV

V

第一章 引言

全国大学生“飞思卡尔”杯智能汽车竞赛起源于韩国,是韩国汉阳大学汽车控制实验室在飞思卡尔半导体公司资助下举办的以HCSl2单片机为核心的大学生课外科技竞赛。组委会提供一个标准的汽车模型、直流电机和可充电式电池,参赛队伍要制作一个能够自主识别路径的智能车,在专门设计的跑道上自动识别道路行驶,最快跑完全程而没有冲出跑道为获胜者。其设计内容涵盖了控制、模式识别、传感技术、汽车电子、电气、计算机、机械、能源等多个学科的知识,对学生的知识融合和实践动手能力的培养,具有良好的推动作用。

本技术报告主要包括机械系统、硬件系统、软件系统等,详尽地阐述了我们的设计方案,具体表现在硬件电路的设计以及控制算法的独特想法。智能车的制作过程包含着我们的辛勤努力,这份报告可以说是凝聚了我们这半年来的血汗和智慧,我们团队此次能突破我校在历年的参赛史上的突破,这也是我们共同努力的结果。

在准备比赛的过程中,我们小组成员涉猎控制、模式识别、传感器技术、汽车电子、电气、计算机、机械等多个学科,半年来的经历,培养了我们电路设计、软件编程、系统调试等方面的能力,锻炼了我们知识融合、实践动手的能力,对今后的学习工作都有着重大的实际意义。在此要感谢南京信息工程大学信息与控制学院对此次比赛的关注,我们的成果离不开学校的大力支持及指导老师悉心的教导;还要感谢的是和我们一起协作的队员们,协助,互促,共勉使我们能够走到今天。

1

第二章 系统总体设计

第二章 系统总体设计

2.1系统概述

该系统以Freescale32位单片机K60作为系统控制处理器,采用基于线性CCD的图像采集模块获取赛道图像信息,对图像进行软件二值化,提取黑色引导线,算出赛道中心线,计算出小车与赛道中心线的位置偏差;通过光电编码器来检测车速,并采用MK60N512VMD100的输入捕捉功能进行脉冲计算获得速度;转向舵机采用PD控制;驱动电机采用 PID控制,通过PWM控制驱动电路调整电机的功率;而车速的目标值由默认值、运行安全方案和基于图像处理的优化策略进行综合控制。

根据智能车系统的基本需求,我们设计了系统结构图,如图2.1所示

电机驱动模块光电编码器速度信息电源模块K60最小系统赛道信息线性CCD模块PID控制PD控制舵机模块

图2.1系统结构图

2

第二章 系统总体设计

2.2 整车布局

在整个设计过程中我们根据赛车的特点,在满足比赛要求的情况下,力求系统简单高效,因而在设计过程中尽量简化硬件结构,整车布局总结为以下6点:

(1)舵机采用立式安装,以提高舵机的响应速度; (2)采用强度高、质量轻的碳素杆制作线性CCD支架;

(3) 为了降低车模重心,电池低位放置;

(4) 将电池放在小车中间偏后,重心稍后,利于小车过弯;

“FIW”的整车布局图如图2.2所示:

图2.2 整车布局图

3

第二章 系统总体设计

第三章 机械设计及实现

光电组今年采用B车模,在整个调试过程中我们发现,车辆在高速情况对整车机械性能要求很高,为了能够使车在高速情况下更稳定流畅地运行,我们在前期装配时,对整车进行了细致的分析和装配,在规则允许的范围内进行改造,使车模的机械性能得到了提升。而前轮的束角和主销倾角对车的高速运行下的稳定性影响是最大的。舵机的灵敏程度同样对高速运行的车辆起着至关重要的的作用。

图3.1 车模示意图

3.1转向轮倾角的调整

3.1.1 主销后倾

主销的轴线相对于车轮的中心线向后倾斜的角度叫做主销后倾角,前轮的重心在主销轴线上,由于主销后倾使前轮的重心不在车轮与地面的接触点上,于是产生了离心力,这样可以保证汽车在直线行驶的稳定性还可以帮助车轮自动回正。

4

第三章 机械设计及实现

图3.1.1主销后倾示意图

3.1.2 主销内倾

主销轴线相对于车轮的中心线向内倾斜的角度叫主销内倾角。主销内倾角可以帮助舵机复位。但主销内倾角不宜过大,否则会加速轮胎的磨损。 3.1.3 前轮外倾

转向轮上端略向外倾斜的角度叫前轮外倾角。小车在空载时如果车轮垂直于路面,承载后由于悬架的变形而出现车轮内倾,由于外端轴承明显小于内端轴承,于是车轮工作的不安全性大大增加。为提高车轮的工作安全性,前轮要留有一定外倾角,承载后车轮正好垂直于地面。 3.1.4 前轮前束

前轮前束是指转向轮前端向内倾。此举是为了抵消前轮外倾带来的转向轮向两侧滚开的趋势,最大限度地减少车轮行驶过程中的横向滑移。

图3.1.4前轮前束示意图

5

第三章 机械设计及实现

3.2 舵机安装

舵机安装直接关系到是否能快速灵敏地转向的问题。如果舵机调整不到位,将很大程度上限制转向角度和转向响应速度。

舵机安装有两种方式,一种是卧式安装,另外一种为立式安装。

卧式安装为车模默认安装方式,但这样安装会使左右两边轮子连杆不等长,根据杠杆原理可知舵机对长连杆轮子用的力要大些,因此造成了舵机对左右两边转向响应时间不一样。另外由于卧式安装会使连杆与水平面呈现一定角度,从力学知识可以知道在轮子转向获得的力只是舵机施加在连杆上力的一个水平方向上的分力。

立式安装把舵机架高,增长了力臂,使得小车反应更加灵活,但增大了阻力,力的作用减小。因此,根据舵机性能和实际情况确定高度,将舵机立式正放,提高了其响应速度,然后将支架以合适的高度固定在底盘上。如图3.2所示。

图3.2舵机安装图

6

第三章 机械设计及实现

3.3 编码器的安装

编码器是智能小车速度反馈元件,其安装位置应该充分考虑测速的准确性,我们使用的是欧姆龙编码器。欧姆龙编码器连接线有四根,棕色为+5V,蓝色为负极,细黑和白色为信号输出,信号线需上拉电阻5—10K,区分正反转是通过相位差获得。在尝试了各种可能性后,采用了如图的安装方式。将编码器安装于车模尾部并尽量放低,与车轮传动齿轮良好啮合。

图3.3 编码器安装图

3.4 线性CCD的安装

CCD支架的选取,支架与车模的固定,CCD与支架的固定等,都会对小车的性能造成很大的影响。我们选取轻质碳杆作为CCD的支架,我们购买合适的器件将支架和车模牢牢的固定在一起,防止小车在运行过程中大幅抖动。CCD的安装如图3.4:

7

第三章 机械设计及实现

图3.4 线形CCD安装图

3.5 齿轮啮合与差速调整

电机与差速齿轮啮合主要是调整两齿轮的齿间距,合适齿间距能够减少两齿轮的磨损,延长其使用寿命。齿轮传动部分安装不恰当,会增大电机驱动后轮的负载;齿轮配合间隙过松则容易打坏齿轮过紧则会增加传动阻力。所以我们在电机安装过程中尽量使得传动齿轮轴保持平行,传动部分轻松、流畅,不存在卡壳或迟滞现象。

差速结构的作用是在车模转弯的时候,降低后轮与地面之间的滑动;并且还可以保证在轮胎抱死的情况下不会损害到电机。差速器的特性是:阻力越大的一侧,驱动齿轮的转速越低;而阻力越小的一侧,驱动齿轮的转速越高,以此次使用的后轮差速器为例,在过弯时,因外侧后轮轮胎所遇的阻力较小,轮速便较高;而内侧后轮轮胎所遇的阻力较大,轮速便较低。好的差速机构,在电机不转的情况下,右轮向前转过的角度与左轮向后转过的角度之间误差很小,不会有迟滞或者过转动情况发生。

8

第四章 智能车硬件系统设计及实现

4.1 硬件设计方案

我们在硬件总体设计时综合考虑了系统的可靠性、稳定性以及简洁性。硬件设计应在可靠的基础上使其简单化,满足稳定工作的基本要求,电源管理模块要保证使整个系统供电稳定;保证传感器信息采集准确有效;电机驱动则需在保证正常工作的情况下尽量减少对其他电路的干扰,所以电机驱动模块与主板无论是设计还是制板都是分开的。同时为了提高系统的简洁性,PCB板的形状设计和安装也充分考虑了车模的外观和整体设计。

整个智能车控制系统由三部分构成:K60最小系统板、主板、电机驱动和键盘电路板。

4.2 单片机最小系统板

MK60N512VMD100是K60系列MCU。Kinetis系列微控制器是Cortex-M4系列的内核芯片。

图4.2 最小系统原理图

9

第九届全国大学生智能汽车邀请赛技术报告

最小系统使用K60100 PIN封装,为减少主板空间,板上仅将本系统所用到的引脚引出,包括 PWM 接口,若干普通 IO 接口。其他部分还包括电源滤波电路、时钟电路、复位电路、串行通讯接口、SPI接口。

用到的接口如下:

电机PWM波输出:PTB0、PTB2 舵机PWM波输出:PTA5

编码器两相信号输入:PTB18、PTB19 线性CCD图像信号输入:PTE2

键盘输入信号:PTD0、PTD1、PTD2、PTD3、PTD4、PTD5、PTD6、PTD7 LCD接口:PTA14、PTA15、PTA16、PTA17

4.3 电源稳压电路

智能车的硬件电路有可充电镍镉电池(7.2V、2000mAh)提供。系统中的各个电路模块所需要的供电电压是不相同的,所以设计了稳压电路,将电池电压转换成各个模块所需要的电压。

本系统中,+3.3V给单片机供电;+5V为LCD、光电编码器、线性CCD模块、电机驱动模块供电;+6V为舵机供电;

单片机供电选用了低压差降压稳压芯片LM1117IPM-3.3,在压差为1V时就可稳定输出3.3V电压。

由于在电机驱动时电池压降较大为提高系统稳定性,必须使用低压差稳压芯片。为此我们选用了低压差线性稳压芯片LM2940S-5.0为5V工作的芯片供电。它们的纹波电压小,能对负载的变化迅速做出反应,适合为各个模块供电。

10

第四章 智能车硬件系统设计及实现

图4.3 电源稳压电路

4.4 电机驱动

B车模电机功率比较大,在实际工作时转速很快, 电机本身的性能直接决定了小车的行驶速度和加减速性能,因此,对驱动电路的参数要求十分严格, 驱动电路的参数性能对电机的发挥影响重大。

11

第四章 智能车硬件系统设计及实现

图4.4电机驱动电路

4.5 辅助调试模块

辅助调试模块主要用于智能汽车系统的功能调试、赛车状态监控 。我们采用液晶OLED128*64和按键以及蓝牙进行辅助调试,为方便调试提供了好的平台。

图4.4 键盘电路图

12

第五章 软件系统设计及实现

5.1 系统软件流程图

开始系统初始化开启中断等待中断定时计数1到定时计数2到图像处理PID电机控制赛道识别PD舵机控制

图5.1主程序流程图

图5.2 中断流程图

13

第五章 软件系统设计及实现

5.2 线性CCD赛道识别处理

5.2.1 图像预处理

由于TSL1401线性CCD返回的数据是模拟量,单片机需要对其进行AD转换后方能使用。要对CCD数据进行二值化,还需要设定一个阀值。但是由于CCD受环境光线影响较大,所以这个阀值需要实时变化即动态阈值,才能使CCD数据二值化后更准确。

由于线性CCD数据中本身可能会有噪声,赛道上的白色跑道可能会有斑点,赛道背景可能会反光等原因,CCD返回的数据不能直接使用。首先舍掉两边各7个像素点,对剩余数据进行比较求取最大最小值,然后将最大最小值求和平均得到动态阈值,由此进行二值化,二值化后再进行单点滤波。结果表明,此算法可行性较好。 5.2.2边沿检测算法

本系统采用的黑线提取方案是:只寻找黑白跳变沿,然后根据跳变沿的位置及历史数据来判断左右黑线。经过实验,本方案可靠性较高。

(1) 从左向右检测右黑线,从右向左检测左黑线,记录左右黑线坐标; (2) 两边都检测到黑线,直接求和平均求出中心值;

(3) 只检测到一边时,根据赛道宽度补线得出另一边,求出中心值; (4) 两边都未检测到时,根据CCD曝光时间判断出是十字交叉处还是赛

道背景;

5.3 控制策略

5.3.1 舵机PD控制

本系统的方向控制策略是:根据实时采集左右黑线算出的中线位置与初始赛道中心线的偏差通过PD控制来计算方向控制量。

滤波后的CCD数据中第8-120点的数据为有效数据,假设front_l为左黑线的位置,front_r为右黑线的位置,所以当前赛道中心线为(front_l+front_r)/2,将中心线视野中心的值作差,把这个差值作为方向控制的输入,乘以方向控制的P参数,即可得到方向控制量。同时方向控制加入了微分环节,即先计算本次方

14

第五章 软件系统设计及实现

向控制的误差与上一次方向控制的误差的差,然后把此差乘以方向控制的D参数。微分环节的加入,不仅使车模在通过小S弯时能减小左右摆动,而且在较大弯道中转弯更加平滑。 最后实现车模运行方向的控制。 5.3.2 速度PID控制

数字PID在生产过程中是一种最普遍采用的控制方法,在机电、冶金、机械、化工等行业中获得了广泛的应用。将偏差的比例(P)、积分(I)和微分(D) 通过线性组合构成控制量,对被控对象进行控制,故称PID控制器。

数字PID的版本有非常多,但是比较常用的版本是位置式PID和增量式PID,其中位置式PID的特点是它的输出与整个过去的状态有关,用到了误差的累加值,所以累积误差较大,位置式PID适用于执行机构不带积分部件的对象,如液伺服阀等;增量式PID的特点是它输出的控制量对应的是本次执行机构位置的增量,而不是对应执行机构的实际位置,因此要求执行机构必须具有对控制量增量的累积功能,如步进电机等。

本系统中速度控制使用了增量式PID和Bang-Bang控制。 PID算式连续控制系统中的PID控制规律是:

?1x(t)?KP?e(t)?Ti??t0e(?)d??Tdde(t)?? 公式1 dt?计算机系统是采样控制系统,只能根据采样时刻的偏差值计算控制量,数字PID是模拟连续系统PID控制规律的数字实现,所以要对控制系统反馈的信息进行离散化处理,用数字形式的差分方程代替连续系统的微分方程,即:

?n0e(t)dt??E(i)?t?T?E(i) 公式2

i?0i?0

nnde(t)E(K)?E(K?1)E(K)?E(K?1)?? 公式3 dt?tT

TTKP(K)?KP?E(K)??E(i)?D?E(K)?E(K?1)??公式4

TIi?0T

得到位置式PID算法公式为:

P(K)?KPE(K)?KI?E(i)?KD?E(K)?E(K?1)?i?0K公式5

15

第五章 软件系统设计及实现

根据公式5可得前一时刻的PID输出为:

P(K?1)?KPE(K?1)?KI?E(i)?KD?E(K?1)?E(K?2)?i?0K?1公式6

将公式5与公式6相减,得到增量式PID算法的计算公式为:

?P(K)?P(K)?P(K?1)

?KP?E(K)?E(K?1)??KIE(K)?KD?E(K)?2E(K?1)?E(K?2)? 公式7

在实验中,根据需要选取其中的一个或者多个环节来对被控对象进行控制。本系统中的速度控制则使用了增量式PID中的三个环节。

小车行驶途中,控制的主要思想:由于线性CCD每次只能采到一行数据,有效数据非常少,对于以较高速度行驶的智能车来说,仅凭借当前采到的一行数据是远远不够的,所以我们想到利用历史数据辅助判断赛道类型,将之前50个赛道中心线位置数据保存在数组中,组成一连串的赛道路径数据,并实时更新,由于CCD有一定的前瞻,所以历史数据便组成了一幅二维图像。在直道中主要通过直道计数器来完成对电机的控制。若正在处理的累计方差大于一个设定的值(通过测试得到),直道计数器加一。计算中心线与视野中心的累计方差,对于直道来说累积方差较小,对于弯道来说累积方差较大。若数组中前面累积方差大,同时直道计数器达到某一范围值时,即可判定是直道入弯,此时电机减速;若前面的累积方差较小,同时直道计数器达到一定的值时,即可判断是直道或弯道入直道,此时控制电机加速。

16

第六章 系统开发及调试工具

6.1软件开发工具

程序开发在IAR Embedded Workbench IDE下进行, Embedded Workbench for ARM 是IAR Systems 公司为ARM 微处理器开发的一个集成开发环境(下面简称IAR EWARM)。比较其他的ARM 开发环境,IAR EWARM 具有入门容易、使用方便和代码紧凑等特点。

EWARM 中包含一个全软件的模拟程序(simulator)。用户不需要任何硬件支持就可以模拟各种ARM 内核、外部设备甚至中断的软件运行环境。从中可以了解和评估IAR EWARM 的功能和使用方法。

图6-1 IAR Embedded Workbench IDE

17

图6.2 IAR编程主界面

6.2上位机

调试期间为了监测车模运行中遇到的各种赛道情况,需要用专门的串口软件来观察线性CCD的数据,我们选择了线性CCD调试助手。它能以灰度的方式显示CCD数据,实时地观察到数据的变化。图6.3为线性CCD调试助手的界面。

图6.3 线性CCD调试助手

18

第七章 车模的主要技术参数

表7.1 车模的主要技术参数表 赛车基本参数 长 宽 高 车重(千克) 功耗 电容总容量(微法) 传感器 280mm 180mm 380mm 1.3 >15W 1240.6 1个 1个 0 带载 光电编码器 线性CCD 除了车模原有的驱动电机、舵机之外伺服电机个数 赛道信息检测 精度 频率 5 mm 50Hz

19

第八章 结论

8.1 制作成果

本文主要介绍了线性CCD智能车的总体设计方案,包括机械结构的安装调整,硬件电路的设计和软件算法的设计和创新。在设计和制作车模的过程中,我们仔细阅读了历届优秀的技术报告,吸取前人的经验,结合实践进行改进,并且取得了一定的成果。本着严谨的态度,综合考虑各种问题。在设计过程中不断发现问题,分析问题,从车模的搭建,机械结构的调整,到控制算法的提出,程序编写,整个过程无不凝聚了我们辛勤的汗水。

8.2 问题与思考

在车模制作的过程中,我们的小车曾经出现了很大问题,如小车在高速行驶时,在弯道中尤其是大弯处容易跑出去,经过分析,低速行驶时从未出现这种情况,我们确定不是软件的问题,将问题锁定在硬件上,包括舵机和机械结构。通过舵机的更换调试,我们发现舵机本身存在一定的缺陷,为此我们从几个舵机中挑出一个性能相对较好的。同时机械结构的调整对速度的提高至关重要,哪怕是细微调整,对高速行驶的小车都有很大的影响。我们通过不断摸索和实践,调出较合适的前轮前束和主销倾角。

8.3 不足与改进

(1)对小车机械结构没有进行深入的研究和建模,只是根据小车实际情况来调整机械,缺乏理论依据。以后要就机械这一块进行深入研究,做到机械结构优良,在此基础上研究软件才能使效率更高。

(2)在程序设计和调试的过程中,有点模糊化。需要进行实际的建模和仿真,对智能车的运行要进行定量的分析和计算,对智能车运行中出现的各种情况都能做到理解,站在理论支持的角度进行分析和调试;

(3)使用的是蓝宙给的CCD上位机,只能观察图像,不便于小车的调试。开发具有接收数据和发送参数功能的的上位机,能够将小车的实时数据记录下来,对小车的调试带来极大的方便。

20

第八章 总结

8.4 致谢与总结

智能车比赛的整个准备过程历时将近一年的时间,不仅使我们得到了对已有知识进行实践的机会,更培养了一定的科研能力,拓宽了知识面,同时我们小组在智能车制作的过程中学会了发现问题,分析问题和解决问题的能力,我们大家互相配合,锻炼了团队精神。展望未来。 在此特别感谢一直支持和关注智能车比赛的学校和学院领导以及指导老师、指导学长,同时也感谢比赛组委会能组织这样一项有意义的比赛。

如今,我们做好一切准备,迎接即将到来的全国总决赛。到时,我们将面临更高的挑战,不过我们有信心,因为我们努力了,不管结果如何,它都将成为我们生命中一笔宝贵的财富,成为我们一生的回忆。

21

参考文献

[1]卓晴,黄开胜,邵贝贝.学做智能车.北京:北京航空航天大学出版社.2007. [2]谭浩强著.C程序设计.北京:清华大学出版社,2003.

[3]王宜怀,吴瑾,蒋银珍.嵌入式系统原理与实践.北京:电子工业出版社. 2012 [4]童诗白,华成英.模拟电子技术基础[M].北京: 高等教育出版社,2001. [5]周尧,顿海洋,罗林聪.北京科技大学摄像头一队技术报告.2013. [6]成毅,汪自强,崔永强.常熟理工学院闪电五队技术报告.2013.

[7]叶立威,曾巧文,邓振鹏.光电组-电子科技大学中山学院香山一队技术报告.2013. [8]邓兆祥,褚志刚等,“汽车前轮定位参数优化设计”,重庆大学机械传动国家重点实验

室.

22

附录

附录A系统原理图

I

附录B部分程序源代码

一、初始化程序

//初始化相关设备

/************************舵机频率初始化****************************/ uint8 FTM_PWM_Init(uint8 FTMx,uint32 freq) {

uint32 bus_clk_hz; uint32 mod; uint8 ps=0;

bus_clk_hz = periph_clk_khz*1000;

if(freq>bus_clk_hz) return 0;

if((mod=bus_clk_hz/(freq*128)) < 0xFFFFu)

{

ps = 7;

if(FTMx==0) {

FTM_MOD0=mod; }

else if(FTMx==1) {

FTM_MOD1=mod; }

else if(FTMx==2) {

FTM_MOD2=mod; } else

return 0;

if((mod=bus_clk_hz/(freq*64)) < 0xFFFFu) {

ps = 6; if(FTMx==0) {

FTM_MOD0= mod; }

II

else if(FTMx==1)

{

FTM_MOD1=mod; }

else if(FTMx==2) {

FTM_MOD2=mod; } else

return 0;

if((mod=bus_clk_hz/(freq*32)) < 0xFFFFu) {

ps = 5;

if(FTMx==0) {

FTM_MOD0= mod; }

else if(FTMx==1) {

FTM_MOD1=mod; }

else if(FTMx==2) {

FTM_MOD2=mod; } else

return 0;

if((mod=bus_clk_hz/(freq*16)) < 0xFFFFu) {

ps = 4; if(FTMx==0) {

FTM_MOD0= mod; }

else if(FTMx==1) {

FTM_MOD1=mod; }

else if(FTMx==2) {

III

FTM_MOD2=mod;

} else

return 0;

if((mod=bus_clk_hz/(freq*8)) < 0xFFFFu) {

ps = 3;

if(FTMx==0) {

FTM_MOD0= mod; }

else if(FTMx==1) {

FTM_MOD1=mod; }

else if(FTMx==2) {

FTM_MOD2=mod; } else

return 0;

if((mod=bus_clk_hz/(freq*4)) < 0xFFFFu) {

ps = 2;

if(FTMx==0) {

FTM_MOD0= mod; }

else if(FTMx==1) {

FTM_MOD1=mod; }

else if(FTMx==2) {

FTM_MOD2=mod; } else

return 0;

if((mod=bus_clk_hz/(freq*2)) < 0xFFFFu) {

IV

ps = 1;

if(FTMx==0) {

FTM_MOD0= mod; }

else if(FTMx==1) {

FTM_MOD1=mod; }

else if(FTMx==2) {

FTM_MOD2=mod; } else return 0;

if((mod=bus_clk_hz/(freq*1)) < 0xFFFFu) {

ps = 0;

if(FTMx==0) {

FTM_MOD0= mod; }

else if(FTMx==1) {

FTM_MOD1=mod; }

else if(FTMx==2) {

FTM_MOD2=mod; } else

return 0; } } } } } } } }

V

else

{

return 0; }

if(FTMx==0) {

// 使能FTM时钟模块

SIM_SCGC6 |= SIM_SCGC6_FTM0_MASK;

// 配置FTM控制寄存器

// 禁用中断, 加计数模式, 时钟源:System clock(Bus Clk), 分频系数:8 // 假设SysClk = 50MHz, SC_PS=3, FTM Clk = 50MHz/2^3 = 6.25MHz FTM0_SC = FTM_SC_CLKS(1)|FTM_SC_PS(ps);

// 设置PWM周期及占空比

// PWM周期 = (MOD-CNTIN+1)*FTM时钟周期 : // 配置FTM计数初始值 FTM0_CNT = 0; FTM0_CNTIN = 0;

// 配置FTM计数MOD值 FTM0_MOD = FTM_MOD0; }

else if(FTMx==1) {

// 使能FTM时钟模块

SIM_SCGC6 |= SIM_SCGC6_FTM1_MASK;

// 配置FTM控制寄存器

// 禁用中断, 加计数模式, 时钟源:System clock(Bus Clk), 分频系数:8 // 假设SysClk = 50MHz, SC_PS=3, FTM Clk = 50MHz/2^3 = 6.25MHz FTM1_SC = FTM_SC_CLKS(1)|FTM_SC_PS(ps);

// 设置PWM周期及占空比

// PWM周期 = (MOD-CNTIN+1)*FTM时钟周期 : // 配置FTM计数初始值 FTM1_CNT = 0; FTM1_CNTIN = 0;

VI

// 配置FTM计数MOD值

FTM1_MOD = FTM_MOD1; }

else if(FTMx==2) {

// 使能FTM时钟模块

SIM_SCGC3 |= SIM_SCGC3_FTM2_MASK;

// 配置FTM控制寄存器

// 禁用中断, 加计数模式, 时钟源:System clock(Bus Clk), 分频系数:8 // 假设SysClk = 50MHz, SC_PS=3, FTM Clk = 50MHz/2^3 = 6.25MHz FTM2_SC = FTM_SC_CLKS(1)|FTM_SC_PS(ps);

// 设置PWM周期及占空比

// PWM周期 = (MOD-CNTIN+1)*FTM时钟周期 : // 配置FTM计数初始值 FTM2_CNT = 0; FTM2_CNTIN = 0;

// 配置FTM计数MOD值 FTM2_MOD = FTM_MOD2; } else

return 0;

return 1; }

/*************************************************************** *函数名:uint8 FTM_PWM_Open(uint8 FTMx,uint8 channel, uint32 duty)

*功能: 打开PWM通道,设置占空比 *入口参数:(1)FTMx : 0 1 2

(2)channel 通道号 (3)duty 占空比 *出口参数:无

VII

*说明:无

***************************************************************/ uint8 FTM_PWM_Open(uint8 FTMx,uint8 channel, uint32 duty) {

uint32 cv;

volatile uint32 mod; if(duty>10000) return 0;

//占空比 = (CnV-CNTIN)/(MOD-CNTIN+1) if(FTMx==0) {

mod = FTM_MOD0; }

else if(FTMx==1) {

mod==FTM_MOD1; }

else if(FTMx==2) {

mod==FTM_MOD2; } else

return 0;

cv = (duty*(mod-0+1)+0)/10000;

if(FTMx==0) {

//选择并开启通道 switch(channel) {

case 0: case 1: case 2:

SIM_SCGC5 |= SIM_SCGC5_PORTA_MASK; PORT_PCR_REG(PORTA_BASE_PTR, PORT_PCR_MUX(3); break; case 3:

SIM_SCGC5 |= SIM_SCGC5_PORTC_MASK;

VIII

channel+3) =

PORT_PCR_REG(PORTC_BASE_PTR, channel+1) PORT_PCR_MUX(4); break; case 4: case 5: case 6: case 7:

SIM_SCGC5 |= SIM_SCGC5_PORTD_MASK; PORT_PCR_REG(PORTD_BASE_PTR, channel) PORT_PCR_MUX(4); break; default: return 0; }

// 配置FTM通道控制寄存器

=

=

// 通道模式 MSB:MSA-1X, 通道边缘选择 ELSB:ELSA-10 FTM_CnSC_REG(FTM0_BASE_PTR, channel) = FTM_CnSC_MSB_MASK|FTM_CnSC_ELSB_MASK; // 配置FTM通道值

FTM_CnV_REG(FTM0_BASE_PTR, channel) = cv; }

if(FTMx==1) {

//选择并开启通道 switch(channel) {

case 0:

SIM_SCGC5 |= SIM_SCGC5_PORTB_MASK; PORT_PCR_REG(PORTB_BASE_PTR, channel) = PORT_PCR_MUX(3);

//SIM_SCGC5 |= SIM_SCGC5_PORTB_MASK; //PORTB_PCR0= PORT_PCR_MUX(0x3)| PORT_PCR_DSE_MASK; break; case 1: case 2: case 3: case 4:

IX

case 5: case 6: case 7: default: return 0; }

// 配置FTM通道控制寄存器

// 通道模式 MSB:MSA-1X, 通道边缘选择 ELSB:ELSA-10 FTM_CnSC_REG(FTM1_BASE_PTR, channel) FTM_CnSC_MSB_MASK|FTM_CnSC_ELSB_MASK; // 配置FTM通道值

FTM_CnV_REG(FTM1_BASE_PTR, channel) = cv; }

if(FTMx==2) {

//选择并开启通道 switch(channel) {

case 0: break; case 1: case 2: case 3: case 4: case 5: case 6: case 7: default: return 0; }

// 配置FTM通道控制寄存器

// 通道模式 MSB:MSA-1X, 通道边缘选择 ELSB:ELSA-10 FTM_CnSC_REG(FTM2_BASE_PTR, channel) FTM_CnSC_MSB_MASK|FTM_CnSC_ELSB_MASK; // 配置FTM通道值

FTM_CnV_REG(FTM2_BASE_PTR, channel) = cv; }

X

=

=

return 1; }

/********************************正交解码测速初始化*************************/ void FTM2_QUAD_Iint(void) {

SIM_SCGC3|=SIM_SCGC3_FTM2_MASK;//使能FTM2时钟

//SIM_SCGC5|= SIM_SCGC5_PORTB_MASK; //Turn on PORTB clock

PORTB_PCR18= PORT_PCR_MUX(6); // 设置引脚B18引脚为FTM2_PHA功 PORTB_PCR19= PORT_PCR_MUX(6); // 设置引脚B19引脚为FTM2_PHB功能

FTM2_MODE |= FTM_MODE_WPDIS_MASK;//写保护禁止

FTM2_QDCTRL|=FTM_QDCTRL_QUADMODE_MASK;//AB相同时确定方向和计数值

//FTM2_FILTER|=(10<

FTM2_CNTIN=0;//FTM0计数器初始值为0 FTM2_MOD=65535;//结束值

FTM2_QDCTRL|=FTM_QDCTRL_QUADEN_MASK;//启用FTM2正交解码模式

FTM2_MODE |= FTM_MODE_FTMEN_MASK;//FTM2EN=1 FTM2_CNT=0; }

/********************************线性CCD初始化*************************/ void CCD_init(void) {

gpio_init (PORTE , 0, 1, 1); gpio_init (PORTE , 1, 1, 1); gpio_init (PORTA , 12, 1, 1); gpio_init (PORTA , 13, 1, 1); hw_adc_init(1); }

/********************************液晶初始化*************************/ void LCD_Init(void) {

XI

//设置PORTA pin14,pin15为GPIO口

PORTA_PCR14=(0|PORT_PCR_MUX(1)); PORTA_PCR15=(0|PORT_PCR_MUX(1)); PORTA_PCR16=(0|PORT_PCR_MUX(1)); PORTA_PCR17=(0|PORT_PCR_MUX(1));

//设置PORTA pin14,pin15为输出方向;pin16,pin17为输入方向

GPIOA_PDDR=GPIO_PDDR_PDD(GPIO_PIN(14)|GPIO_PIN(15)|GPIO_PIN(16)|GPIO_PIN(17));

GPIOA_PDOR |= GPIO_PDOR_PDO(GPIO_PIN(14)); //LCD_CS=1; //预制SLK和SS为高电平

GPIOA_PDOR &= ~GPIO_PDOR_PDO(GPIO_PIN(16)); LCD_DLY_ms(50);

GPIOA_PDOR |= GPIO_PDOR_PDO(GPIO_PIN(16));

LCD_WrCmd(0xae);//--turn off oled panel

LCD_WrCmd(0x00);//---set low column address LCD_WrCmd(0x10);//---set high column address

LCD_WrCmd(0x40);//--set start line address Set Mapping RAM Display Start Line (0x00~0x3F)

LCD_WrCmd(0x81);//--set contrast control register

LCD_WrCmd(0xcf); // Set SEG Output Current Brightness

LCD_WrCmd(0xa1);//--Set SEG/Column Mapping 0xa0左右反置 0xa1正常

LCD_WrCmd(0xc8);//Set COM/Row Scan Direction 0xc0上下反置 0xc8正常

LCD_WrCmd(0xa6);//--set normal display

LCD_WrCmd(0xa8);//--set multiplex ratio(1 to 64) LCD_WrCmd(0x3f);//--1/64 duty

LCD_WrCmd(0xd3);//-set display offset Shift Mapping RAM Counter (0x00~0x3F)

LCD_WrCmd(0x00);//-not offset

LCD_WrCmd(0xd5);//--set display clock divide ratio/oscillator frequency LCD_WrCmd(0x80);//--set divide ratio, Set Clock as 100 Frames/Sec LCD_WrCmd(0xd9);//--set pre-charge period

LCD_WrCmd(0xf1);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock LCD_WrCmd(0xda);//--set com pins hardware configuration

XII

LCD_WrCmd(0x12);

LCD_WrCmd(0xdb);//--set vcomh

LCD_WrCmd(0x40);//Set VCOM Deselect Level

LCD_WrCmd(0x20);//-Set Page Addressing Mode (0x00/0x01/0x02) LCD_WrCmd(0x02);//

LCD_WrCmd(0x8d);//--set Charge Pump enable/disable LCD_WrCmd(0x14);//--set(0x10) disable

LCD_WrCmd(0xa4);// Disable Entire Display On (0xa4/0xa5) LCD_WrCmd(0xa6);// Disable Inverse Display On (0xa6/a7) LCD_WrCmd(0xaf);//--turn on oled panel LCD_Fill(0x00); //初始清屏 LCD_Set_Pos(0,0); LCD_Fill(0xff);//黑屏 delay_ms(100);

LCD_Fill(0x00);//亮屏 delay_ms(100); }

/********************************定时器初始化**********************/ void hw_pit_init(uint8 pitno,uint32 timeout) {

SIM_SCGC6|=SIM_SCGC6_PIT_MASK; //使能PIT时钟 PIT_MCR&=~(PIT_MCR_MDIS_MASK); //调试模式下禁止 PIT_MCR|=PIT_MCR_FRZ_MASK; //使能PIT模块时钟 PIT_LDVAL(pitno)=timeout; //设置周期 1ms中断 PIT_TCTRL(pitno)|=PIT_TCTRL_TEN_MASK; //使能pit模块运行 PIT_TCTRL(pitno)&=~(PIT_TCTRL_TIE_MASK); //关pit中断 }

/********************************电机初始化*************************/ void Motor_init(void) //直流电机 {

DIL_B; //左电机B9 反转

/* Turn on all port clocks */

SIM_SCGC5 |= SIM_SCGC5_PORTC_MASK;

PORTB_PCR0= PORT_PCR_MUX(0x3)| PORT_PCR_DSE_MASK; // FTM is alt3 function for this pin PTB0

SIM_SCGC6|=SIM_SCGC6_FTM1_MASK; //使能FTM1时钟

XIII

//change MSnB = 1

FTM1_C0SC |= FTM_CnSC_ELSB_MASK; FTM1_C0SC &= ~FTM_CnSC_ELSA_MASK; FTM1_C0SC |= FTM_CnSC_MSB_MASK;

FTM1_SC = 0x29; //not enable the interrupt mask 0010 1001 FTM1_MODE |= FTM_MODE_WPDIS_MASK; //BIT1 Initialize the Channels Output

//FTMEN is bit 0, need to set to zero so DECAPEN can be set to 0 FTM1_MODE &= ~1; //BIT0 FTM Enable

//0 Only the TPM-compatible registers (first set of registers) can be used without any restriction. Do not use the FTM-specific registers.

//1 All registers including the FTM-specific registers (second set of registers) are available for use with no restrictions.

FTM1_OUTMASK=0XFE; //0 Channel output is not masked. It continues

to operate normally.

//1 Channel output is masked. It is forced to its inactive state.

FTM1_COMBINE=0; //Function for Linked Channels (FTMx_COMBINE)

FTM1_OUTINIT=0;

FTM1_EXTTRIG=0; //FTM aExternl Trigger (FTMx_EXTTRIG) FTM1_POL=0; //Channels Polarity (FTMx_POL) //0 The channel polarity is active high.

//1 The channel polarity is active low. //Set Edge Aligned PWM

FTM1_QDCTRL &=~FTM_QDCTRL_QUADEN_MASK;

//QUADEN is Bit 1, Set Quadrature Decoder Mode (QUADEN) Enable to 0, (disabled)

//FTM0_SC = 0x16; //Center Aligned PWM Select = 0, sets FTM Counter to operate in up counting mode,

//it is field 5 of FTMx_SC (status control) - also setting the pre-scale bits here

FTM1_INVCTRL=0; //反转控制

FTM1_SWOCTRL=0; //软件输出控制F TM Software Output Control (FTMx_SWOCTRL)

FTM1_PWMLOAD=0; //FTM PWM Load

XIV

//BIT9: 0 Loading updated values is disabled. //1 Loading updated values is enabled. FTM1_CNTIN=0; //Counter Initial Value

FTM1_MOD=1250; //Modulo value,The EPWM period is determined by (MOD - CNTIN + 0x0001)

//PMW频率=系统频率/4/(2^FTM1_SC_PS)/FTM1_MOD 10000=180000000/4/2/2250

FTM1_C0V=0; //设置 the pulse width(duty cycle) is determined by (CnV - CNTIN). 高电平的时间

//FTM1_C1V=0; //设置 the pulse width(duty cycle) is determined by (CnV - CNTIN). 高电平的时间

FTM1_CNT=0; //只有低16位可用,寄存器的初值 }

二、CCD采集与图像处理

void get_front() {

int16 i,j;

uint8 hold_flag=0;

if(qpx_count++>200) qpx_count=201; hold_flag=0;

mq=0;

if(LHOLDdiao_flag==0) {

Ldiaotou_flag=0; }

if(RHOLDdiao_flag==0) {

Rdiaotou_flag=0; }

for(i=121;i>=6;i--) {

if((Pixel_F[i]==1)&&(Pixel_F[i-1]==0)&&(Pixel_F[i-2]==0)&&(Pixel_F[i-3]==0)) {

front_l = i-2; front_l_flag = 1;

XV

break; }

front_l_flag = 0; }

for(j=6;j<=121;j++) {

if((Pixel_F[j]==1)&&(Pixel_F[j+1]==0)&&(Pixel_F[j+2]==0)&&(Pixel_F[j+3]==0))

{

front_r = j+2; front_r_flag = 1; break; }

front_r_flag = 0; }

//A.采到左右数据

if((front_l_flag == 1)&&(front_r_flag == 1)) {

LR_count++; Lr_count=0; lR_count=0; lr_count=0;

lost_all_flag = 0;

//a.1.左值大于右值,两边数据都丢失 if(front_l>front_r) {

//-m4-//---------------------------------------

if(qpx_count>200)//防止启动时主CCD采到起跑线出错 {

if(mq_last==1) {

mq=mq_last;

if((front_l_fg_last==1)&&(front_r_fg_last==0))

front_r=front_l+front_width;

if((front_l_fg_last==0)&&(front_r_fg_last==1))

front_l=front_r-front_width;

front_l_flag=front_l_fg_last;

XVI

front_r_flag=front_r_fg_last; } else {

if(Abs((front_l+front_l+front_width)/2-cl_f_pre)>Abs((front_r+front_r-front_width)/2-cl_f_pre))

front_l=front_r-front_width; else

front_r=front_l+front_width; } } else {

front_l=front_l_last; front_r=front_r_last; } }

//a.2.右值大于左值,正常 else {

if(front_r-front_l

if((front_l+front_r)/2<63) front_l=front_r-(front_r_last-front_l_last); else front_r = front_l+(front_r_last-front_l_last); }

else if(front_r-front_l>=front_width-10) {} } }

//B.右数据丢失

else if((front_l_flag == 1)&&(front_r_flag == 0)) {

LR_count=0; Lr_count++; lR_count=0; lr_count=0;

if(front_l>front_r_last) {

XVII

front_l=front_l_last;//+1; front_r=front_r_last;//+1; }

//-m2-//以赛宽判断-------------- else if(front_l<=front_r_last) {

//在“十字弯”处大偏差

if(front_l<127-front_width-10)//-5 {

Rdiaotou_flag=1;

Lpre4=Lpre3; Lpre3=Lpre2; Lpre2=Lpre1; Lpre1=front_l;

//A.实际上front_width不是此时实际的赛道宽度 //B.此处用了历史值,在过人字弯后会受影响 //-m1-//

front_r =front_l+(front_r_last-front_l_last); } else

{ //-m1-//

front_r =front_l+(front_r_last-front_l_last); } } }

//C.左数据丢

else if((front_l_flag == 0)&&(front_r_flag == 1)) {

LR_count=0; Lr_count=0; lR_count++; lr_count=0;

if(front_r

front_l=front_l_last;//-1; front_r=front_r_last;//-1; }

XVIII

//2.以赛宽判断

else if(front_r>=front_l_last) { //在“十字弯”处大偏差

if(front_r>front_width+10)//路径,每次入弯的效果,经验大+5 {

Ldiaotou_flag=1;

Rpre4=Rpre3; Rpre3=Rpre2; Rpre2=Rpre1; Rpre1=front_r; //-m1-//

front_l=front_r-(front_r_last-front_l_last);//!!!!-3 } else

{ //-m1-//

front_l=front_r-(front_r_last-front_l_last); } } }

//D.两边数据都丢失

else if((front_l_flag == 0)&&(front_r_flag == 0)) {

//全白,只有十字弯

if(IntegrationTime_Front

//-m3-//------------------- mq=1;

front_l_flag=front_l_fg_last; front_r_flag=front_r_fg_last;

if((front_l_fg_last==0)&&(front_r_fg_last==0)) {

front_l=front_l_last; front_r=front_r_last; }

else if((front_l_fg_last==0)&&(front_r_fg_last==1)) {

if(lR_count>20)

XIX

{

front_l=front_l_last-jiao_L;//2*lR_count; front_r=front_r_last-jiao_L;//2*lR_count; } }

else if((front_l_fg_last==1)&&(front_r_fg_last==0)) {

if(Lr_count>20) {

front_l=front_l_last+jiao_R;//2*Lr_count; front_r=front_r_last+jiao_R;//2*Lr_count; } }

else if((front_l_fg_last==1)&&(front_r_fg_last==1)) {

front_l=front_l_last; front_r=front_r_last; } }

//全黑,曲率大的弯处

else if(IntegrationTime_Front>IT_ref) {

front_l=front_l_last;//-2; front_r=front_r_last;//-2; /*

if((front_l_last+front_r_last)/2>front_reference) {

front_l=front_l_last;//+2; front_r=front_r_last;//+2; }

else if((front_l_last+front_r_last)/2<=front_reference) {

front_l=front_l_last;//-2; front_r=front_r_last;//-2; } */ }

LR_count=0; Lr_count=0;

XX

lR_count=0; lr_count++; }

mq_last=mq;

L_aver=(Lpre1+Lpre2+Lpre3)/3;

L_fch=((Lpre1-L_aver)*(Lpre1-L_aver)+(Lpre2-L_aver)*(Lpre2-L_aver)+(Lpre3-L_aver)*(Lpre3-L_aver))/3;

R_aver=(Rpre1+Rpre2+Rpre3)/3;

R_fch=((Rpre1-R_aver)*(Rpre1-R_aver)+(Rpre2-R_aver)*(Rpre2-R_aver)+(Rpre3-R_aver)*(Rpre3-R_aver))/3; //简单的变化率

delta_l=(front_l-front_l_last)+(front_l_last-front_l_pre1)+(front_l_pre1-front_l_pre2); delta_r=(front_r-front_r_last)+(front_r_last-front_r_pre1)+(front_r_pre1-front_r_pre2);

Lfch=((front_l-front_l_reference)*(front_l-front_l_reference)+(front_l_last-front_l_reference)*(front_l_last-front_l_reference)+(front_l_pre1-front_l_reference)*(front_l_pre1-front_l_reference))/3;

Rfch=((front_r-front_r_reference)*(front_r-front_r_reference)+(front_r_last-front_r_reference)*(front_r_last-front_r_reference)+(front_r_pre1-front_r_reference)*(front_r_pre1-front_r_reference))/3;

if(rzop==1) {

//-m2-//以赛道宽及其他判断赛道宽

//右调头-

if(Rdiaotou_flag==1) {

Rdiaotou_count++;

if(Rdiaotou_count<=3)//2 {}

else if(Rdiaotou_count>3&&Rdiaotou_count<60)//30 { if((front_r_last-front_l_last)<=(front_width+5))

XXI

Rendiao=1;

if(Rendiao==1) {

RHOLDdiao_flag=1; if(Rdiaotou_count<20) {

front_l=130-front_width/2; front_r=130+front_width/2; } } } else {

Rdiaotou_count=0; Rdiaotou_flag=0; Rendiao=0;

RHOLDdiao_flag=0; rensha_count=0; rzguo_flag=1; } }

if(Rdiaotou_flag==0&&RHOLDdiao_flag==0) {

Rdiaotou_count=0; }

//左调头

if(Ldiaotou_flag==1) {

Ldiaotou_count++; if(Ldiaotou_count<=3) {}

else if(Ldiaotou_count>3&&Ldiaotou_count<60)//30 { if((front_r_last-front_l_last)<=(front_width+5)) Lendiao=1;

if(Lendiao==1)

XXII

本文来源:https://www.bwwdw.com/article/zhcw.html

Top