找回密码
 立即注册
楼主: 大明狐

STC单片机学习笔记杂记

[复制链接]
  • 打卡等级:以坛为家III
  • 打卡总天数:729
  • 最近打卡:2026-04-09 04:20:46

40

主题

466

回帖

4461

积分

荣誉版主

积分
4461
发表于 2026-3-25 00:25:56 | 显示全部楼层
狂热*** 发表于 2026-3-24 18:10
执行OTA程序耗时比较长
需要暂时关闭看门狗

原来如此👻那么就只能用软件启动的方式开启看门狗了,不然没法软件关闭。
能体会到发现一个不理解的现象然后找原因然后要么解决掉问题要么被问题解决掉的那种快乐是我的幸运
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:729
  • 最近打卡:2026-04-09 04:20:46

40

主题

466

回帖

4461

积分

荣誉版主

积分
4461
发表于 前天 15:36 | 显示全部楼层
最近学了下PCA的内容,但在更新学习笔记之前,先浪费一层楼嘟囔几句……

  今天看到论坛有人抱怨例程复杂,不够简单。
  于是试着换位思考了一下,感觉其实并不算是无理抱怨,只是他想要的大概是那种“喂饭级”的例程。
  但是换到另一个位置思考,例程这种东西,就是把功能实现出来的参考而已。随着学习单片机的编程,发现同一个功能,可以用很多种方法实现,对于不那么专业的人来说,总有一种或几种方法用起来比较顺手。所以对于别人分享的例程,如果想拿来参考,首先应该报以感谢的态度,然后再去研读里面的内容,分析程序作者的思路,进而消化成自己的理解。如果觉得自己的方法更好,不妨也拿出来分享,至于好不好,也应该敢于接受来自他人的评判。
  至于例程是否属于“喂饭级”,要喂饭,也得先知道你习惯的“口味”才可以。相信以论坛上的专业大佬们的水平,在了解“口味”的前提下,完全可以给出合口味的程序思路。
  关于那些例程,对于我这种初学者来说,大部分也确实比较难懂,但那也只是因为我个人基础差。在我看来,那些例程虽然算不上“喂饭级”,但也足够算得上是“保姆级”了。保姆嘛,避免磕磕碰碰,必要的时候拉扯一把,就已经尽职尽责了。至于吃饭,还是要自己学会拿筷子。

  查手册、扒例程、最后自己写出跑得起来的程序……在学习的过程中,例程里的很多编程思路和代码的表达方法,经常能让我从满脸迷茫,到豁然开朗,再到欣喜若狂。
  迷茫的是虽然例程完完整整地摆在那,但是对于一些关键点的说明和注释,却比较模糊或者没有。类似的例子还有一些教材书籍,记得曾经买过一本单片机的教材,结果除了重要注释比较少之外,例程更是一塌糊涂,比如本该是讲解几种功能的综合应用的章节,却单独把一套完整的TFT彩屏的驱动代码印了上去(还没有注释!),从驱动函数到显示函数甚至上K字节的字库和图片数组,洋洋洒洒占了好多页(网络时代了,难道还指望对着书页把几K字节的代码手敲进电脑吗?!),反而跟章节有关的内容却需要自己去寻找归纳。
  不过豁然开朗的地方就是在这个“寻找和归纳”的过程中。再说回到例程,在跟手册和其他文章帖子等等的交叉学习之后,逐渐摸到了例程作者的思路,以及代码的实际意思。这个过程并不美丽,但是一旦踏过这一步,随之而来的就是“欣喜”了。
  “欣喜”之后,就是带着思路自己尝试写程序,当自己敲出的程序顺利运行出了想要的结果之后,才是真正的“欣喜若狂”。在一次又一次的“若狂”之后,还是需要静下心来继续学习,提防把“若狂”变成了“真狂”,毕竟学无止境。尤其是像我这种初学者,切不可狂妄到拿着自己的“小狂”,去对别人的劳动吹毛求疵。

  曾经有位老师给我讲,读书学习的过程共分两步:
   第一步是“把书读厚了”,书(例程)只是一个资料库,在学习的过程中,把自己的迷惑和疑问都塞进去,于是书会越来越厚;
   第二步是“把书读薄了”,就是带着疑问,通过请教老师、查阅资料,把迷惑扫除,把疑问解开,最后书里的内容就会变成自己的理解,各种知识点都会变得凝练,于是这本书甚至会变得比之前还要薄。

  这个过程虽然艰苦,但是也的确充满乐趣。那么接下来就是把手册里的PCA章节“读厚了”的学习笔记。

能体会到发现一个不理解的现象然后找原因然后要么解决掉问题要么被问题解决掉的那种快乐是我的幸运
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:729
  • 最近打卡:2026-04-09 04:20:46

40

主题

466

回帖

4461

积分

荣誉版主

积分
4461
发表于 前天 16:03 | 显示全部楼层
一、什么是PCA/CCP/PWM

芯片手册中的这个章节叫“PCA/CCP/PWM”,于是产生第一个疑问,“什么是PCA/CCP/PWM”?

PCA:Programmable Counter Array(可编程计数器阵列)
  PCA是STC单片机内部的一个多功能硬件模块,包含一个16位计数器和多个捕获/比较模块,可配置为多种工作模式,如 Capture(捕获)、Compare(比较)、PWM(脉宽调制)输出等。

  CCP:Capture / Compare / PWM(捕获/比较/脉宽调制)
    CCP是PCA模块的三大核心功能的统称,描述的是PCA模块在具体应用时的三种工作模式,而非独立的模块名称。

    PWM:Pulse Width Modulation(脉宽调制)
      PWM是一种通过调节方波占空比来控制平均功率或电压的技术,常用于LED调光、电机调速等场景。PCA模块可硬件实现PWM输出,无需CPU干预。

简言之:
PCA是硬件模块的名称
  CCP是其功能分类的统称
    PWM是PCA的CCP功能之一

======================================================

开始学习的过程中,由于不想搞得太复杂,于是打算用一个STC8G1K08最小系统板和几颗LED,一边学习一边进行实验。
STC8G系列单片机内部集成了3组PCA模块。不同型号和封装,包含的PCA模块数量和引脚的分配会有所不同。比如:
  • STC8G1K08,8脚封装的没有PCA模块,16/20脚的则只有两组PCA模块;
  • STC8G1K08A,有三组PCA模块,但是分配的引脚跟其它STC8G的不同。

因为使用的是20脚的STC8G1K17,所以使用两组模块中的第一组进行学习。
截图202604071604574227.jpg

