33、基于CCP模块的自行车速度测量程序解析

基于CCP模块的自行车速度测量程序解析

1. CCP模块概述

CCP(Capture/Compare/PWM)模块有三个主要功能,在之前的应用中,我们使用了其PWM(脉冲宽度调制)功能来控制直流电机的速度。而在本次应用中,我们将聚焦于该模块的另外两个功能:捕获(Capture)和比较(Compare)功能。

捕获模式可以用于短时间内事件的精确计时。捕获的内容是Timer1或Timer3的当前计数值,并将其加载到CCPRX寄存器中。捕获操作会在CCP引脚发生以下四种特定动作之一时触发:
- 输入从高电平变为低电平(下降沿)
- 输入从低电平变为高电平(上升沿)
- 每第4个上升沿
- 每第16个上升沿

捕获操作会读取定时器中的16位值,并将其存储在由CCPRXH和CCPRXL组成的CCPRX寄存器对中。由于PIC中有两个CCP模块,“X”会被替换为“1”或“2”。在本次程序中,我们使用CCP2模块,当CCP2引脚发生事件时,定时器的当前值将被加载到CCPR2寄存器中。CCPR2寄存器可以拆分为两个字节:
- 高字节CCPR2H存储第15位到第8位
- 低字节CCPR2L存储第7位到第0位

我们需要通过定时器3控制寄存器T3CON的第6位和第3位来告知PIC使用哪个定时器作为捕获源,具体设置如下表所示:
| Bit 6 (T3CCP2) | Bit 3 (TCCP1) | 设置 |
| ---- | ---- | ---- |
| 0 | 0 | Timer1是CCP1和CCP2的捕获/比较源 |
| 0 | 1 | Timer3是CCP2的捕获/比较源,Timer1是CCP1的捕获/比较源 |
| 1 | 0 | Timer3是CCP1和CCP2的捕获/比较源 |
| 1 | 1 | 未提及具体功能 |

同时,我们还需要通过CCP控制寄存器CCPXCON的第3、2、1和0位来设置触发捕获的事件,具体设置如下表所示:
| Bit 3 | Bit 2 | Bit 1 | Bit 0 | 设置 |
| ---- | ---- | ---- | ---- | ---- |
| 0 | 1 | 0 | 0 | 每个下降沿 |
| 0 | 1 | 0 | 1 | 每个上升沿 |
| 0 | 1 | 1 | 0 | 每第4个上升沿 |
| 0 | 1 | 1 | 1 | 每第16个上升沿 |

2. 自行车速度测量程序算法

在本次自行车速度测量程序中,我们使用Timer3作为捕获源。车轮上会安装一个传感器,车轮每旋转一圈,传感器会输出一个高电平然后变为低电平的脉冲信号。程序的基本原理如下:
1. 将Timer3清零。
2. 当第一个事件发生时,捕获Timer3的值并将其记为count1。
3. 当第二个事件发生时,再次捕获Timer3的值并将其记为count2。
4. 通过计算count2与count1的差值来得到车轮旋转一圈的时间周期。
5. 重复上述步骤,每次都将Timer3和count1、count2清零。

3. 自行车速度计算与定时器频率设置

假设自行车车轮的半径为14英寸,根据圆的周长公式$C = 2\pi r$,可以计算出车轮的周长为:
$C = 2\times3.14286\times14 = 88$英寸

如果自行车的平均速度为15英里/小时,我们可以计算出车轮每秒的旋转圈数:
1. 1英里等于1760码或63360英寸,那么15英里/小时换算为英寸/秒为:$15\times63360\div3600 = 264$英寸/秒
2. 车轮每秒的旋转圈数为:$264\div88 = 3$圈/秒
3. 车轮旋转一圈所需的时间为:$1\div3 = 333.333$毫秒

接下来,我们需要计算Timer3的计数频率。由于我们将Timer3设置为16位定时器,其最大计数值为65535。如果使用内部振荡器设置为8MHz,系统时钟将运行在$8MHz\div4 = 2MHz$。如果Timer3使用最大分频比8,那么Timer3的计数频率为$2MHz\div8 = 250kHz$,即每个计数周期为4微秒。如果要在333.333毫秒内达到这个计数值,需要计数$333.333ms\div4\mu s = 83333.25$,这个值超过了Timer3的最大计数值65535,因此不能使用250kHz的频率。

