pic16f616是一款14引脚、8位的cmos单片机。采用精简指令集,仅有35条指令,由于采用了数据总线和指令总线分离的哈佛总线结构,使得除少量指令不是单周期之外,大部分的指令都是单周期指令。这样有利于提高单片机的运行速度和执行效率。
pic16f616这款单片机供电电压可以在2v到5.5v之间,内部集成了一个rc振荡器,频率可以配置成8mhz或者4mhz,也可以用外部晶振提供时钟。内部集成有ad转换、比较器等硬件模块,还具有上电复位、欠压复位、看门狗、代码保护等功能。三个定时器、pwm发生器等可以由用户编程。下面我来一一介绍关于pic单片机的这些模块和功能。
1.存储器
pic16f616分为程序存储其和数据存储器,程序存储器的大小是2048words,数据存储器的大小是128bytes.
程序存储器中0000h的地址为复位地址,当上电或者看门狗计时器等复位的时候,均会导致pc指针指向复位地址。地址0004h为中断地址,当无论发生什么中断的时候,pc指针就会指向此地址。在地址0005h~07ffh可以移植程序。
数据存储器分为两个部分,分别叫做bank0和bank1,其中bank0的地址范围为:00h-7fh,bank1的地址范围为80h-ffh.一般的寄存器都放在里面。可以通过寄存器statusl里面的rp0位来选择bank0和bank1.
在编程序的时候要注意的是,当你要操作的寄存器在bank0的时候,先要选择bank0(将寄存器status的rp0位置0),然后再对你所要操作的寄存器进行操作,当你要操作的寄存器在bank1的时候,同理先要选择bank1.
如果想要定义一些变量,可以在数据存储器20h开始的地址定义,定义的地址范围为20h-7fh.一般这么多就够用了。
2.pic的输入输出端口
在学习这个部分的时候,曾经遇到过一些问题.pic单片机的引脚不多,大多都是复用引脚,例如ad、io、比较器、外接晶振等等,所以在配置端口的时候,一定要知道每个功能怎样设置才能实现的,在这一小节中,我要讲的是通用io口的设置问题。
pic16f616有12个io口,但是有一个引脚(ra3)只能作为输入引脚用,不能用作输出,另外,a口具有电平变化中断的功能,而c口没有,在设计的时候要注意。
在设置的时候,一般要进行以下几项设置:
(1)设置端口是模拟端口还是数字端口,可以通过寄存器ansel来设置。例如你想用ad,就要将相应的引脚设置为模拟输入端口。
(2)如果你选择的是数字端口,接下来就要设置端口的方向,是输入还是输出(ra3除外),可通过寄存器trisa(a口)或trisc(c口)来设置。
(3)设置端口的输出电平,可以通过寄存器porta(a口)或portc(c口)来设置。
这是对io口的通用设置,但是这不是全部的设置,接下来的设置要看时a口还是c口了。对于a口,它有几个特殊的功能:内部弱上拉、电平变化中断、ra2/int引脚的沿中断。如果想要这些功能,就要对相应的寄存器进行设置。
弱上拉的设置:只有当引脚为输出的时候弱上拉才有效,可以通过寄存器wpua来设置相应引脚的弱上拉,值得一提的如果开启了弱上拉,会有多余的电流浪费,这样对于低功耗的设计是不可取的,但是如果在进行一些例如键盘电路设计的候,可以开启弱上拉功能,这样就不需要在键盘电路中加上拉电阻了。
电平变化中断的设置:可以通过寄存器ioca来设置,但是首先要将相应引脚设置为数字端口且为输入状态。同时要将寄存器intcon的reie位设置为1,总中断要允许(置寄存器intcon的gie位),如果设置相应引脚有这个功能,当此引脚电平发生的时候,就会产生一个中断,同时一些中断标志位被置上(intcon的raif位被置1),且总中断gie被置为0.在中断服务程序中,要软件清除raif位和重新置gie位才能继续开启此中断。
ra2/int脚的沿中断设置:同样首先要将相应引脚设置为数字端口且为输入状态,设置intcon的intf位为1,表示允许int引脚外部中断,寄存器option_reg的integd位可以设置是上升沿中断还是下降沿中断。当发生中断时,intcon的intf位被置为1,gie被清零,在中断服务程序中,要软件清除intf位和重新置gie位才能继续开启此中断。
对于c口,不能产生电平变化中断和沿中断。
3.定时器
定时器是单片机的一个很重要的部分,用它可以产生很多不同的定时时间,来满足程序设计的不同需求.pic16f616有三个定时器,分别是timer0、timer1、timer2.它们的用法不是很相同,下面来分别谈谈这三个定时器的用法和设置问题。
(1)timer0
timer0是一个八位的计数器,它有一个八位的计数寄存器tmr0,八位的预分频器(与看门狗共用),可以选择内部或者是外部时钟源,有计数器溢出中断的功能。
timer0可以作为一个定时器或者计数器来使用,与timer0有关的寄存器有:tmr0,intcon,option_reg,trisa.
当timer0作为定时器来使用的时候,要设置option_reg的t0cs位为0,表示用的是内部时钟,每一个指令周期tmr0的值会增加(当没有预分频的时候),当tmr0被赋值的时候,会有两个指令周期的延时。预分频器可以和看门狗共用,可以由option_reg的psa位来设置,当psa 为0的时候分频器选择timer0,当psa为1的时候分频器选择看门狗。同时,与分频器的分频值可以通过寄存器option_reg来设置,设置的值可以由1:2到1:256.当timer0的计数器tmr0计数从ffh到00h的时候会产生溢出,同时溢出标志位(intcon寄存器的t0if位)会置位(无论timer0的中断是否开启),如果中断已经开启了(intcon寄存器的t0ie被置位),那么就会产生溢出中断.t0if位需要软件对其进行清零。
当timer0作为计数器来使用的时候,就要用外部时钟源(option_reg的t0cs置1),每次当引脚t0ck1的沿到来时timer0的 tmr0会增加1,上升沿和下降沿可以由option_reg的t0se来设置。中断和timer0作为定时器使用时一样。在我们编程序的时候,可以用 timer0进行定时或产生定时信息,下面我来解释定时器的定时时间的计算。假设timer0用的时钟源是内部的4mhz,那么每条指令的执行时间就是 1us,设timer0的预分频系数是1:256,tmr0的初值是6,那么定时时间为:
256×(256-6)×1us=64ms
在编程的时候需要注意的是timer0的中断是不能把单片机从sleep的状态唤醒的
(2)timer1
timer1是一个十六位的计数器。它有一个计数寄存器对(tmr1h:tmr1l),时钟源也是内外可选的,具有一个2bit的预分频器,可以同步或者异步操作,具有中断功能,但是溢出中断只能在外部时钟、异步的模式才能将单片机从sleep中唤醒,timer1具有捕获/比较功能,还有被一些特殊事件触发功能(eccp),比较器的输出可以与timer1的时钟同步。下面来一一介绍这些功能。
在编程的时候也可以按照这样的步骤来进行。设置寄存器t1con,时钟源可以选择外部或者内部的时钟源,外部时钟源可以选择lp晶体.timer1在选择内部时钟时,可以运行在定时器的状态,选择外部时钟的时候,可以运行在定时器或者是计数器状态,工作于计数器状态时可以选择门限是高电平还是低电平计数。这些都可以通过寄存器t1con来设置。
以下是t1con每个位的具体功能:bit1:timer1是否开启位,当此位设为1时,timer1开启,设为0时,timer1关闭;bit2:时钟源选择位,置1时,选择外部时钟(t1ck1引脚的上升沿),此位置0时,选择的是内部时钟,并且和t1acs(寄存器cm2con1中)配合,当 t1acs位为0时,时钟为fosc/4,当t1acs位为1时,时钟为fosc.bit2:t1sync:定时器1的外部时钟输入同步位,当 tmr1cs位为1、t1sync位为1,定时器1被设置成与外部时钟不同步,t1sync位为0时,定时器1被设置成与外部时钟同步模式.bit3: t1oscen:此位为1时timer1的时钟选择lp,为0时lp晶体被关闭.bit5-4:t1ckps:timer1时钟的预分频系数设置,通过这两位的是指,可以讲timer1设置成1:1、1:2、1:4、1:8几种分频值.bit6:tmr1ge:只有当tmr1on位为1时才有效,当此位为 1时,timer1计数被timer1的门限控制,此位为0时,timer1正常计数.bit7:t1ginv:此位为1时,timer1在门限为高时计数,此位为0时,timer1在门限为低时计数。
timer1的中断编程:当timer1的计数产生溢出的时候,如果timer1中断允许的话,就会产生中断。中断可以这样设置,timer1的中断允许位tmr1ie(在pie1寄存器中)置1,寄存器intcon的peie位置1,同时总中断位gie(位于寄存器intcon中)要置为1.当定时器产生中断的时候,会把中断标志t1if置为1(位于寄存器pir1中),然后pc指针指向0004h地址.t1if位必须软件清除。
(3)timer2
timer2的功能于timer1有些不同,timer2时一个八位的计数器,有一个八位的计数寄存器tmr2,timer2具有以下功能:有两个分频器,一个是前分频器,一个是后分频器。分频可以软件进行设置,另外,timer2的时钟源是指令时间(fosc/4),timer2有一个寄存器 pr2,此寄存器的功能是当tmr2增加到pr2的值时,将产生中断,当然,中断必须允许,然后pr2的值会重新变为00h.下面来介绍timer2的编程:
timer2的控制寄存器t2con作用是设置timer2的开启关闭和前后分频的分频系数,寄存器t2con的toutps《3:0》 位设置后分频系数,可以被设置成1:1~1:16;位tmr2on为1时,timer2开启,为0时,timer2关闭;位t2ckps《1: 0》可以设置前分频系数,可以被设置成1、4、16.
timer2的中断可以这样控制,允许timer2中断位tmr2ie(位于pie1寄存器内)被置1时,timer2中断被允许,被置0时, timer2中断禁止。寄存器intcon的peie位置1,同时总中断位gie(位于寄存器intcon中)置为1.通过上面的设置,timer2就可以产生中断了。当定时器产生中断的时候,会把中断标志t2if置为1(位于寄存器pir1中),然后pc指针指向0004h地址。中断标志位t2if必须软件清除。
下面是三个定时器的比较:
唤醒功能
其他功能
定时器timer0
内部或外部时钟源,有一个预分频器。
定时器、
醒功能。
计数器值溢出时发生中断
预分频器与看门狗共用。
定时器timer1
内部或外部时钟源,有一个预分频器
定时器、计数器
外部时钟、异步模式时可唤醒cpu
计数器值溢出时发生中断
与比较器模块、
捕获/比较模块共用
定时器timer2
有前分频器和后分频器
醒功能。
计数器值与预置值相等时发生中断
pwm的产生需要此定时器
4.ad模块
pic16f616有一个十位、八路的ad转换器。其参考电压可以为电源电压vdd,也可以是外部参考电压(vref引脚),当ad转换完成后可以产生一个中断,此中断可以把单片机从睡眠状态中唤醒。下面来介绍一下关于ad转换的编程方法。
要使用一个adc,要做的有一下几件事情:
(1)设置端口,需要采样模拟信号的端口必须设置为模拟输入状态,如果设置为数字端口,将使转换结果不正确,端口的模拟输入可以由寄存器ansel来配置,在讲ra口的时候已经说到了如何配置了。
(2)通道的选择,有八路外部通道和三路内部通道,可以通过adcon0寄存器的chs《3:0》位来设置通道的选择。
(3)参考电压的选择,参考电压可以是vdd,也可以是外部参考电压,可以通过adcon0寄存器的vcfg位来设置,当vcfg=0时,参考电压为vdd,当vcfg=1时,参考电压为外部参考电压(来自vref引脚)
(4)adc的转换格式,ad转换后的结果保存在一个寄存器对里面:adresh和adresl,但是ad转换结果只有十位,设置ad转换格式可以通过设置 adcon0的adfm位来选择,当adfm=1时10位的ad结果的低八位保存在adresl内,高两位保存在adresh内;当adfm=0时10位的ad结果的高八位保存在adresh内,低两位保存在adresl内。
(5)ad时钟源的选择,寄存器adcon1专门来设置ad的时钟源,adcs《2:0》不同组合,可以将ad的时钟源设置为不同的频率,可以为fosc/2、fosc/4、fosc/8、fosc/16、fosc/32、fosc/64和frc(内部rc)。
(6)ad中断的配置,要使用ad的中断功能,可以先把ad中断使能,adie位设置为1(在寄存器pie1中),peie位置1(在intcon寄存器中),总中断gie位置1(intcon寄存器中)。
要开始一个ad转换,首先要使能adc模块,即把寄存器adcon0的adon位置1即可,然后将go/done位(adcon0中)置1就可以启动ad转换了。
ad转换需要时间,转换1bit需要tad的时间,tad与ad转换的时钟源和vdd有关,转换十位就需要11个tad时间,如果第一个ad转换完成了,要进行第二个ad转换,必须还要等待2*tad的时间才能开始。一个ad完成了,go/done位会被置为0,如果中断允许的话,就会产生中断,且中断标志位adif(寄存器pir1内)会被置1,在ad中断程序中就可以把ad转换结果读取出来(读adresh和adresl),需要时把ad中断标志位清零.
ad中断可以把单片机从睡眠中唤醒,但是要注意,使用这个功能的时候,时钟源必须设置为frc,否则的话在睡眠的时候就不会产生ad中断了。
5.看门狗
pic16f616的看门狗wdt其定时计数的脉冲序列由片内独立的rc振荡器产生,所以它不需要外接任何器件就可以工作。而且这个片内rc振荡器与引脚osc1/clkin上的振荡电路无关,即使osc1和osc2上的时钟不工作,wdt照样可以监视定时。例如:当pic16f616在执行 sleep指令后,芯片进入休眠状态,cpu不工作,主振荡器也停止工作,但是,wdt照样可监视定时。当wdt超时溢出后,可唤醒芯片继续正常的操作。而在正常操作期间,wdt超时溢出将产生一个复位信号。如果不需要这种监视定时功能,在编程时,可关闭这个功能。
wdt的定时周期在不加分频器的情况下,其基本定时时间是18ms,这个定时时间还受温度、vdd和不同元器件的工艺参数等的影响。如果需要更长的定时周期,还可以通过软件控制option寄存器(psa位置1)把预分频器配置给wdt,这个预分频器的最大分频比可达到1∶128.这样就可把定时周期扩大128倍,即达到2.3秒。
wdt的预分频器是和timer0所共用的,如果把预分频器配置给wdt,用clrwdt和sleep指令可以同时对wdt和预分频器清零,从而防止计时溢出引起芯片复位。所以在正常情况下,必须在每次计时溢出之前执行一条clrwdt指令喂一次狗,以避免引起芯片复位。当系统受到严重干扰处于失控状态时,就不可能在每次计时溢出之前执行一条clrwdt指令,wdt就产生计时溢出,从而引起芯片复位,从失控状态又重新进入正常运行状态。
当wdt计时溢出时,还会同时清除状态寄存器中的d4位t0,检测t0位即可知道复位是否由于wdt计时溢出引起的。
6.比较器
pic16f616有两个比较器:c1和c2,c1的结构比c2的结构要简单,下面我分别对这两个比较器的用法和特性作简要说明。
(4)比较器c1:它有一个独立的控制寄存器cm1con0,通过这个寄存器可以对比较器c1进行一些设置。位c1on可以控制c1的开启关闭,位c1oe 可以决定比较器的输出是从引脚输出还是内部输出,位c1pol可以选择比较器输出的极性,位c1r选择参考电压是链接到引脚c1in+还是连接到 c1vref,c1ch可以选择比较器负端从哪一个引脚输入的,位c1out存放了比较器的输出结果。
(5)比较器c2:它的控制寄存器cm2con0的操作跟c1一样,但是比较器c2比比较其c1功能要强,因为它与timer1挂上钩了,c2可以连接到 timer1,而c1不能。当c2与timer1相连接的时候,c2的输出可以设置成与timer1的下降沿锁定,如果timer1有分频,则比较器的输出与分频后的timer1下降沿锁定,可以通过相关寄存器来进行设置。
(6)两个比较还有其它的功能,都能组成滞回比较器,这样就可以对输入电压有一定的滤波功能。两个比较器还可以形成一个sr锁存器。
由于在本项目中没有选择用比较器这个功能,所以在这里就不详细叙述其细节设置,但要注意的是在不用此模块的时候,要能够保证此模块不能影响其他模块的正常工作,可以把比较器功能关闭(通过寄存器cm1con0、cm2con0的cxon位置0来关闭)。
7.捕获/比较/pwm功能
pic16f616具有捕获/比较/pwm的模块,下面来简单的介绍一下它们的功能。
这三个功能需要定时器的支持,捕获和比较功能需要定时器timer1的支持,pwm功能需要定时器timer2的支持。都有中断的功能,选择这三种功能的某一种功能可以通过寄存器ccp1con来设置.ccp1con的低四位ccp1m《3:0》可以通过不同的组合来开启某项功能和关闭所有功能,当ccp1m《3:0》=0000的时候,捕获/比较/pwm模块的所有功能被禁止。具体其他的不同组合实现的功能,请参考 pic16f616的用户手册。
当选择捕获功能时,它可以捕获引脚ccp1发生的事件,同时把16位timer1的计数值拷贝到ccpr1h:ccpr1l中来,引脚ccp1的发生事件可以指的是下列事件:ccp1引脚的每个上升沿或者下降沿、第四个上升沿、第十六个上升沿。可以通过寄存器ccp1con的低四位ccp1m《 3:0》来设置是哪一种事件。当事件发生的时候,单片机会置中断标志位ccp1if(寄存器pir1上),如果中断被允许(寄存器peie的位 ccp1ie=1)的话,就会产生中断,中断标志位ccp1if需要软件清零。
选择比较功能时,如果定时器timer1的计数器值与寄存器ccpr1h:ccpr1l相等的话,将产生下面的事件:把引脚ccp1置1/0、产生一个中断、触发一个事件(把定时器timer1的技术器tmr1清零,并且如果此时ad是允许的话,它将触发一次ad转换),这些事件可以通过寄存器 ccp1con的低四位ccp1m《3:0》来设置是哪一种事件。
当选择pwm功能时,通过设置pr2、t2con、ccpr1l、ccp1con这四个寄存器,模块可以产生不同占空比的pwm波形。具体的设置和占空比的计算请参考手册。
如果我们不需要这些功能,可以把这个模块关闭掉(设置ccp1m《3:0》=0000即可)。
8. 复位、中断和睡眠
(1)复位
pic16f616包括这样的几个复位功能,上电复位(power-on)、硬件复位、欠压复位(brown-out)、看门狗复位。
关于上电复位por,大家都不陌生,单片机在上电的时候保持复位直到电压能够满足其正常的工作电压,同时你可以通过对connfig(编译器上即可设置)的设置,来开启power-up time,这个时间一般为64ms.
硬件复位可以通过mclr引脚外界复位电路,即可实现硬件复位(将此引脚接低电平)。
欠压复位这个功能是可选的,也可以直接在编译环境中配置config寄存器来开启此功能。当此功能开启时,如果单片机在运行的时候,供电电压不足就会引起欠压复位,复位后单片机如果发现供电电压已经达到正常值的时候,会有一个64ms的延时,然后再运行程序。
关于看门狗的复位在看门狗部分已经说了。这里的一些复位还涉及到一些标志位。这些标志位分布在status和pcon上面.status上有两个位 to、pd,当标志位to=1时,表示表示已经操作了上电复位或者是执行了clrwdt或者sleep指令,当to=0时,表示发生了看门狗复位。当标志位pd=1时表示操作了上电复位或者是执行了clrwdt指令,当pd=0时,表示执行了sleep指令.pcon上有两个标志位是por和bor,分别表示的是上电复位和欠压复位标志。具体的可以参看手册.
(2)中断
pic16f616包括这样的几个中断源:ra2/int引脚外部中断、ra端口电平变化中断、定时器timer0、timer1、timer2溢出中断、比较器中断、ad转换中断、捕获/比较/pwm中断。
这些中断的允许位和中断标志位分别位于intcon、pie1、pir1、ioca这些寄存器里面,如果要开启相应的中断,就要置相应的中断允许位,开启总中断位(intcon寄存器的gie位),还要开启intcon上的peie位(定时器0溢出中断、int引脚沿中断和ra端口的电平变化中断除外)。
当中断发生的时候,相应的中断标志位就会置起来,同时总中断标志位gie会被清零,保证在此时间内不会相应其他的中断,然后将当前的pc指针值压栈保存,以用来保证中断能正确的返回到原来执行的地方。然后pc指针指向中断向量地址0004h的地方,所以在编程序的时候,你可以在0004h的地址存一条跳转指令跳到你定义的中断服务程序里面去就可以了。如果在中断的时候想保存一些重要的寄存器的话,可以在中断程序的起始将其保存,然后在中断服务程序的末尾将其恢复即可。
要注意的是中断标志位不会自己清零,这就需要在编程的时候在软件上对其清零,否则的话,单片机不停的执行中断服务程序。如果你想要在以后的程序中还能产生中断的话,就要把总中断允许位gie重新置位。
(3)睡眠
要想让单片机睡眠的方法很简单,执行一条sleep指令就可以了,如果看门狗允许的话,wdt就会被清零,但是还保持运行,寄存器status的pd位将会置0,to位将会置1,io口还保持原来的状态,在睡眠状态下,不能驱动振荡器了。
有些事件可以将单片机从睡眠状态中唤醒:看门狗、ra口电平变化中断、外部复位引脚mclk被拉低、ra2/int引脚沿中断、timer1中断(必须工作在异步计数模式)、eccp捕获模式中断、ad转换中断(时钟源必须为内部rc的时候)、比较器输出有变化,这些事件能够将单片机唤醒,其他的事件不能。
如果某项能唤醒单片机的中断已经开了,当总中断允许位gie为1的时候,单片机被唤醒后可以进入中断程序中去,而当gie位为0的时候,单片机也可以被唤醒,但是是执行下面的语句,而不能进入中断程序中去。
为了保证在执行sleep语句后看门狗能够清零,最好在sleep语句之前加一句清看门狗的语句clrwdt.
9. pic单片机的一些电特性
vss引脚的最大输出电流和vdd最大的输入电流为:90ma;
每个io口的输出电流可达25ma,io口总共输出电流可达90ma;
每个io口是由两个保护二极管上下钳位的。当电压超过vdd和vss的时候,二极管最大能承受20ma的电流;
io口输入漏电流最大为±1ua,引脚mclr和osc漏电流最大为±5ua;
porta内部弱上拉(若设置了此功能)电流最大为 400ua;
io口输出低电平为0.6v,输出高电压为vdd-0.7v;
10. 编程注意事项及技巧
在编程调试后和根据网上的一些资料和经验,我注意到了一些在编程的事项和技巧,通过这些设置,可以使系统更加稳定的工作,现在总结如下:
(1)在设置端口的时候,先将端口输出你想要预置的值,以免发生出示状态的不稳定,影响系统正常工作。虽然在当前还没有定义端口是输出还是输入状态,这样做总是好的。
(2)在开启某个中断功能的时候,最好将其中断标志位清一次零。
(3)在设计低功耗的时候,其中有些功能是比较耗电的,如果不用的话,一定要将其关掉。例如将io口设置成输入并将其悬空,就会很耗电流;ra口设置弱上拉的时候如果引脚接地,电流会很大;欠压复位也是一个耗电大户。而看门狗开启时用的时钟源为内部的rc,不怎么耗电;ad转换耗电也不多。
(4)单片机里面的功能很多,在有些功能不需要的时候,一定要将其关闭(可以放在初始化程序之中),这样一来有利于程序的稳定性;二来可以省电,因为开启某个功能总是要电来驱动的。
(5)如果一个寄存器被多种功能所共用,建议只对相应位进行操作,例如用bcf、bsf、或、异或、与、非等指令,而不要整个的将其赋值,以免弄错了使其他模块受到干扰.
华为将继续为欧洲5G网络客户提供服务?
宽带接收机中四种干扰信号
基于双通道光电耦合离轴旋转连接器设计方案
安全路由器的安全要求及中小企业如何选择
汽车行业中热熔胶点胶加工所用的热熔胶特点
PIC单片机的快速入门指南
temi机器人打造行业专属的智能助手
英飞凌FP100R12KT4 IGBT模块内部拆解分析
无线路由器在油田监控组网系统的应用
多晶硅产量大幅上涨,电池片转化效率有所提升
使用LED边缘光源技术优化背光系统
二极管的正负极怎么区分?
力神全面布局应战2018动力市场
随着对小型储能设备的需求不断增长 锂离子电池成本目标遥遥无期
IPMT:用于小样本语义分割的中间原型挖掘Transformer
智慧路口交通灯控制系统怎样设计比较合理
电池交流阻抗和直流内阻的介绍
解密vivo发展之路:从颜值至上到内外兼修
射频器件概述 射频器件的挑战与创新
A 5G Odyssey :2021北京漫游