======================================================

二、PCA用到的寄存器

使用PCA模块,需要通过以下寄存器。
1、P_SW1(PCA功能脚切换)
截图202604071605413854.jpg
通过配置 P_SW1 寄存器的Bit_5、Bit_4两个位,进行CCP引脚切换。
CCP_S[1:0]:PCA功能脚选择位
截图202604071606102554.jpg
截图202604071606562957.jpg
ECI:是 External Capture Input (外部捕获输入)的缩写,主要用于PCA模块的外部脉冲源的输入。
CCP0:第一组CCP模块
CCP1:第二组CCP模块
CCP2:第三组CCP模块

2、PCA控制寄存器(CCON)
截图202604071607136545.jpg
CF:PCA计数器溢出中断标志。
  当PCA的16位计数器计数发生溢出时,硬件自动将此位置1,并向CPU提出中断请求。
CR:PCA计数器允许控制位。
  0:停止PCA计数,
  1:启动PCA计数
CCFn(n=0,1,2):PCA计数器溢出中断标志。
  当PCA模块发生匹配或者捕获时,硬件自动将此位置1,并向CPU提出中断请求。
注意:CF和CCFn共用一个中断号,所以需要通过CF和CCFn判断是由哪一个中断行为触发的。
   四个中断标志位均需要软件清零。

3、PCA模式寄存器(CMOD)
截图202604071607405670.jpg
CIDL:空闲模式下是否停止PCA计数。
  0:空闲模式下PCA继续计数
  1:空闲模式下PCA停止计数
CPS[2:0]:PCA计数脉冲源选择位
截图202604071608096999.jpg
ECF:PCA计数器溢出中断允许位。
  0:禁止PCA计数器溢出中断
  1:使能PCA计数器溢出中断

4、PCA计数器寄存器(CL,CH)
截图202604071608303210.jpg
PCA计数器是由CL和CH两个8位寄存器组合成的一个16位计数器,CL为低8位,CH为高8位。
计数器每个PCA时钟自动加1。

5、PCA模块模式控制寄存器(CCAPMn)
截图202604071609013547.jpg
ECOMn:允许PCA模块n的比较功能
CCAPPn:允许PCA模块n进行上升沿捕获
CCAPNn:允许PCA模块n进行下降沿捕获
MATn:允许PCA模块n的匹配功能
TOGn:允许PCA模块n的高速脉冲输出功能
PWMn:允许PCA模块n的PWM脉宽调制输出功能
ECCFn:允许PCA模块n的匹配/捕获中断

6、PCA模块模式捕获值/比较值寄存器(CCAPnL,CCAPnH)
截图202604071609263514.jpg
  • 当PCA模块捕获功能使能时,CCAPnL和CCAPnH用于保存发生捕获时的PCA的计数值(CL和CH);
  • 当PCA模块比较功能使能时,PCA控制器会将当前[CH:CL]中的计数值与保存在[CCAPnH:CCAPnL]中的值进行比较,并给出比较结果;
  • 当PCA模块匹配功能使能时,PCA控制器会将当前[CH:CL]中的计数值与保存在[CCAPnH:CCAPnL]中的值进行比较,看是否匹配(相等),并给出匹配结果。

7、PCA模块PWM模式控制寄存器(CCA_PWMn)
截图202604071609538774.jpg
EBSn[1:0]:PCA模块n的PWM位数选择
截图202604071610245054.jpg
XCCAPnH[1:0]:10位PWM的第9位和第10位的重载值
XCCAPnL[1:0]:10位PWM的第9位和第10位的比较值
EPCnH:PWM模式下,重载值的最高位
    8位PWM的第9位,7位PWM的第8位,6位PWM的第7位,10位PWM的第11位
EPCnL:PWM模式下,比较值的最高位
    8位PWM的第9位,7位PWM的第8位,6位PWM的第7位,10位PWM的第11位
注意:在更新10位PWM的重载值时,必须先写高两位XCCAPnH[1:0],再写低8位CCAPnH[7:0]。


能体会到发现一个不理解的现象然后找原因然后要么解决掉问题要么被问题解决掉的那种快乐是我的幸运
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:729
  • 最近打卡:2026-04-09 04:20:46

40

主题

466

回帖

4461

积分

荣誉版主

积分
4461
发表于 前天 16:16 | 显示全部楼层
三、PCA工作方式

PCA模块的工作核心是一个16位计数器一系列捕获、比较的机制
概括来说,PCA的工作方式就是两个环节:
  • 以计数器的计数值为基础。PCA的绝大部分功能都依赖于这个计数值。
  • 对目标信号跟计数值进行匹配/捕获,然后根据结果进行相应的输出。


1、计数环节
  通过置位PCA控制寄存器的CR位(CCON[6])启动PCA计数器之后,计数器使用指定的脉冲源,从设定的16位起始值[CH:CL]开始不断加1,直到达到最大值65535后溢出归零。这跟系统定时器的工作过程类似。
  脉冲源通过PCA模式寄存器的CPS位(CMOD[3:1])进行选择,包括:
  • 系统时钟(比如STC8G,根据需要可以配置为SYSclk/1,/2,/4,/6,/8,/12)
  • 定时器0(Timer0)
  • 外部计数脉冲源(通过ECI脚输入)

PCATimer0.jpg

PCA计数器的溢出有两种方式:
  • 从设定的起始值[CH:CL]开始计数,在超过16位最大值65535的时候溢出;
  • 计数器从0开始计数,当超过匹配/捕获值寄存器[CCAPnH:CCAPnL]设定的阈值的时候溢出。

每当PCA计数器溢出时,都会自动将溢出中断标志位(CF或者CCFn)置1。
  • 计数器溢出中断,由CMOD寄存器的ECF位(CMOD[0])开启/关闭,对应的中断标志位是CF(CCON[7]),需要手动清零。
  • 阈值溢出中断,由CCAPMn寄存器的ECCFn位(CCAPMn[0])开启/关闭,对应的标志位是CCFn(CCON[2:0]),需要手动清零。

如果使能了相应的溢出中断功能,则会触发中断。

截图202604071615565545.jpg

2、比较/捕获环节
  通过PCA模块模式控制寄存器(CCAPMn),可以开启PCA模块n的比较、匹配和捕获的功能。以及开启中断。
 (1)比较功能
 通过将ECOMn位(CCAPMn[6])和MATn位(CCAPMn[3])置1,可以使能PCA模块的比较和匹配功能。
 当比较功能使能时(ECOMn=1,MATn=1),PCA控制器会通过16位比较器,将当前的[CL:CH]的计数值与对应模块n的比较值[CCAPnH:CCAPnL]进行比较,并给出比较结果。
 如果比较结果相同,则匹配成功,会触发对应模块的匹配中断(CCFn置1,需手动清零)。
 从效果方面来看,PCA模块的比较功能其实也相当于一个定时器。