我们应该使用自行车的最小速度来确定振荡器和Timer3的频率设置。假设最小速度为1英里/小时,此时车轮每秒的旋转圈数为$1\times63360\div3600\div88 = 0.2$圈/秒,车轮旋转一圈需要5秒。为了在最大计数值65535内达到5秒的时间,每个计数周期应该为$5\div65536 = 76.294$微秒,那么Timer3的计数频率为$1\div76.294\mu s = 13.1072kHz$。系统时钟频率为$13.1072kHz\times8 = 104.857kHz$,振荡器频率为$104.857kHz\times4 = 419.430kHz$。由于内部振荡器可以设置为500kHz或250kHz,我们选择更接近的250kHz。

当内部振荡器设置为250kHz时,系统时钟运行在$250kHz\div4 = 62.5kHz$。使用最大分频比8,Timer3的计数频率为$62.5kHz\div8 = 7.8125kHz$,每个计数周期为128微秒。在5秒内,Timer3的计数值为$5\div128\mu s = 39062.5$,在Timer3的最大计数值范围内。

为了将内部振荡器设置为250kHz,我们需要将OSCCON寄存器的第6、5和4位设置为010。为了实现Timer3的最大分频比8,我们需要将T3CON寄存器的第5和4位设置为11。

4. 程序测试与事件处理

为了测试程序,我们使用Timer1生成一个6Hz的方波信号,该信号应该对应30英里/小时的速度。我们将使用CCP模块的比较功能来生成这个方波信号。比较功能会将Timer1的计数值与CCPR1寄存器中存储的值进行比较,当两者相等时,可以设置以下三种操作之一:
- 将CCPX引脚驱动为高电平
- 将CCPX引脚驱动为低电平
- 如果CCPX引脚当前为低电平,则将其驱动为高电平;如果为高电平,则将其驱动为低电平

Timer1的计数速率与Timer3相同,当Timer1达到651时,时间为$651\times126ms = 83.333$毫秒,这是6Hz方波信号的半个周期。因此,我们可以使用这个原理生成一个占空比为50%的6Hz方波信号。

在程序中,有两个事件会触发PIC执行特定的操作:
- Timer1的计数值等于CCPR1寄存器中存储的值(CCP模块的比较功能),这会导致CCP1引脚的逻辑电平在高电平和低电平之间切换。
- CCP2引脚的电平从高电平变为低电平(CCP模块的捕获功能),这会使PIC将Timer3的当前值加载到count1或count2中。

为了确保这两个事件不会被遗漏,我们使用CCP模块的中断功能。同时,我们将捕获功能的中断优先级设置为高于比较功能,以展示中断优先级的使用方法。

5. 代码实现与分析

以下是完整的自行车速度测量程序代码:

/*
 * File:   bikeSpeedProg.c
 * Author: Hubert Ward
 * written for the PIC18f4525
 * using both low and high priority interrupts
 * Created on 21 April 2020, 12:06
 */
