由于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 信号, 原因没有找到。
|
|