截图202604071618274571.jpg



 (2)捕获功能
  PCA的捕获模式主要用于精确捕获外部输入信号的边沿事件(如上升沿、下降沿或双边沿),并自动记录发生该事件时PCA计数器的当前值。

  通过CCAPPn位(CCAPMn[5])和CCAPNn位(CCAPMn[4])可以对捕获方式进行选择。
  • 将CCAPPn位置1,可以对外部CCPn管脚的输入信号的上升沿进行捕获;
  • 将CCAPNn位置1,可以对外部CCPn管脚的输入信号的下降沿进行捕获;
  • 也可以将CCAPPn、CCAPNn位都置1,可以对外部输入信号的上升沿和下降沿都进行捕获。


  当捕获功能使能时,对模块的外部管脚CCPn的输入跳变进行采样。
  此时[CCAPnH:CCAPnL]用于保存指定通道采样到有效跳变时的PCA的计数值[CH:CL]
  如果还开启了捕获中断(CCAPMn[0]:ECCFn=1),还会触发对应的捕获中断(CCFn被硬件置1,需手动清零)。

  捕获功能记录的是一个特定信号发生的确切时间点,因此可以通过多次记录的数值,实现跟速度频率有关的功能,比如测量速度、脉宽、频率等等。
  另外,由于可以通过输入脉冲的上升沿、下降沿或者双边沿触发中断,也可以作为外部中断功能的扩展,以弥补外部中断引脚的数量和功能不足的情况。
截图202604071621493001.jpg

1 喜欢他/她就送朵鲜花吧,赠人玫瑰,手有余香!
能体会到发现一个不理解的现象然后找原因然后要么解决掉问题要么被问题解决掉的那种快乐是我的幸运
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:729
  • 最近打卡:2026-04-09 04:20:46

40

主题

466

回帖

4461

积分

荣誉版主

积分
4461
发表于 前天 16:47 | 显示全部楼层

四、PCA都能干啥
  通过PCA模块的比较和捕获功能的搭配组合,可以实现很多实用的功能。

1、模拟一个软件定时器
  在学习这部分内容之前,先复习了一下单片机内部的定时器的知识。这样理解起来会更容易一些。因为传统的定时器其实也是一个计数器。
  通过PCA模块可以模拟出类似定时器的效果。之所以说“模拟”,是因为它本身并不是一个定时器,而是能够实现跟定时器一样的操作过程和定时效果。

使用PCA模块实现软件定时器功能有两种方式:
  • 第一种方式是通过PCA模块的计数器溢出中断,直接产生定时效果。
  • 第二种方式是使用PCA模块的比较值寄存器跟计数器结合产生定时效果

两种方式的区别在于:
  • 第一种方式是让计数器从设定的起始值开始计数,以16位最大值65535作为溢出上限;
  • 第二种方式则是让计数器从0开始计数,以比较/捕获值寄存器设定好的阈值作为溢出上限。

理解了这些区别,这两种方式实现起来也都比较简单。
===========================================、

(1)第一种方式:通过计数器溢出中断CF,产生定时效果。
 (1-1) 第一步,设置计数器的起始值
  根据CMOD[3:1]选择的PCA脉冲源的频率,可以计算出需要的定时时长的脉冲周期数
  所以,用16位计数器上限最大值65535减去这个脉冲周期数,就可以得到计数器的起始值。将这个值赋值给计数器寄存器[CH:CL]。
比如:
系统时钟频率:SYSclk = 24Mhz
时钟脉冲源频率:CMOD[3:1]=000  (SYSclk/12)
一个周期的时长:1000000μs/(24000000/12) = 0.5μs
最大定时时长:0.5μs*65535 = 32768μs = 32.768ms
定时1ms的脉冲数:1000μs/0.5μs = 2000
所以定时1ms的计数器起始值[CH:CL]就是65535-2000=63535=0xF82F
截图202604071629314993.jpg
再比如:
系统时钟频率:SYSclk = 30Mhz
时钟脉冲源频率:CMOD[3:1]=100  (SYSclk/1)
一个周期的时长:1000000μs/30000000 = 0.033μs(1/30)
最大定时时长:0.033μs*65535 = 2184.5μs = 2.1845ms
定时1ms的脉冲数:1000μs/0.033μs = 30000
所以定时1ms的计数器起始值[CH:CL]就是65535-30000=62535=0xF447
截图202604071630362014.jpg

 (1-2) 第二步,启动计数器
  通过将PCA控制寄存器CCON[6]的CR位写1来启动计数器。(记得EA=1打开总中断)
  计数器启动之后,从设定的初始值[CH:CL]开始加1。

 (1-3) 第三步,处理计数器溢出中断事件
  当PCA计数器[CH:CL]的值超过16位最大值65535后,就会自动将溢出中断标志位CF置1,并触发溢出中断。此时就可以在中断函数中编写需要的操作。

*关键事项:
  • 因为[CH:CL]计数器在每次溢出溢出之后,会自动从0开始重新计数,而不会自动重载起始值,所以需要在中断事件函数中重新设置[CH:CL]的起始值。
  • 编写触发中断后要做的工作。(比如count++)
  • CF标志需手动清零(CF=0),防止程序重复进入中断。

*思考问题:
 如果在中断函数中没有将CF标志位清零会怎样?
  • CF标志位被硬件置位之后,只能由软件清零,硬件不会自动复位;
  • 在PCA计数器第一次溢出之后,若中断函数中未执行CF=0,退出中断函数后CF仍保持为1。之后CPU每过一个系统时钟周期,都会重复检测一次溢出标志,并且发现CF=1且溢出中断已使能,就会持续重新进入这个中断,从而形成中断的“死循环”!
  • 重新进入中断的时间间隔,并不是按照PCA设定的脉冲时钟频率,而是按照系统时钟的频率进入。比如SYSclk=24MHz,PCA脉冲频率=SYSclk/12,那么重复进入中断的间隔就是系统时钟的大约41.67ns,而非12分频之后的500ns,重复检测的间隔被“系统时钟”锁定了,和PCA的分频设置彻底无关了。

