打印
[应用相关]

【转】STM8 STVD 定时器1 捕获信号周期频率

[复制链接]
1305|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
lefeng|  楼主 | 2018-3-25 18:42 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
由于3个文件,我一个一个贴上来,若运行有问题,自己修改下 头文件即可
硬件 PC1 作为信号输入口
串口使用UART2  PD5 PD6
主文件
/* MAIN.C file
*



* 实现功能 通过TIM1 输入捕获功能测量信号的周期和占空比 这个与上一个例子相似,但增加了
* 占空比的测量 。
* 步骤:
* 1、选择TIM1_CCR1 的有效输入配置输入捕获 功能
* 2,选择TI1FP1 的有效极性 用来捕获数据到TIM1_CCR1 和清除计数器
* 选择上升还是下降沿有效
* 3、选择TIM1_CCR1 的有效输入 TIM1_CCMR2 寄存器 CC2S 选中 TI2FP2
*  4,选择有效的惩罚信号
* 5,选择TI1FP2 的捕获极性
*6 , 配置触发模式控制器为复位惩罚模式
* 7,使能捕获功能
* 整体思路:当捕获到第一个上升沿定时器开始定时计数;捕获到
* 下降沿时将定时器中的数据保存下来,当捕获到第二个上升沿时
* 将当前定时器值在保存下来,并清0 定时器重新计数
* 达到的 第一个就是 信号  就是信号宽度
* 达到 的第二个信号  就是 信号周期
* 第一个值 / 第二个值 就是占空比
* 这里用到复位触发模式 是指 当一个触发输入事件发生时
* 计数器和它的预分频器能够重新被初始化
* 通过串口输出
*  测的外部 100Khz
*  这里需要关闭CC0 时钟否则 测的频率不稳定
*  串口用外部时钟 16Mhz  9600波特率
*  亲测 测的频率 101Khz  占空比 49.4%
*  采用外部输入一个100KHZ 的信号  占空比 50% 存在误差
*/

#include "common.h"  //头文件 可以去掉的


unsigned long f;
unsigned char dutycycle;
unsigned int value1,value2;
unsigned char gewei,shiwei,baiwei,qianwei,dutycycle1,dutycycle2;





//配置系统时钟
void CLK_Init(void)
{

  //CLK_CKDIVR = 0x11;          // 10: fHSI = fHSI RC output/ 4
                              //          = 16MHZ / 4 =4MHZ
                              // 001: fCPU=fMASTER/2. = 2MHZ
        CLK_ECKR |=0X1;   //开启外部时钟
        while(!(CLK_ECKR&0X2)); //等待外部时钟rdy
        
        //外部晶振 = 8MHz
        //CLK_CKDIV = 0XF8   1111 1000  

        //CLK_CKDIVR &= 0X00;      //CPU无分频
        //CLK_CKDIVR &= 0X07;
        //16000000/128 = 125khz
        //CLK_CKDIVR = 0X00;  //不分频
        // 16000000 /8 = 2MHZ
        CLK_CKDIVR = 0X00;  // 16分频
        //16000.000/16 = 1MHZ
        CLK_SWR = 0XB4;   //选择外部时钟
        while(!(CLK_SWCR&0X8)); //这里要等
        CLK_SWCR |=0X02;   //使能外部时钟
}


void  delay_ms(u8  ms)
{
    u8  i,j;
    while(ms--)
    {
        for(i=4;i!=0;i--)
          for(j=100;j!=0;j--);
    }
}