#include <xc.h>
#include <conFigInternalOscNoWDTNoLVP.h>
#include <4bitLCDPortb.h>
#include <stdio.h>
//some definitions
#define slow PORTAbits.RA0
#define fast PORTAbits.RA1
#define slowest PORTAbits.RA2
#define LED0 PORTDbits.RD0
#define LED1 PORTDbits.RD1
//some variables
unsigned count1, count2;
float speed, period;
//some subroutines
void displaySpeed(float dp)
{
    sprintf(str, "%.2f", dp);
    writeString(str);
    writeString(" mph");
}
void interrupt HP_int ()
{
    PIR2bits.CCP2IF = 0;
    LED0 = 1;
    if (count1 == 0) count1 = CCPR2;
    else
    {
        count2 = CCPR2;
        period = (count2 - count1)*0.000128;
        count1 = 0;
        count2 = 0;
        TMR3 = 0;
    }
}
void interrupt low_priority LP_int ()
{
    PIR1bits.CCP1IF = 0;
    LED1 = 1;
    if (TMR1 >= CCPR1) TMR1 = 0;
}
void main ()
{
    PORTA = 0;
    PORTB = 0;
    PORTC = 0;
    PORTD = 0;
    TRISA = 0b00010111;
    TRISB = 0x00;
    TRISC = 0b00000010;
    TRISD = 0;
    ADCON0 = 0x00;
    ADCON1 = 0x0F;
    OSCCON = 0b00100100;
    OSCTUNE = 0b10000000;
    T0CON = 0b10000010;
    T1CON = 0b10110001;
    T3CON = 0b10111001;
    INTCON = 0b11000000;
    INTCON2 = 0;
    INTCON3 = 0;
    PIE1 = 0b00000100;
    PIE2 = 0b00000001;
    RCON = 0b10000000;
    IPR1 = 0;
    IPR2 = 0b00000001;
    CCP1CON = 0b00000010;
    CCP2CON = 0b00000100;
    CCPR1 = 1953;
    setUpTheLCD ();
    writeString ("The Speed is");
    while (1)
    {
        line2 ();
        if (!slow) CCPR1 = 1302;
        if (!fast) CCPR1 = 651;
        if (!slowest) CCPR1 = 1953;
        speed = 5/period;
        displaySpeed(speed);
    }
}
6. 代码详细分析
6.1 显示速度的子函数
void displaySpeed(float dp)
{
    sprintf(str, "%.2f", dp);
    writeString(str);
    writeString(" mph");
}

这个子函数使用 sprintf 函数将浮点数 speed 格式化为两位小数的字符串,并在后面添加“mph”表示英里/小时,然后将该字符串显示在LCD上。需要注意的是, sprintf 函数在 stdio.h 头文件中定义,因此我们需要在代码中包含该头文件。

6.2 高优先级中断服务程序(捕获功能)
void interrupt HP_int ()
{
    PIR2bits.CCP2IF = 0;
    LED0 = 1;
    if (count1 == 0) count1 = CCPR2;
    else
    {
        count2 = CCPR2;
        period = (count2 - count1)*0.000128;
        count1 = 0;
        count2 = 0;
        TMR3 = 0;
    }
}
  • PIR2bits.CCP2IF = 0; :清除CCP2中断标志位,防止自动触发下一次中断,并使PIC能够检测到下一次中断的发生。
  • LED0 = 1; :这是一个调试辅助语句,用于检查PIC是否进入了该中断服务程序。如果LED亮起,说明PIC已经进入了该子程序。
  • if (count1 == 0) count1 = CCPR2; :如果 count1 的值为0,说明这是CCP2引脚的第一个事件,将CCPR2寄存器的当前值加载到 count1 中。
  • else :如果 count1 的值不为0,说明这是CCP2引脚的第二个事件,将CCPR2寄存器的当前值加载到 count2 中,并计算车轮旋转一圈的时间周期 period 。最后,将 count1 count2 和TMR3清零,准备下一次测量。
6.3 低优先级中断服务程序(比较功能)
void interrupt low_priority LP_int ()
{
    PIR1bits.CCP1IF = 0;
    LED1 = 1;
    if (TMR1 >= CCPR1) TMR1 = 0;
}
  • PIR1bits.CCP1IF = 0; :清除CCP1中断标志位。
  • LED1 = 1; :同样是调试辅助语句。
  • if (TMR1 >= CCPR1) TMR1 = 0; :检查Timer1的计数值是否大于或等于CCPR1寄存器中存储的值,如果是,则将Timer1清零。实际上,由于PIC执行到该语句时,Timer1的值可能已经超过了CCPR1,因此我们使用 >= 而不是 ==