**实验验证:
{实验程序1}
系统主频SYSclk=24MHz
PCA脉冲源选择SYSclk/12(CPS[2:0]=000),所以一个脉冲周期为0.5μs。
在中断函数中
 重新装填计数器初始值[CH:CL]为65535-2000(1ms为2000个脉冲周期);
 给全局变量IsrCount++,统计进入中断的次数;
 CF=0清除溢出标志位。
在main函数的while(1)循环里
 判断如果IsrCount的值大于等于1000(达到1000ms),则IsrCount=0;
 并且Led = !Led; 让LED端口电平翻转。
实验现象1:LED亮1s灭1s循环闪烁。

现将中断函数中的CF=0注释掉,再次运行程序。
实验现象2:LED依然在闪烁,但是闪烁频率明显变快了

重新编写一个闪灯程序,
 在while(1)循环中,使用ISP生成的微秒延时函数每隔41700μs执行一次Led=!Led;
实验现象3:LED闪烁频率跟实验现象2基本一致

结果证明:如果没有及时将CF标志位清零,CPU会按照系统时钟频率反复检测标志位,并且重复进入该中断。

==========================================

(2)第二种方式:通过计数器和比较值寄存器的值匹配产生中断,现定时器效果。
  STC8G单片机的PCA包含了三个通道:CCP0、CCP1和CCP2。
  每个通道都有一组16位的捕获/比较寄存器[CCAPnH:CCAPnL]。这个寄存器的值就是触发比较中断的计数阈值。n代表启用通道编号(0,1,2)。

 (2-1) 第一步,设置计数器寄存器[CH:CL]的起始值
  让起始值[CH:CL]=0,让计数器从0开始累加。
  累加的速度由脉冲源的频率决定。比如跟前面一样,将CMOD寄存器的CPS[2:0]三个位设置为000,使用系统频率的1/12,每个脉冲周期为0.5μs。

 (2-2) 第二步,设置比较值寄存器[CCAPnH:CCAPnL]的值
  根据CMOD[3:1]选择的PCA脉冲源的频率,可以计算出需要的定时时长的脉冲周期数。
  将这个周期数赋值给寄存器[CCAP0H:CCAP0L],作为计数器溢出上限的阈值。
  在计数器计数过程中,这个寄存器值不会被硬件修改,只能手动设置。所以如果后续不需要改变,就只需要在初始化的时候设置一次就可以了。

 (2-3) 第三步,配置模式控制寄存器CCAPMn
  置位ECOMn位(CCAPMn[6]),使能CCPn的16位比较器;
  置位MATn位(CCAPMn[3]),开启CCPn的匹配功能。
  置位ECCFn位(CCAPMn[0]),开启CCPn的匹配中断。

  比如要让通道0每1ms触发一次中断,需要2000个脉冲周期,就让[CCAP0H:CCAP0L]的值等于2000。
  而且因为计数器计数值每次可能达不到最大值,所以可以不开启计数器溢出中断ECF。


  PCA计数器每次计数,都会将[CH:CL]的值跟[CCAPnH:CCAPnL]设定的值进行比较,如果两个值相等(匹配),就会触发该通道的匹配中断(CCFn)。
  利用这一机制,就可以实现软件定时器的效果。

 (2-4) 第四步,启动PCA计数器
  通过置位PCA控制寄存器的CR位(CCON[6])来启动PCA计数器。(记得EA=1打开总中断)
  计数器启动之后,计数器寄存器[CH:CL]从设定的初始值0开始累加。

 (2-5) 第五步,处理中断事件
  在[CH:CL]累加的过程中,会跟比较值寄存器[CCAPnH:CCAPnL]的值进行比较。当[CH:CL]的值跟[CCAPnH:CCAPnL]的值相等时,PCA模块会置位相应通道的中断标志位CCFn,并触发PCA中断。
  由于PCA模块的CF中断和CCFn中断共用同一个中断向量号(7),所以进入中断后,需要对CCFn标志位的值进行判断,来确定是否是所用通道产生的中断。如果判断CCFn的值是1,则进入中断事件的代码。
  进入中断后,[CH:CL]的值会继续累加。所以首先需要在中断函数里将[CH:CL]手动复位成0,重新开始计数;
  然后将该中断标志位CCFn置0,防止中断函数重复进入该中断事件;
  同时可以加入进入中断后需要的操作功能代码。

**实验验证:
{实验程序2}
系统主频SYSclk=24MHz;
PCA脉冲源选择SYSclk/12(CPS[2:0]=000),所以一个脉冲周期为0.5μs;
初始化计数器[CH:CL]的值为0;
初始化比较值[CCAPnH:CCAPnL]的值为2000(定时1ms的周期数);
LED端口初始值1,点亮灯珠。
在中断函数中
 重新装填计数器初始值[CH:CL]为0;
 通过对全局变量IsrCount++; 进行累加,记录进入中断的次数;
 CCF0=0清除比较中断标志位。
在main函数的while(1)循环里
 判断如果IsrCount的值大于1000(一次中断1ms,1000次就是1s),则执行Led =!Led; 让LED端口的电平翻转;
 并且将IsrCount=0; 将统计的次数清零。
实验现象:LED亮1s灭1s循环闪烁。

*思考问题:
 如果没有将CCF0=0清除比较标志位,会发生什么?
  首先,[CH:CL]的值会反复置0,永远达不到[CCAPnH:CCAPnL]设置的值;
  同时IsrCount的值会以系统时钟的频率累加,每记录1000次,仅经过了41.67ns×1000= 41.67ms,灯珠闪烁的频率约等于12KHz,远高于人眼对闪烁感知的临界值(80~100Hz左右),所以肉眼完全看不出灯珠闪烁,几乎就是“常亮”状态了。


能体会到发现一个不理解的现象然后找原因然后要么解决掉问题要么被问题解决掉的那种快乐是我的幸运
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:729
  • 最近打卡:2026-04-09 04:20:46

40

主题

466

回帖

4461

积分

荣誉版主

积分
4461
发表于 前天 17:00 | 显示全部楼层
四、PCA都能干啥

2、高速脉冲输出
  在前面的软件定时器的学习中,通过置位ECOMn位(CCAPMn[6])和MATn位(CCAPMn[3]),使用PCA模块的比较和匹配功能产生指定时间长度的中断,实现了软件定时器的效果。
  与此同时,如果将TOGn位(CCAPMn[2])置1,当PCA计数器的计数值[CH:CL]与模块比较寄存器[CCAPnH:CCAPnL]的值相匹配时,PCA模块的输出端口CCPn的输出电平也将触发翻转。