void main()
{
        _asm("sim");
        UART2_Init();// 串口初始化
         CLK_Init();  //时钟初始化
         //CLK_CKDIVR = 0X00;
         tim1_init(); //定时器初始化
          //cco_init();
         _asm("rim");
        
        while (1)
  {


        
                TIM1_SR1 &=0xf9;//0B11111001;
//CC2IF CC2 通道配置为输入模式 当捕获事件发生时 该位由硬件置1 软件清0
//CC1IF CC1  首先把状态位 置 0

    TIM1_SR2 &=0xfd;//0B11111101;
//CC1OF 捕获比较重复捕获标记
//=0 无重复捕获产生
//先把 重复捕获标志位 清 0

    TIM1_CCER1 |=0X11;
//CC1 通道配置为输入  =1 捕获使能
//CC2 通道配置为输入  =1 捕获使能

                while(!(TIM1_SR1 & 0X02)); //等待通道CC1IF 计数器捕获
                while(!(TIM1_SR1 & 0X04)); //等待通道CC2IF 计数器捕获
    value1 =(unsigned int) TIM1_CCR2H<<8;
    value1 |= TIM1_CCR2L;                 

   //存储捕获比较寄存器 2 数值
         while(!(TIM1_SR1 & 0X02)); //在此 等待通道CC1IF计数器捕获数值
         value2 =(unsigned int)TIM1_CCR1H<<8;
         value2 |= TIM1_CCR1L;

         //TIM1_CCER1 &=0B11101110;
         //禁止捕获比较1 =0
         //禁止捕获比较2 =0
         //关闭捕获功能
         f =(16000000UL/value2); //测试信号的频率
         
         //f = f/100;  //KHZ 小数点显示 别忘了上一个程序
         //f=101
         dutycycle = value1/value2;  //占空比出来
         //为什么*100  25%   30% 占空比否则小数很小明白不
         //一般我们说占空比 都是 20% 30%  这里 *100 直接显示的是 20 30
         //明白了
         
         //for(i=0;i<400;i++)  //延时作用 注释掉也行
   // display(); //数码管显示
    printf("value1:%d\r\n",value1);
                printf("value2:%d\r\n",value2);
                //printf("频率:%d\r\n",f);
                //printf("占空比:%d\r\n",dutycycle);
  }
}

/*************************************/
由于串口使用了中断 下面是中断函数
/*        BASIC INTERRUPT VECTOR TABLE FOR STM8 devices
*        Copyright (c) 2007 STMicroelectronics
*/

#include "common.h"

typedef void @far (*interrupt_handler_t)(void);

struct interrupt_vector {
        unsigned char interrupt_instruction;
        interrupt_handler_t interrupt_handler;
};

@far @interrupt void NonHandledInterrupt (void)
{
        /* in order to detect unexpected events during development,
           it is recommended to set a breakpoint on the following instruction
        */
        return;
}

@far @interrupt void NonHandledInterrupt_UART2RX (void)
{
        /* in order to detect unexpected events during development,
           it is recommended to set a breakpoint on the following instruction
        */
        UART2_SR &= 0xdf;
        UART2_Send(UART2_DR);
        return;
}

extern void _stext();     /* startup routine */

struct interrupt_vector const _vectab[] = {
        {0x82, (interrupt_handler_t)_stext}, /* reset */
        {0x82, NonHandledInterrupt}, /* trap  */
        {0x82, NonHandledInterrupt}, /* irq0  */
        {0x82, NonHandledInterrupt}, /* irq1  */
        {0x82, NonHandledInterrupt}, /* irq2  */
        {0x82, NonHandledInterrupt}, /* irq3  */
        {0x82, NonHandledInterrupt}, /* irq4  */
        {0x82, NonHandledInterrupt}, /* irq5  */
        {0x82, NonHandledInterrupt}, /* irq6  */
        {0x82, NonHandledInterrupt}, /* irq7  */
        {0x82, NonHandledInterrupt}, /* irq8  */
        {0x82, NonHandledInterrupt}, /* irq9  */
        {0x82, NonHandledInterrupt}, /* irq10 */
        {0x82, NonHandledInterrupt}, /* irq11 */
        {0x82, NonHandledInterrupt}, /* irq12 */
        {0x82, NonHandledInterrupt}, /* irq13 */
        {0x82, NonHandledInterrupt}, /* irq14 */
        {0x82, NonHandledInterrupt}, /* irq15 */
        {0x82, NonHandledInterrupt}, /* irq16 */
        {0x82, NonHandledInterrupt}, /* irq17 */
        {0x82, NonHandledInterrupt}, /* irq18 */
        {0x82, NonHandledInterrupt}, /* irq19 */
        {0x82, NonHandledInterrupt}, /* irq20 */
        {0x82, NonHandledInterrupt_UART2RX}, /* irq21 */
        {0x82, NonHandledInterrupt}, /* irq22 */
        {0x82, NonHandledInterrupt}, /* irq23 */
        {0x82, NonHandledInterrupt}, /* irq24 */
        {0x82, NonHandledInterrupt}, /* irq25 */
        {0x82, NonHandledInterrupt}, /* irq26 */
        {0x82, NonHandledInterrupt}, /* irq27 */
        {0x82, NonHandledInterrupt}, /* irq28 */
        {0x82, NonHandledInterrupt}, /* irq29 */
};



