• 学习笔记|计数器|Keil软件中 0xFD问题|I/O口配置|STC32G单片机视频开发教程(冲哥)|第十二集:计数器的作用和意义


    1.计数器的用途

    直流有刷的电机,后面两个一正一负的电接上,电机就可以转
    在这里插入图片描述

    到底是转子个几个圈呢?
    我们就可以在背后加装这么一个码盘,我们假设这里是有60个这个光电的一个孔对吧
    那我们转一圈是不是就可以一个轴上输出60个这样的脉冲,有遮挡就输出1,无遮挡输出0,
    两路交替,如果说这圈有60个脉冲,60个高低电平(总共几个脉冲除60就是转的圈数)
    本节利用开关,模拟计数器的功能。
    编码器也可以实现脉冲输出,输出波形:方波
    流过了多少液体,中间有个比例变化。一高一低算一个脉冲。
    只要输出信号带这种高低电平变化的,想要计算个数的就可以用计数器的功能
    在这里插入图片描述

    2.计数器的配置

    在这里插入图片描述

    GATE=O(TMOD.7)时,如TR1=1,则定时器计数。GATE=1时,允许由外部输入INT1控制定时器1,
    这样可实现脉宽测量。TR1为TCON寄存器内的控制位,TCON寄存器各位的具体功能描述见上节TCON寄存器的介绍。
    当c/T=0时,多路开关连接到系统时钟的分频输出,Tl对内部系统时钟计数,T1工作在定时方式
    当C/T=1时,多路开关连接到外部脉冲输入P3.5/T1,即 T1工作在计数方式。
    STC单片机的定时器1有两种计数速率:一种是12T模式,每12个时钟加1,与传统8051单片机相同;另外一种是1T模式,每个时钟加1,速度是传统8051单片机的12倍。T1的速率由特殊功能寄存器AUXR中的T1x12决定,如果T1x12=0,T1则工作在12T模式;如果T1x12=1,T1则工作在1T模式定时器1有两个隐藏的寄存器RL_TH1和RL_TL1。RL_TH1与TH1共有同一个地址,RL_TL1与TL1共有同一个地址。当TRI=0即定时器/计数器Ⅰ被禁止工作时,对TLl写入的内容会同时写入RL_TLl,对
    TH1写入的内容也会同时写入RL_TH1。当TR1=1即定时器/计数器Ⅰ被允许工作时,对TLl写入内容,实际上不是写入当前寄存器TL1中,而是写入隐藏的寄存器RL_TL1中,对THl 写入内容,实际上也不是写入当前寄存器TH1中,而是写入隐藏的寄存器RL_THl,这样可以巧妙地实现16位重装载定时器。当读THl和TL1的内容时,所读的内容就是TH1和TL1的内容,而不是RL_TH1和RL_TLl的内容。
    当定时器1工作在模式1(TMOD[5:4][M1.MO]=00B)时,[THl,TL1l]的溢出不仅置位TF1,而且会自动将[RL_TH1,RL_TL1]的内容重新装入[TH1,TL1]。
    当T1CLKO/INT_CLKO.1=1时,P3.4/TO管脚配置为定时器1的时钟输出T1CLKO。输出时钟频率为T1溢出率/2。
    如果C/T=0,定时器/计数器T1对内部系统时钟计数,则:
    T1工作在1T模式(AUXR.6/T1x12=1)时的输出时钟频率=(SYsclk)(TM1PS+1)(65536-[RL_TH1,RL_TL1])/2T1工作在12T模式(AUXR.6/TIx12=0)时的输出时钟频率=(SYSclk)(TM1PS+1)/12/(65536-[RL_TH1,RL_TLI])/2如果C/T=1,定时器/计数器T1是对外部脉冲输入(P3.5/Tl)计数,则:输出时钟频率=(Tl_Pin_CLK)/(65536-[RL_TH1,RL_TL1])/2

    Tl_C/T:Tl_C/T位写1,控制定时器1用作定时器或计数器,清0则用作定时器(对内部系统时钟进行计数),置1用作计数器(对引脚T1/P3.5外部脉冲进行计数)。
    Tl_GATE:控制定时器1,置1时只有在INT1脚为高及TRI控制位置1时才可打开定时器/计数器1。此处仅需要TR1计数,故可以将Tl_GATE置0.

    官方例程

    在这里插入图片描述
    在这里插入图片描述
    设置TMOD = 0X40 =0100 0000 ,即Tl_C/T置1,用作计数。16位自动重载。
    TL1 = 0xff; //1111 1111,65535,相当于再来1个脉冲,就可以进一下中断,给引脚取反。每来一个脉冲,取反一次。
    TH1 = 0xff;

    开始

    用按钮模拟,松开是高电平,然后不断的按下松开按下松开。
    选用例程作为参考:
    在这里插入图片描述

    需要打开内部4K的上拉电阻打开,用到端口上拉电阻控制寄存器(PxPU):在这里插入图片描述

    在这里插入图片描述

    STC-ISP软件中有这个IO口的一个配置工具:
    在这里插入图片描述

    利用上一节的例程,改名为8.计数器,可以用T1来实现。
    将void Timer0_Isr(void) interrupt 1屏蔽,调用T0中断屏蔽,本节不需要。
    按手册上的例程顺序编写。

    Tips:编译时提示错误FILE DOES NOT EXIST:

    在这里插入图片描述

    C251 FATAL-ERROR -
      ACTION:  OPENING INPUT-FILE
      FILE:    \STC32\8.计势鱘COMM\stc32g.h
      ERROR:   FILE DOES NOT EXIST
    C251 TERMINATED.
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这里涉及“附录I关于Keil软件中 0xFD问题的说明”。

    Keil项目路径名的字符中也不能含有带OxFD编码的汉字,否则Keil软件会无法正确编译此项目。故需要修改本工程的目录名:
    改为英文名:8.Count_T1,编译正常通过,按键led循环亮灭。
    课后可以试验一下通过改变H1跟TL1的值,实现2下亮灭。
    这个值的确定可以使用STC-ISP中的I/O口配置工具,高级配置,设置条件:
    在这里插入图片描述

    得到设置代码: P3PU = 0x20;

    3.计数器的应用

    见2017年全国大学生电子设计竞赛试题 ——直流电动机测速装置(O题),题目如下
    在这里插入图片描述

    解题思路如下:
    在这里插入图片描述

    M法测速:又叫做频率测量法。这种方法是在一个固定的定时时间内(以秒为单位),统计这段时间的编码器脉冲数,计算速度值。设编码器单圈总脉冲数为C, 时间T0内,统计到的编码器脉冲数为M0,则转速n的计算公式为:
    在这里插入图片描述

    首先我们定义一个变量叫u16 Count_T1 = 0;初值设置为0。
    改写中断处理函数:void Timer0_Isr(void) interrupt 1:

    void Timer0_Isr(void) interrupt 1 //1ms进来执行一次,无需其他延时,重复赋值
    {
    
    	TimCount++; //计算2000次=2s,可以一直运行
    	if(TimCount>= 2000)
    	{
    		TimCount = 0;
    
    		Count_T1 = (TH1 * 256) + TL1; //单位:转/s,
    
    		TH1 = 0;
    		TL1 = 0;
    
    		Show_Tab[4] = TimCount/1000%10;
    		Show_Tab[5] = TimCount/100%10+10;
    		Show_Tab[6] = TimCount/10%10;
    		Show_Tab[7] = TimCount/1%10;		//取10位
    
    	}
    
    	SEG_Fre();		//计算得到结果后,数码管刷新
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    本例完整代码:

    #include "COMM/stc.h"		//调用头文件
    #include "COMM/usb.h"
    
    #define KEY1 P32		//定义一个按键 引脚选择P32
    #define KEY2 P33		//定义一个按键 引脚选择P33
    
    #define BEEP P54		//定义一个按键 引脚选择P54
    
    #define SEG_Delay  1	//延时多少ms
    
    #define MAIN_Fosc 24000000UL	//定义主时钟
    
    char *USER_DEVICEDESC = NULL;
    char *USER_PRODUCTDESC = NULL;
    char *USER_STCISPCMD = "@STCISP#";
    	
    u8 SEG_Tab[21] = { 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90, 0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10,0xff};	//0-9段码,0-9带小数点
    u8 COM_Tab[8] = { 0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe };	//0-7的位码数组
    u8 Show_Tab[8] = {20,20,20,20,0,0,0,0};
    
    u32 TimCount = 0;		//计数单位1ms
    bit RUN_State = 0;		//开始运行/结束运行
    u8 num = 0;
    
    u16 Count_T1 = 0;
    
    void sys_init();	//函数声明
    void delay_ms(u16 ms);	//unsigned int 
    
    void SEG_Fre( void ) 
    {
    	//位码选择第一位,段码选择0  
    	P7 = COM_Tab[num];			//位码的选择
    	P6 = SEG_Tab[Show_Tab[num]];//需要显示的数字的内码 赋给 P6   NUM =0 -> Show_Tab[num]] = 1 -> p6 = oxF9 
    	//delay_ms(SEG_Delay);
    
    	num++;
    	if( num >7 )
    		num = 0;	
    }
    
    //========================================================================
    // 函数名称: Timer0_Init
    // 函数功能: 定时器0初始化
    // 入口参数: 无
    // 函数返回: 无
    // 当前版本: VER1.0
    // 修改日期: 2023
    // 当前作者: 
    // 其他备注: 
    //========================================================================
    void Timer0_Init(void)		//1毫秒@24.000MHz
    {
    	AUXR &= 0x7F;			//定时器时钟12T模式
    	TMOD &= 0xF0;			//设置定时器模式
    	TL0 = 0x30;				//设置定时初始值
    	TH0 = 0xF8;				//设置定时初始值
    	TF0 = 0;				//清除TF0标志
    	TR0 = 1;				//定时器0开始计时
    	ET0 = 1;				//使能定时器0中断
    }
    
    
    
    
    void main()					//程序开始运行的入口
    {
    	
    	sys_init();				//USB功能+IO口初始化
    	usb_init();				//usb库初始化
    
    	TMOD = 0x50;			//设置计数器模式   
    	TL1 = 0x00;				//设置计数初始值
    	TH1 = 0x00;				//设置计数初始值
    	TF1 = 0;				//清除TF1标志
    	TR1 = 1;				//定时器1开始计时
    	ET1 = 1;				//使能定时器1中断
    	    
    	P3PU = 0x20; 			//打开内部上拉4.1K
    
    	Timer0_Init();
    	
    	EA = 1;					//CPU开放中断,打开总中断。
    	
    	while(1)		//死循环
    	{
    		if( DeviceState != DEVSTATE_CONFIGURED ) 	//
    			continue;
    		if( bUsbOutReady )								
    		{
    			usb_OUT_done();
    
    		}
    
    	
    		
    	}
    }
    
    
    void Timer0_Isr(void) interrupt 1
    {
    	
    	
    	TimCount++;			//每隔1ms+1		//	计数到2000 = 2s
    	if( TimCount>=2000 )		//2秒定时时间到了
    	{
    		TimCount = 0;
    		
    		Count_T1 = (TH1 *256 )+ TL1;		// 转/2s  转/min
    		TH1 = 0;
    		TL1 = 0;
    		
    		Show_Tab[4] = Count_T1/1000%10;
    		Show_Tab[5] = Count_T1/100%10;	
    		Show_Tab[6] = Count_T1/10%10;		
    		Show_Tab[7] = Count_T1/1%10;		//取10位 
    	}
    	SEG_Fre();		//数码管刷新的
    }
    
    void Timer1_Isr(void) interrupt 3
    {
    	
    }
    
    /*
     11111110 0XFE
     11111101 0XFD
     11111011 0XFB
     11110111 0XF7
     11101111 0XEF
     11011111 0XDF
     10111111 0XBF
     01111111 0X7F
    */
    
    void sys_init()		//函数定义
    {
        WTST = 0;  //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
        EAXFR = 1; //扩展寄存器(XFR)访问使能
        CKCON = 0; //提高访问XRAM速度
    
    	P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
        P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
        P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
        P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
        P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
        P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
        P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
        P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口
    	
        P3M0 = 0x00;
        P3M1 = 0x00;
        
        P3M0 &= ~0x03;
        P3M1 |= 0x03;
    
        //设置USB使用的时钟源
        IRC48MCR = 0x80;    //使能内部48M高速IRC
        while (!(IRC48MCR & 0x01));  //等待时钟稳定
    
        USBCLK = 0x00;	//使用CDC功能需要使用这两行,HID功能禁用这两行。
        USBCON = 0x90;
    }
    
    
    void delay_ms(u16 ms)	//unsigned int 
    {
    	u16 i;
    	do
    	{
    		i = MAIN_Fosc/6000;
    		while(--i);
    	}while(--ms);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176

    总结

    了解计数器的使用方法和应用场景

    课后练习:

    尝试实现T法测速:又叫做周期测量法。这种方法是建立一个已知频率的高频脉冲并对其计数,计数时间由捕获到的编码器相邻两个脉冲的间隔时间TE决定, 计数值为M1。设编码器单圈总脉冲数为C,高频脉冲的频率为F0,则转速n的计算公式为:
    在这里插入图片描述

  • 相关阅读:
    LinkedList与链表
    对象的动态建立和释放(new和delete)
    vue2 mixins混入
    【SpringCloud】认识微服务
    时间序列-AR模型与MA模型的原理与实现
    【编程不良人】SpringSecurity实战学习笔记04---RememberMe
    华为eNSP配置专题-路由策略的配置
    springboot启动流程分析
    Java开发基础_04
    计算机毕业设计Javam和vue的酒店管理系统2021(源码+系统+mysql数据库+lw文档)
  • 原文地址:https://blog.csdn.net/Medlar_CN/article/details/132718232