截图202604071650111913.jpg

  因为通过P_SW1寄存器的PCA功能脚切换位(P_SW1[5:4]),可以选择使用第几组PCA模块,
截图202604071651118943.jpg
  所以其中的CCPn(n=0,1,2)就是对应模块的对外输出引脚。

使CCPn输出电平的产生翻转的过程由两个环节构成:
  • 启动PCA计数器之后,[CH:CL]从0开始向上计数,并将[CH:CL]的值与[CCAPnH:CCAPnL]的值进行比较;
  • 当开启了高速脉冲输出模式(MATn=1)时,如果[CH:CL]与[CCAPnH:CCAPnL]的值相匹配,就会立即触发CCPn的电平翻转。如果开启了匹配中断功能(ECCFn=1),还会同时触发匹配中断(CCFn被硬件置1后,需软件清零)。

这个【计数 → 比较/匹配 → 电平翻转】的过程,就实现了脉冲输出的效果。

  在这个过程中,电平翻转的阈值[CCAPnH:CCAPnL]如果不需要改变,可以在初始化的时候预先配置好。也可以在程序中重新写入新的阈值,改变脉冲的宽度。
而计数器寄存器[CH:CL]的值,在触发中断之后会继续计数。所以在每次触发中断之后需要手动清零。
  由于输出的脉冲周期最快可以等于一个系统时钟周期,从而输出的脉冲频率可以达到几十MHz。这个速度在脉冲速度的定义范围里属于高速脉冲,所以可以叫做“高速脉冲输出”。

*关键事项
(1)为什么(CCAPnL, CCAPnH)必须先写低位再写高位?
  在芯片手册的【高速脉冲输出模式结构图】里有红字标注的“必须先写CCAPnL”,强调在配置比较寄存器的时候必须先写低位再写高位。
截图202604071655252405.jpg
  原因的本质是关系到比较器使能位(ECOMn)相关的一些状态控制机制
  • 先写低位CCAPnL时,ECOMn会被置0,关闭比较器,数值只会存在缓冲里,不会立刻同步到比较器;
  • 写完高位CCAPnH的瞬间,硬件会一次性把「低位 + 高位」完整载入16位比较器,并且将ECOMn位重新置1,使能比较器;
  • 反过来:如果先写高位、再写低位,ECOMn可能处于未预期的状态,导致无法正确使用写入的数据,可能导致使PWM输出异常或定时器功能失效。



  因此,先写低位再写高位,可以保证寄存器数据的原子更新。

  PCA的这个赋值顺序的要求,在定时器、串口、ADC、PWM等外设中也同样有效:
  • 定时器:例程中可以看到,装载过程都是先写TL后写TH,保证原子更新;
  • UART波特率发生器:16位重装值先写TL再写TH,保证原子更新;
  • ADC结果寄存器:先读ADC_RESL,再读ADC_RES,可以避免转换过程中数值跳变;
  • PWM 占空比寄存器:先写低位,再写高位,保证占空比一次性更新,无毛刺


(2)给CCAPnL和CCAPnH寄存器赋值的操作对ECOMn位有什么影响?
  基于上一个问题,在程序中分别给CCAPnL和CCAPnH赋值,同时读取ECOMn位的状态并赋值给LED,观察LED的亮灭状态:
  • 只给CCAPnL赋值,不给CCAPnH赋值,此时ECOMn位不论之前是否置1,都会被置0;
  • 不给CCAPnL赋值,只给CCAPnH赋值,此时ECOMn位不论之前是否置0,都会被置1;

  单片机的这个机制,猜测一是为了提高PCA的工作效率,二是避免忘记开启比较功能的误操作。但是也要重视这一机制,防止阈值赋值错误导致时序混乱。

**实验验证:
{实验程序3}
系统主频SYSclk=24MHz
在PCA配置代码中
 PCA脉冲源选择SYSclk/12(CPS[2:0]=000),所以一个脉冲周期为0.5μs;
 使能通道0的比较功能ECOM0=1;
 使能通道0的匹配功能MAT0=1;
 使能高速脉冲输出功能TOG0=1;
 设置溢出阈值[CCAP0H:CCAP0L]为2000(1ms中断一次);
 PCA计数器[CH:CL]清零;
 启动计数器CR=1。
在中断函数中
 重新装填计数器初始值[CH:CL]为0;
 给全局变量IsrCount++,统计进入中断的次数;
 清除匹配溢出标志CCF0=0。
在main函数的while(1)循环里
 判断如果IsrCount的值达到1000(1000ms),则IsrCount=0;
 让LED端口电平翻转Led = !Led。
实验现象:LED亮1s灭1s循环闪烁。


能体会到发现一个不理解的现象然后找原因然后要么解决掉问题要么被问题解决掉的那种快乐是我的幸运
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:729
  • 最近打卡:2026-04-09 04:20:46

40

主题

466

回帖

4461

积分

荣誉版主

积分
4461
发表于 前天 17:09 | 显示全部楼层
四、PCA都能干啥

3、捕获外部电平跳变
  PCA模块除了跟定时器有关的功能之外,还具有捕获外部信号的功能。
  通过P_SW1寄存器的PCA功能脚切换位(P_SW1[5:4]),可以选择使用哪一组PCA模块。
截图202604071702351306.jpg
  其中的CCP0、CCP1、CCP2就是对应模块的外部输入引脚

  启用PCA模块的捕获功能,需要配置ECOMn寄存器的CCAPPn位(ECOMn[5])CCAPNn位(ECOMn[4])
  启用捕获功能之后,就可以检测PCA模块的外部输入引脚CCPn的电平跳变。

可以捕获的电平跳变方式有三种:
  • 当CCAPPn位置1时,捕获电平上升沿的跳变;
  • 当CCAPNn位置1时,捕获电平下降沿的跳变;
  • 当CCAPPn位和CCAPNn位都置1时,可以同时捕获上升沿和下降沿两种跳变。

当外部引脚(CCPn)检测到配置好的电平跳变(上升沿、下降沿或双边沿)时:
  • PCA自动将当前计数器的值(CH:CL)锁存到对应的捕获寄存器[CCAPnH:CCAPnL]中;
  • 同时置位该通道的捕获标志(CCFn=1);
  • 若使能中断,则触发中断请求,通知CPU有事件发生。
  • 此过程由PCA硬件完成,无需CPU干预,确保高精度和实时性。