/*************************************************/
串口函数如下 ,记得修改 串口波特率  因为用2MHZ 与 16MHZ

/*************** (C) COPYRIGHT  EW工作室 ***************************************
* 文件名  :uart.c
* 描述    :串口调试文件   
* 实验平台:EW STM8开发板 V1
* 库版本  :V2.1.0
* 作者    :EW  QQ:307298507
* 公众微信:eworld2013
* 修改时间:2013-07-20
*******************************************************************************/

/* 包含系统头文件 */

/* 包含自定义头文件 */
#include "uart.h"

/* 自定义新类型 */

/* 自定义宏 */

/* 自定义变量 */

/*实现函数部分*/


/*******************************************************************************
* 名称: Uart_Init
* 功能: UART2初始化操作
* 形参: 无
* 返回: 无
* 说明: 无
******************************************************************************/
/*
void  UART2_Init(void)
{
    UART2_CR1=0x00;
    UART2_CR2=0x00;
    UART2_CR3=0x00;
    UART2_BRR2 = 0x07;
    UART2_BRR1 = 0x01;     //115200波特率
    UART2_CR2 = 0x2c;     //允许发送  允许接收 接收中断使能
}
*/
void  UART2_Init(void)
{
                UART2_CR1 = 0X00;
                //控制寄存器 1
                //R8存放接收到的字第9位
                //T8 存放发送字的第9位
                //UARTD=0 使能
                //WAIE 被空闲总线唤醒
                //PCEN UART 模式=0 奇偶校验 禁止
                //PS 奇偶校验选择 这里不选择
                //PIEN 校验中断 = 0 禁止
                UART2_CR2 = 0X0C;//0000 1100
                //发送接收使能
                UART2_CR3 = 0X00; //0010
                //00 1个停止位
                //01 保留
                //10 2个停止位
                // 11 1.5 个停止位
                //UART2_BRR2 = 0X00; //2MHZ 下9600
                //UART2_BRR1 = 0X0D; //2000000/9600 = 208 =D0 =00D0
               
                UART2_BRR2 = 0X03; //16MHZ 下9600
                UART2_BRR1 = 0X68;
               
                //设置波特率
                //这个波特率设置很有意思
                //不是直接赋值 而是 达到的 16进制数
                // 中间两位赋值 给BRR1 两边2为赋值给BRR2
                //2000000/2400=833=0341
    //16000000/2400 = 6666 =1a0a =2400b波特率
    //16000/9600=683


}



void  UART2_Send(u8 dat)
{
    while((UART2_SR & 0x80)==0x00);
    UART2_DR = dat;
}


/*******************************************************************************
* 名称: UART2_SendString
* 功能: UART2发送len个字符
* 形参: data -> 指向要发送的字符串
*       len -> 要发送的字节数
* 返回: 无
* 说明: 无
******************************************************************************/
void UART2_SendString(u8* Data)
{
        u16 i=0;
        //for(; i < len; i++)
        while(Data!='\0')
        {
           UART2_Send(Data);        /* 循环调用发送一个字符函数 */
           i++;
        }
               
}

/*******************************************************************************
* 名称: void putchar(char ch)
* 功能: 打印一个字符
* 形参: 需要输出的字符
* 返回: 无
* 说明: 无

******************************************************************************/
void putchar(char ch)                // print normal characters or '\n'
{         
        if('\n' == ch)
        {
                UART2_Send('\r');
        }

        UART2_Send(ch);

}