6.4 主程序
void main ()
{
    PORTA = 0;
    PORTB = 0;
    PORTC = 0;
    PORTD = 0;
    TRISA = 0b00010111;
    TRISB = 0x00;
    TRISC = 0b00000010;
    TRISD = 0;
    ADCON0 = 0x00;
    ADCON1 = 0x0F;
    OSCCON = 0b00100100;
    OSCTUNE = 0b10000000;
    T0CON = 0b10000010;
    T1CON = 0b10110001;
    T3CON = 0b10111001;
    INTCON = 0b11000000;
    INTCON2 = 0;
    INTCON3 = 0;
    PIE1 = 0b00000100;
    PIE2 = 0b00000001;
    RCON = 0b10000000;
    IPR1 = 0;
    IPR2 = 0b00000001;
    CCP1CON = 0b00000010;
    CCP2CON = 0b00000100;
    CCPR1 = 1953;
    setUpTheLCD ();
    writeString ("The Speed is");
    while (1)
    {
        line2 ();
        if (!slow) CCPR1 = 1302;
        if (!fast) CCPR1 = 651;
        if (!slowest) CCPR1 = 1953;
        speed = 5/period;
        displaySpeed(speed);
    }
}
  • 初始化端口和寄存器:将PORTA、PORTB、PORTC和PORTD清零,并设置相应的方向寄存器。同时,设置ADC、振荡器、定时器和中断相关的寄存器。
  • OSCCON = 0b00100100; :将内部振荡器设置为250kHz。
  • T0CON = 0b10000010; :将Timer0设置为16位定时器,并将其分频比设置为8。
  • T1CON = 0b10110001; T3CON = 0b10111001; :设置Timer1和Timer3的工作模式和分频比。
  • INTCON = 0b11000000; :使能全局中断和外设中断。
  • PIE1 = 0b00000100; PIE2 = 0b00000001; :使能CCP1和CCP2的外设中断。
  • RCON = 0b10000000; :使能中断优先级功能。
  • IPR1 = 0; IPR2 = 0b00000001; :设置CCP1为低优先级中断,CCP2为高优先级中断。
  • CCP1CON = 0b00000010; CCP2CON = 0b00000100; :将CCP1设置为比较功能,CCP2设置为捕获功能。
  • CCPR1 = 1953; :设置CCPR1寄存器的值,用于生成2Hz的方波信号。
  • 在主循环中,根据输入信号调整CCPR1的值,从而改变方波信号的频率,进而改变自行车的模拟速度。同时,根据计算得到的时间周期 period 计算自行车的速度 speed ,并调用 displaySpeed 函数将速度显示在LCD上。
7. 速度计算原理

在程序中,我们使用公式 speed = 5/period 来计算自行车的速度。其中, period 是车轮旋转一圈的时间周期,通过 (count2 - count1)*0.000128 计算得到。而数字“5”的计算过程如下:
$5 = \frac{60\times60\times88}{63360}$
- $60\times60$:将秒转换为小时。
- 88:车轮的周长,将小时转换为距离。
- 63360:1英里的英寸数。

因此,数字“5”的作用是将频率(1/周期)转换为英里/小时。

8. 总结

通过使用CCP模块的捕获和比较功能,我们可以实现自行车速度的精确测量。在程序中,我们详细介绍了定时器频率的设置、中断处理、速度计算和显示等关键步骤。同时,我们还使用了调试辅助语句来检查程序的运行情况。希望通过本文的解析,你能够更好地理解CCP模块的工作原理和应用方法,以及如何使用PIC微控制器来实现实际的测量和控制任务。

基于CCP模块的自行车速度测量程序解析

9. 信号输入与输出关系

在这个自行车速度测量程序中,信号的输入与输出有着特定的关系。我们使用CCP1引脚(PORTC的第2位)输出的方波信号来模拟自行车车轮传感器的信号,该信号会输入到CCP2引脚(PORTC的第1位)。以下是信号输入输出关系的流程图:

graph LR
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;

    A(Timer1生成方波):::process --> B(CCP1引脚输出方波):::process
    B --> C(模拟自行车传感器信号):::process
    C --> D(CCP2引脚输入信号):::process
    D --> E(CCP模块捕获功能):::process
    E --> F(计算速度):::process
    F --> G(显示速度):::process

这个流程图展示了从Timer1生成方波信号,到最终显示自行车速度的整个过程。通过这种方式,我们可以模拟不同速度下自行车的运行情况,并对程序进行测试。

10. 中断优先级的重要性

在程序中,我们将捕获功能的中断优先级设置为高于比较功能。这是因为捕获功能用于获取车轮旋转的关键时间信息,对于准确计算自行车速度至关重要。如果捕获功能的中断被比较功能的中断延迟处理,可能会导致时间信息不准确,从而影响速度计算的精度。