使用捕获功能需要以下步骤:
  • 选择脉冲源(可选)
  通过 CMOD 寄存器的CPS[2:0]位设置PCA计数器脉冲源(系统时钟、定时器0、ECI引脚输入)。
  • 选择捕获模式
  设置对应通道的CCAPMn寄存器的CCAPPn、CCAPNn,选择捕获模式。
  • 连接外部信号
  将待测信号接入PCA捕获引脚(CCPn)。
  • 启动PCA计数器(可选)
  设置CR=1启动计数器。
  • 使能捕获中断
  设置ECCFn=1使能捕获中断。
  • 处理中断事件
  在中断服务程序中完成需要的操作。比如对中断事件的响应,或者读取捕获值[CCAPnH:CCAPnL]的值;并清除捕获中断标志CCFn。
=================================================

通过捕获功能可以实现一些跟电平跳变相关的功能。
(1) 作为外部中断功能的扩展
 (1-1) 第一步,选择输入引脚
   通过P_SW1寄存器的CCP_S位,切换PCA模块,从而选定了CCPn输入引脚。
 (1-2) 第二步,选择捕获边沿
   通过ECOMn寄存器的CCAPPn位(ECOMn[5])和CCAPNn位(ECOMn[4])选择上升沿、下降沿或者双边沿触发捕获中断。
 (1-3) 第三步,处理中断事件
   触发中断时,对应的CCPn也会被置1。比如CCP2捕获到有效电平,则CCF2被硬件置1。
 在中断服务程序中,根据CCFn判断为需要的中断之后,即可完成需要的操作,比如切换LED亮灭状态等。最后清除捕获中断标志CCFn,防止重复进入该中断。

*关键事项:
由于捕获功能不依赖于PCA模块的计数器,所以单纯用来捕获外部中断时,可以不用开启PCA计数器以及溢出中断等。

**实验验证:
{实验程序4}
系统主频SYSclk=24MHz。
在PCA模块初始化函数中
 配置P3.7引脚位准双向口模式,并开启内部上拉电阻;
 使能CCP2通道捕获外部下降沿CCAPP2(CCAPM2[5])=0; CCAPN2(CCAPM2[4])=1;
在中断函数中
 判断CCF2=1时,让LED1端口的电平翻转,并清空中断标志CCF2=0。
实验现象1:每次按下连接在P3.7端口的按键,LED1会进行亮灭切换。

然后将CCP1通道配置成比较匹配模式的软件定时器
在PCA模块初始化函数中
 添加PCA脉冲源选择SYSclk/12(CPS[2:0]=000),一个脉冲周期为0.5μs。
 添加代码将ECOM1、MAT1、ECCF1位置1
 添加CCAP1的比较阈值[CCAP]为2000个脉冲(1ms)
在中断函数中
 添加判断CCF1=1时,将计数器[CH:CL]清零,IsrCount++,CCF1=0;
在main函数的while(1)循环中
 每当IsrCount=1000(1s),LED2端口的电平翻转,并将IsrCount清零。
实验现象2:LED2每1s亮/灭一次;每次按下P3.7端口的按键,LED1会进行亮灭切换。


(2) 测量外部脉冲
  在开启PCA模块的捕获外部信号之后,如果同时开启了PCA计数器,那么每次检测到有效的电平跳变,模块都会将当时的计数值[CH:CL]自动写入捕获值寄存器[CCAPnH:CCAPnL]。
  此时可以在捕获中断的函数中,将捕获寄存器的值读取出来。经过多次捕获,将记录下的脉冲数进行对比,可以换算出每个电平跳变的时间间隔,从而计算出信号的频率。
  还可以在中断函数中记录触发中断的次数,从而得到外部输入的脉冲数。

*注意事项
  • 在中断函数中必须手动清除CCFn标志,否则会重复进入中断;
  • 在中断或主循环中读取捕获值[CCAPnH:CCAPnL]的时候,必须先读低字节(CCAPnL),再读高字节(CCAPnH),以确保数据一致性。
  • 如果信号频率过高,需确保PCA时钟足够快,避免漏捕;

*应用场景
  • 频率/周期测量:记录两个连续上升沿或者下降沿之间的时间差;
  • PWM脉宽测量:捕获上升沿和下降沿,计算一个周期内的高电平持续时间;
  • 编码器接口:捕获正交脉冲的边沿,实现位置/速度检测;
  • 超声波测距:捕获回波信号的起始和结束时刻,计算超声波飞行时间。


**暂无实验



能体会到发现一个不理解的现象然后找原因然后要么解决掉问题要么被问题解决掉的那种快乐是我的幸运
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:729
  • 最近打卡:2026-04-09 04:20:46

40

主题

466

回帖

4461

积分

荣誉版主

积分
4461
发表于 前天 17:20 | 显示全部楼层
四、PCA都能干啥

4、PWM输出
  PWM脉宽调制是使用程序来控制波形的占空比、周期、相位的一种技术。

PWM波形可以通过多种方式产生:
  • 通过软件延时函数控制高低电平输出的时间,从而达到不同的PWM周期和占空比;
  • 通过定时器实现;
  • 通过PCA模块实现。

用PCA模块实现PWM输出功能,是将软件定时器、高速脉冲输出等功能结合得到的高级用法。

(1) 通过PCA计数器溢出时间实现PWM
  通过给PCA计数器[CH:CL]装填不同的起始值,在对应的溢出中断函数中对指定端口输出的电平进行翻转,实现输出的高低电平的不同时长,达到PWM的效果。

**实验验证:
{实验程序5}
系统主频SYSclk=24MHz。
PCA脉冲源选择SYSclk/12(CPS[2:0]=000),所以一个脉冲周期为0.5μs。
定义PWM占空比数值PWM_Duty,数值范围是0~1。
定义PWM脉宽PWM_PulseWidth为20000(10ms)
定义LedState为LED亮灭状态(1-亮,0-灭)
在CF溢出中断函数中
 翻转LedState的状态,LedState=!LedState;
 当LedState=1的时候,将高电平持续时长赋值给PCA计数器
   [CH:CL] = 65535-(u16)(PWM_PulseWidth * PWM_Duty);
 当LedState=0的时候,将低电平持续时长赋值给PCA计数器
   [CH:CL] = 65535-(u16)(PWM_PulseWidth * (1-PWM_Duty));
 让LED端口的电平状态等于LedState。
通过按键KeyDown和KeyUP改变PWM_Duty的值,每次变化0.1
实验现象:点击KeyDown按键,LED逐渐变暗;点击KeyUP按键,LED逐渐变亮。
=======================================