/*******************************************************************************
* 名称: void printf_str(unsigned char *buffer)
* 功能: 打印字符串
* 形参: 需要打印的字符串首地址
* 返回: 无
* 说明: 例如   printf_str("this is a uart test");

******************************************************************************/
void printf_str(unsigned char *buffer)
{
        while (*buffer != '\0')
        {
            putchar(*buffer++);
    }
}

/*******************************************************************************
* 名称: void putascbase(unsigned char ch)
* 功能: 将字符转换成ASIC输出
* 形参: 需要打印的字符
* 返回: 无
* 说明: 比如我们输出16进制55,在串口调试助手没勾上16进制显示,
                                        这时就用putascbase(0x55);

******************************************************************************/
void putascbase(unsigned char ch)
{
         unsigned char i, j;//,k;
         //for(k = 0; k<4 ;k++)
         {
             i = (ch>>4) & 0x0f;
             j = ch & 0x0f;
             if (i>9) i += 7;
        
             if (j>9) j += 7;
             putchar(i+0x30);
             putchar(j+0x30);
     }

//         printf("\n");
}

/*******************************************************************************
* 名称: void putasc(unsigned char ch)
* 功能: 将字符转换成ASIC输出,换行
* 形参: 需要打印的字符
* 返回: 无
* 说明: 无

******************************************************************************/
void putasc(unsigned char ch)
{
         putascbase(ch);
         putchar(' ');
//         printf("\n");
}

/*******************************************************************************
* 名称: void putascLong(unsigned long ch)
* 功能: 输出一个4字节长度的字符
* 形参: 需要输出的字符
* 返回: 无
* 说明: 无

******************************************************************************/
void putascLong(unsigned long ch)
{
         unsigned char i, j,k;
         for(k = 0; k<4 ;k++)
         {
             i = (((u8 *)&ch)[k]>>4) & 0x0f;
             j = ((u8 *)&ch)[k] & 0x0f;
             if (i>9) i += 7;
        
             if (j>9) j += 7;
             putchar(i+0x30);
             putchar(j+0x30);
     }
         putchar(' ');

}
/*******************************************************************************
* 名称: void putascShort(unsigned short ch)
* 功能: 输出一个2字节长度的字符
* 形参: 需要输出的字符
* 返回: 无
* 说明: 无

******************************************************************************/
void putascShort(unsigned short ch)
{
         putascbase(ch>>8);
         putascbase(ch>>0);
         putchar(' ');
//         printf("\n");         //*/
}
/*******************************************************************************
* 名称: void printf_array(unsigned char *buffer, unsigned short  cnt)
* 功能: 输出数组
* 形参: 数组首地址,长度
* 返回: 无
* 说明: 输出BUF[10]
                                        printf_array(BUF,10);

******************************************************************************/
void printf_array(unsigned char *buffer, unsigned short  cnt)
{
        u16 i;
        for(i=0; i< cnt; i++)
        {
                putasc(buffer);
                if(((i+1)%16)==0)
                    putchar('\n');         
        }
                  
}   //*/


/**************************************************************************/

下面是关键的 TIM1 的设置,
#include "zkb.h"

extern unsigned long f;
extern unsigned char dutycycle;
extern unsigned int value1,value2;
extern unsigned char gewei,shiwei,baiwei,qianwei,dutycycle1,dutycycle2;
extern unsigned char dis[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};

void cco_init(void)
{
                CLK_CCOR |= 0X03; //Flsi 时钟输出
                //CLK_CCOR |= 0X05; //Fhse 时钟输出
                //CLK_CCOR |= 0X09; //Fcpu 时钟输出
                //CLK_CCOR |= 0X15; //Fcpu/64 时钟输出
               
}




void delay(unsigned int t)
{
               
                while(t--);
}

void gpio_init(void)
{
                PB_ODR = 0XFF;
                PB_DDR = 0XFF;
                PB_CR1 = 0XFF;
                PB_CR2 = 0XFF;
                // PB 口 为 位选
                PD_ODR = 0XFF;
                PD_DDR = 0XFF;
                PD_CR1 = 0XFF;
                PD_CR2 = 0XFF;
                //PB 口 为 段选
               
               
                PE_DDR = 0X01;
                PE_CR1 = 0X01;
                PE_CR2 = 0X01;
               
                PC_DDR &= 0B11111101;   //配置捕获输入 IO 口
          PC_CR1 |= 0B00000010;    //PC1
               
}