以下是中断优先级处理的逻辑说明:
- 当捕获功能和比较功能同时产生中断请求时,PIC会优先处理捕获功能的中断服务程序(HP_int)。
- 只有当捕获功能的中断服务程序执行完毕后,PIC才会返回并继续执行比较功能的中断服务程序(LP_int)。

这种中断优先级的设置确保了关键信息的及时处理,提高了程序的稳定性和准确性。

11. 代码优化建议

虽然当前的程序已经能够实现自行车速度的测量,但我们可以对代码进行一些优化,以提高程序的性能和可读性。以下是一些优化建议:
- 减少不必要的条件判断 :在低优先级中断服务程序中, if (TMR1 >= CCPR1) TMR1 = 0; 这一条件判断可以简化为 TMR1 = 0; ,因为只有当 TMR1 等于或超过 CCPR1 时,才会触发该中断。
- 添加注释和文档 :在代码中添加更多的注释和文档,解释每个部分的功能和作用,特别是一些关键的寄存器设置和计算公式,有助于其他开发者理解和维护代码。
- 错误处理 :在程序中添加错误处理机制,例如当捕获的时间周期异常大或小时,进行相应的错误提示或处理,提高程序的健壮性。

12. 拓展应用

基于这个自行车速度测量程序,我们可以进行一些拓展应用,例如:
- 里程计算 :通过记录车轮的旋转圈数和车轮的周长,可以计算出自行车行驶的里程。可以在程序中添加一个变量来记录旋转圈数,并在每次捕获到完整的旋转周期时进行累加。
- 速度报警 :设置一个速度阈值,当自行车速度超过该阈值时,触发报警信号。可以使用一个LED灯或蜂鸣器来实现报警功能。
- 数据记录 :将自行车的速度和里程数据记录到一个外部存储设备中,例如SD卡或EEPROM,以便后续分析和查看。

以下是拓展应用的功能列表:
| 拓展应用 | 实现方法 |
| ---- | ---- |
| 里程计算 | 记录车轮旋转圈数,乘以车轮周长 |
| 速度报警 | 设置速度阈值,超过阈值触发报警 |
| 数据记录 | 将速度和里程数据存储到外部设备 |

13. 总结与展望

通过对基于CCP模块的自行车速度测量程序的详细解析,我们了解了CCP模块的捕获和比较功能的应用方法,以及如何利用PIC微控制器实现自行车速度的精确测量。在程序中,我们涉及了定时器频率设置、中断处理、速度计算和显示等多个关键技术点。

未来,我们可以进一步优化程序,提高测量的精度和稳定性。同时,可以结合更多的传感器和外设,实现更多的功能,例如环境监测、导航等,为自行车的智能化发展提供更多的可能性。希望本文能够为你在嵌入式系统开发和实际应用中提供一些有价值的参考和启示。

【直流微电网】径向直流微电网的状态空间建模与线性化:一种耦合DC-DC变换器状态空间平均模型的方法 (Matlab代码实现)内容概要:本文介绍了径向直流微电网的状态空间建模与线性化方法,重点提出了一种基于耦合DC-DC变换器状态空间平均模型的建模策略。该方法通过对系统中多个相互耦合的DC-DC变换器进行统一建模,构建出整个微电网的集中状态空间模型,并在此基础上实施线性化处理,便于后续的小信号分析与稳定性研究。文中详细阐述了建模过程中的关键步骤,包括电路拓扑分析、状态变量选取、平均化处理以及雅可比矩阵的推导,最终通过Matlab代码实现模型仿真验证,展示了该方法在动态响应分析和控制器设计中的有效性。; 适合人群:具备电力电子、自动控制理论基础,熟悉Matlab/Simulink仿真工具,从事微电网、新能源系统建模与控制研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①掌握直流微电网中多变换器系统的统一建模方法;②理解状态空间平均法在非线性电力电子系统中的应用;③实现系统线性化并用于稳定性分析与控制器设计;④通过Matlab代码复现和扩展模型,服务于科研仿真与教学实践。; 阅读建议:建议读者结合Matlab代码逐步理解建模流程,重点关注状态变量的选择与平均化处理的数学推导,同时可尝试修改系统参数或拓扑结构以加深对模型通用性和适应性的理解。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值