(2) 通过PCA计数器溢出中断次数实现PWM
  设定在一个脉宽之内占空比高电平占用的脉冲次数作为分界,通过PCA计数器进入溢出中断的次数,低于分界输出高电平,否则输出低电平,实现输出的高低电平的不同时长,达到PWM的效果。

**实验验证:
{实验程序6}
系统主频SYSclk=24MHz。
PCA脉冲源选择SYSclk/12(CPS[2:0]=000),所以一个脉冲周期为0.5μs。
定义PWM占空比数值PWM_Duty,数值范围是0~100。
定义PCA脉宽PCA_PulseWidth为200(0.1ms)。
定义IsrCount记录进入中断的次数。
定义LedState为LED亮灭状态(1-亮,0-灭)。
在CF溢出中断函数中
 切换LedState的状态,LedState=!LedState;
 重新装填[CH:CL]起始值(65535-PWM_PulseWidth);
 IsrCount++,累加进入中断次数;
 清除中断标记CF=0。
在main函数的while(1)循环中
 如果IsrCount的值大于100,则清零;
 如果IsrCount的值小于占空比PWM_Duty的值,则LedState=1;
 如果IsrCount的值大于等于占空比PWM_Duty的值,则LedState=0;
 让LED端口电平等于LedState。
通过定时器0中断
 每隔1ms检测一次KeyDown和KeyUP按键是否按下;
 通过两个按键改变占空比PWM_Duty的值,每次改变10。
实验现象:点击KeyDown按键,LED逐渐变暗;点击KeyUP按键,LED逐渐变亮。
=====================================

(3) 通过PCA软件定时器实现PWM
  将一个脉宽之内高电平和低电平占用的脉冲数,分别对比较值寄存器[CCAPnH:CCAPnL]进行赋值,通过不同时长的匹配溢出中断,翻转端口输出的电平状态,实现PWM功能。

**实验验证:
{实验程序7}
系统主频SYSclk=24MHz。
PCA脉冲源选择SYSclk/12(CPS[2:0]=000),所以一个脉冲周期为0.5μs。
定义PWM占空比数值PWM_Duty,数值范围是0~1。
定义PWM脉宽PWM_PulseWidth为20000(10ms)
定义CCP0_THOLD_1为高电平持续时间阈值
定义CCP0_THOLD_0为低电平持续时间阈值
定义LedState为LED亮灭状态(1-亮,0-灭),初始化LedState=1。
在PCA初始化函数中
 PCA计数器起始值[CH:CL]为0。
 配置CCAPM0寄存器,启用ECOM0、MAT0和ECCF0。
 启动PCA计数器。
在CCF0中断函数中
 将比较值寄存器[CH:CL]清零。
 翻转LedState的状态,LedState=!LedState;
 当LedState=1的时候,将高电平持续时长的阈值赋值给比较值寄存器
   [CCAP0H:CCAP0L] = CCP0_THOLD_1;
 当LedState=0的时候,将低电平持续时长的阈值赋值给比较值寄存器
   [CCAP0H:CCAP0L] = CCP0_THOLD_0;
 让LED端口的电平状态等于LedState。
通过按键KeyDown和KeyUP改变PWM_Duty的值,每次变化0.1
实验现象:点击KeyDown按键,LED逐渐变暗;点击KeyUP按键,LED逐渐变亮。

**关键事项:
 由于PCA比较寄存器与计数器同步机制的硬件边界原因,当PCA模块的计数器在溢出到归零的瞬间,会因硬件延迟产生一个极窄的电平脉冲(通常为1~2个系统时钟周期)。
 因此当比较值处在溢出阈值时,会在溢出→归零的瞬间,产生非法的电平毛刺。
  • 当 PWM_Duty = 0    → CCAP0 = 0x0000 → 硬件毛刺 → LED闪一下亮
  • 当 PWM_Duty = 100% → CCAP0 = 0xFFFF → 硬件毛刺 → LED闪一下灭

体现在LED上就是输出的电平被每个周期的微弱打断,从而形成闪烁。

有几种解决方案:
  • 当PWM_Duty在上下边界时,使用判断语句强制State=1或State=0。
  • 将PWM_Duty的数值范围的上下限向内偏移一点儿,避开0%和100%的边界。(比如占空比是0~255,就把边界设置成1和254)


=========================================

(4) 通过PCA模块的PWM脉宽调制模式实现PWM输出
  前面几种方式都是通过繁琐的配置和判断来实现PWM输出;除此之外PCA模块还自带了PWM输出的功能,可以输出6位/7位/8位/10位的PWM信号。
  这是一种高效、低CPU占用的硬件级脉宽调制方案,更加适合特别适合LED调光、电机控制等实时性要求高的场景。

脑子不大够用了,先潜水继续啃资料了。。。。。。

。。。。。。
。。。。。
。。。。
。。。
。。





能体会到发现一个不理解的现象然后找原因然后要么解决掉问题要么被问题解决掉的那种快乐是我的幸运
回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:729
  • 最近打卡:2026-04-09 04:20:46

40

主题

466

回帖

4461

积分

荣誉版主

积分
4461
发表于 5 小时前 | 显示全部楼层
补充笔记


1、关于用定时器0作为PCA模块的输入脉冲源时的定时器中断问题
  在学习使用定时器0作为PCA模块的时钟源时,由于手册和ISP里都没有相关的例程和说明,几位老师的教程视频也都没有提及具体操作,所以自己进行了尝试。
一开始是直接用了一个之前用过的定时器0初始化函数
  1. void Timer0_Init(void) //1毫秒
  2. {
  3.     u16 T0_RELOAD = (65536 - (float)SYSCLK / 12 * 1 / 1000);  //定时周期1毫秒
  4.     AUXR &= 0x7F;   //设置定时器0 时钟12T模式
  5.     TMOD &= 0xF0;  //设置定时器0模式,16位自动重载
  6.     TL0 = (u8)T0_RELOAD & 0xFF;  //设置定时初始值低8位
  7.     TH0 = (u8)(T0_RELOAD>>8) & 0xFF;  //设置定时初始值高8位
  8.     TF0 = 0;  //清除TF0标志
  9.     TR0 = 1;  //定时器0开始计时
  10.     ET0 = 1;  //启用定时器中断
  11. }
复制代码

并将PCA模块的脉冲源设置成“定时器0的溢出脉冲”(CPS[2:0]=010)
  结果发现PCA并不能正常工作

然后尝试设置定时器0和PCA的中断优先级
  将PCA的中断优先级设置成高于定时器0的中断优先级,
  结果PCA依旧不正常工作。