void display(void)
{
                qianwei = f/1000;
                baiwei = (f%1000)/100;
                shiwei = (f%100)/10;
                gewei  =   f%10;
    dutycycle1 = dutycycle%10;
                dutycycle2 = dutycycle/10;
               
               
                PD_ODR = dis[gewei];
    PB_ODR = 0xfe; //0b11111110;  
                delay(100);
               
                PD_ODR = dis[shiwei]|0x80;  //为什么 与 0X80  显示小数点 用
    PB_ODR = 0xfd; //0b11111101;  //1111 1011
                delay(100);
               
                PD_ODR = dis[baiwei];
    PB_ODR = 0xfb; //0b11111011;  //1111 1101
                delay(100);
               
                PD_ODR = dis[qianwei];
    PB_ODR = 0xf7; //0b111101111;  //1111 1110
                delay(100);
               
                PD_ODR = dis[dutycycle2];
    PB_ODR = 0x7f; //0b0111.1111;  //1111 1101
                delay(100);
               
                PD_ODR = dis[dutycycle1];
    PB_ODR = 0xbf; //0b1011.1111;  //1111 1110
                delay(100);
                //PB_ODR = 0XFF;
               
}


void tim1_init(void)
{
// TIM1_CCER1 &=0B11111110; //CC1E = 0;
TIM1_CCMR1 |= 0X01; //cc1s 仅在TIM1_CCER1 CC1E=0 才可写入
//IC1F[3:0] = 0000 无滤波功能
//IC1PSC[1:0] = 00 定义CC1 输入预分频系数 无预分频 每一次都捕获
//CC1S [1:0] = 01 CC1 通道配置为输入 IC1 映射到TI1FP1 上
TIM1_CCER1 &=~(1<<1);
//CC1P = 0 捕获发生在TI1F 的高电平或上升沿

//TIM1_CCER1 &=10101111; //CC2S 仅在通道关闭时 CC2E =0 CC2NE=0 且被更新在可以写

TIM1_CCMR2 |= 0X02;
//CC2S = 10 CC2 通道配置为输入 IC2 映射到TI1FP2 上
TIM1_CCER1 |= (1<<5);
//TI1FP2 下降沿 低电平有效

TIM1_SMCR  |= 0X50;
//触发信号选择 TS =101 滤波器后定时器输入 TI1FP1

TIM1_SMCR |= 0X04;
//触发模式选择 为 复位触发
TIM1_CR1 |= 0X01;
//使能计数器
}
/*************************************************/
全部代码都份上了, 关键是 TIM1寄存器的设置 ,串口的设置简单些,定时器的设置有些拗口 ,如果通俗的总结一下是,
      比如一条水管 PC1 入口-----经过滤波----分频----上下沿------最后出来两个口 TI1FP1 TI2FP1  水出口
    TI1FP1  给IC 1捕获比较寄存器 了----第一个池子供水了
  TI2FP1  给 IC2 捕获比较寄存器   另外一个池子供水。
TI1FP1  是红色的水
TI2FP1  是灰色的水
           (在单片机中是一个上升沿一个下降沿区分)
IC1 寄存器 TIM1_CCR1H TIM1_CCR1L   存放水的多少 (上升沿检测)
IC2 寄存器TIM1_CCR2H  TIM1_CCR1L   存放水的多少(下降沿检测)
(注意程序中这段是 先检测的下降沿存放数据的)然后
通过进总水口PC1 的流速,单片机中的晶振 ,与IC1  IC2 寄存器存放的数据 得出 信号的频率和占空比。
计算在程序有,不在写,
上面程序亲自测试确保能正确输出信号频率和占空比
数值存在 误差
最后在说下 CCO 时钟最好别开,开启后测试不到132Khz  信号, 原因没有找到。
沙发
天灵灵地灵灵| | 2018-3-26 16:42 | 只看该作者
误差是不是晶振不准引起的

使用特权

评论回复
板凳
mmuuss586| | 2018-3-26 16:43 | 只看该作者
不错,感谢分享;

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

200

主题

1087

帖子

0

粉丝