最后尝试将最后那行:定时器0的中断关闭(ET0=0
  然后PCA终于正常工作了!

于是先把前天发的CPS列表的010一行做一下补充

然后分析了一下前面代码错误的原因
  因为手册里明确写了“定时器0的溢出脉冲”,但没有更多相关叙述,所以一开始误以为也需要定时器中断来实现。
  但是查阅了一下这个脉冲触发计数的过程:
   PCA 计数脉冲 = 定时器0 的“溢出信号”
    当定时器0用作PCA脉冲源时,溢出信号直接由硬件送给PCA,不需要软件处理中断
    也就是说PCA模块每捕捉到一次 Timer0 溢出,PCA计数器+1;
    这个溢出信号是硬件自动触发的,不需要 CPU 干预;
    更不需要进入Timer0中断服务函数。
   如果打开了定时器0中断(ET0=1),则会发生致命问题:
    Timer0溢出会被中断服务函数抢走。
    Timer0一溢出,CPU立刻跳去执行Timer0中断,
    PCA还没来得及捕捉这个溢出信号,就被中断打断了,于是导致PCA计数不准
  设置中断优先级也不会起作用。
所以当定时器0用作PCA脉冲源时必须关闭定时器0的中断!

**实验验证:
{实验程序8}
系统主频SYSclk=24MHz;
配置定时器0位12T模式,16为自动重载,溢出周期1ms;
在PCA配置函数中
 PCA脉冲源选择定时器0溢出脉冲(CPS[2:0]=010);
 配置PCA通道0位比较匹配模式(ECOM0=1, MAT0=1);
 初始化比较值[CCAPnH:CCAPnL]的值为500(500ms);
 初始化计数器[CH:CL]的值为0;
 启动PCA计数器(CR=1)。
在中断函数中
 重新装填计数器初始值(CL=0; CH=0;);
 将LED端口电平进行翻转(Led=!Led;);
 清除比较中断标志位(CCF0=0)。
实验现象1:LED每秒闪烁一次。

然后尝试在定时器配置函数里添加使能定时器中断的语句(ET0=1)。
实验现象2:LED上电快速闪烁一下之后不再闪烁。


**继续思考
在PCA中断函数中,对[CH:CL]是先清零还是后清零?
  从查阅网上的例程时看到,有些是把(CL=0;CH=0)放在中断函数的最后,也就是先清空标志位,再运行中断事件语句,最后将计数器清零
从中断函数运行的时间轴来看


先清零的过程:
  匹配中断清零CL/CH清除CCF0 →  LED翻转  → 中断返回
这个过程中,计数器[CH:CL]的值是
   500     →        0        →     0+Δ1   → 0+Δ1+Δ2从0+Δ1+Δ2继续计数

后清零的过程:
  匹配中断 →   清除CCF0   →    LED翻转    → 清零CL/CH  → 中断返回
这个过程中,PCA计数器的值是
   500     →     500+Δ1    → 500+Δ1+Δ2 →         0        → 从0开始计数

可以发现,由于中断函数里的每步操作,都必须消耗一定的系统时钟周期,所以后清零方式的计数过程中,在两次清零之间会多出若干个时钟周期的累积(
Δ1+Δ2),从而会产生计时的误差


====================================

2、顺便复习一下寄存器寻址
  在尝试设置PCA和定时器0优先级的时候注意到一件事情:
  两个优先级的低位都可以直接使用“PPCA”和“PT0”进行赋值;
  而高位“PPCAH”和“PT0H”却在头文件里没有定义,不能直接使用。

原因是:
  “PPCA”和“PT0”都属于中断优先级控制寄存器 IP,地址是 B8H
  而“PPCAH”和“PT0H”都属于高中断优先级控制寄存器 IPH,地址是 B7H

在芯片手册里明确注明了“注意:寄存器地址能够被8整除的才可进行位寻址,不能被8整除的则不可位寻址
截图202604091205307946.jpg

B8H /8 = 184 / 8 = 23  (可以整除)
B7H /8 = 183 / 8 = 22……7(不可整除)

所以在头文件里只有“PPCA”和“PT0”可以通过sbit进行声明。
而“PPCAH”和“PT0H”只能通过直接通过 IPH 进行设置,或者用其它方式自行定义。

这时候就可以模仿一下AiCube生成的设置优先级的代码,写一个比处理了。
  1. #define TIMER0_SetIntPriority(n)   PT0  = ((n)&1); \
  2.                                    IPH &= 0xFD;    \
  3.                                    IPH |= ((n>>1)<<1);
复制代码
  1. #define PCA_SetIntPriority(n)      PPCA = ((n)&1); \
  2.                                    IPH &= 0x7F;    \
  3.                                    IPH |= (((n>>1)<<1)<<7);
复制代码


顺便,PCA脉冲源也可以用这样的方式进行选择:

  1. #define PCA_SetClockSource(n)      CMOD &= 0xF1; \
  2.                                     CMOD |= (n)<<1;
  3. #define PCA_CLK_SYSCLKD12()        PCA_SetClockSource(0)
  4. #define PCA_CLK_SYSCLKD2()         PCA_SetClockSource(1)
  5. #define PCA_CLK_TIMER0OV()         PCA_SetClockSource(2)
  6. #define PCA_CLK_ECI()              PCA_SetClockSource(3)
  7. #define PCA_CLK_SYSCLK()           PCA_SetClockSource(4)
  8. #define PCA_CLK_SYSCLKD4()         PCA_SetClockSource(5)
  9. #define PCA_CLK_SYSCLKD6()         PCA_SetClockSource(6)
  10. #define PCA_CLK_SYSCLKD8()         PCA_SetClockSource(7)
复制代码



其实整个过程挺坎坷的。。。
一开始是用前面的SYSclk/12作为脉冲源的闪灯程序,
忘记了一个脉冲是1μs,1000个脉冲中断一次,1000次中断翻转一次LED端口的电平,
结果就是切换成定时器0作为脉冲源之后,始终看不到LED闪烁


就在这一个小疏忽上折腾了好久,最后重新梳理思路的时候才发现了这个乌龙。
定时器0设定的是1ms溢出一次,PCA计数器计数1000次也就是1s中断一次,LED又等了1000个中断(1000秒=16.7分钟!!!才会闪烁,能看到闪烁才有鬼了。。。。




能体会到发现一个不理解的现象然后找原因然后要么解决掉问题要么被问题解决掉的那种快乐是我的幸运
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-4-9 18:07 , Processed in 0.122186 second(s), 76 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表