- 打卡等级:偶尔看看I
- 打卡总天数:10
- 最近打卡:2025-04-17 01:59:50
已绑定手机
注册会员
- 积分
- 98
|
// 头文件
/*
不要少了ASM文件
CSEG AT 011BH
LJMP 006BH
END
*/
#include "STC8H.h" // 芯片头文件
#include <intrins.h> // 内联汇编指令
// 宏定义
// 数据类型
#define u8 unsigned char
#define u16 unsigned int
#define u32 unsigned long
// 数据存储
#define MAIN_Fosc 11059200UL // 主时钟频率
#define Baudrate 115200L // 串口波特率
// 端口定义
sbit P_K0_LED = P3^7; // TK0对应指示灯
sbit P_K1_LED = P3^6; // TK1对应指示灯
// 声明
// 数组
u16 xdata TK_cnt[16]; // 存储16路触摸的当前实时电容值
u16 xdata TK_zero[16]; // 存储16路触摸的基准值
u16 xdata TK_differ[16]; // 存储16路触摸的差值--判断触摸是否发生
u16 xdata TK_lowest[16]; // 存储16路触摸的最小电容值
u8 xdata Zero_Add_Cnt[16]; // 向上追踪延时计数器
u8 xdata Zero_Sub_Cnt[16]; // 向下追踪延时计数器
u8 xdata TK_counter[16]; // 状态缓冲计数器-判断有效触摸
u8 xdata RX1_Buffer[64]; // 串口接收缓冲区
// TP0 TP1 TP2 TP3 TP4 TP5 TP6 TP7 TP8 TP9 TP10 TP11 TP12 TP13 TP14 TP15
u16 code DIFF[]={350, 360, 800, 600, 600, 850, 850, 1200, 850, 1000, 800, 900, 550, 600, 900, 430};
// 检测阈值=(未触摸值-触摸值)/2
// 函数
void Timer0_init(u32 clk); // timer0初始化函数
void UART1_config(u32 clk, u32 brt); // UART1波特率发生器----
void UART1_TxByte(u8 dat); // 串口1发送一个字节函数
void JudgeKey(u8 tkn); // 判断键值是否有效
void ReadZeroData(void); // 获取触摸按键的零值
void system_init(void); // 系统初始化
void TK_init(void); // 触摸按键初始化
// 变量
bit B_TX1_Busy; // 串口通信忙标志位 (防止串口数据丢失)
bit B_Zero_Flag; // 控制是否保存按键未触摸时的基准值
bit B_1ms; // 定时器0-1ms中断标志位
u8 i; // 通用变量
u8 TK_TimeOut; // 触摸按键扫描超时
u8 read_cnt; // 记录触摸按键读取次数
u8 RX1_Cnt; // 串口接收计数器
u16 tpFlag; // 16位标志位-用于存储触摸按键的状态并根据位置执行对应程序(第0位就是TK0)
u16 JudgeFlag; // 16位标志位-用于防止重复触发触摸按键(第0位就是TK0)
u16 ScanFreq; // 按键扫描,扫描50次执行1次,保证输出稳定
u16 ChannelSet; // 16位标志位-用于存储触摸按键的总使能情况(0x0003就是TK0 TK1 使能)
// 函数模块
// UART1波特率发生器----
void UART1_config(u32 clk, u32 brt)
{
brt = 65536UL - (clk / 4) / brt;
TR1 = 0;
AUXR &= ~0x01; // S1 BRT Use Timer1;
AUXR |= (1<<6); // Timer1 set as 1T mode
TMOD &= ~(1<<6); // Timer1 set As Timer
TMOD &= ~0x30; // Timer1_16bitAutoReload;
TH1 = (u8)(brt >> 8);
TL1 = (u8)brt;
ET1 = 0; // 禁止Timer1中断
INTCLKO &= ~0x02; // Timer1不输出高速时钟
TR1 = 1; // 运行Timer1
P_SW1 &= 0x3f;
P_SW1 |= 0x00; //UART1 switch to, 0x00: P3.0 P3.1, 0x40: P3.6 P3.7, 0x80: P1.6 P1.7, 0xC0: P4.3 P4.4
SCON = (SCON & 0x3f) | (1<<6); // 8位数据, 1位起始位, 1位停止位, 无校验
ES = 1; //允许中断
REN = 1; //允许接收
B_TX1_Busy = 0;
RX1_Cnt = 0;
}
// 串口1发送一个字节函数----
void UART1_TxByte(u8 dat)
{
B_TX1_Busy = 1; //标志发送忙
SBUF = dat; //发一个字节
while(B_TX1_Busy); //等待发送完成
}
// timer0初始化函数(固定)
void Timer0_init(u32 clk)
{
TR0 = 0; //停止计数
ET0 = 1; //允许中断
TMOD &= ~0x03;
TMOD |= 0; //工作模式, 0: 16位自动重装, 1: 16位定时/计数, 2: 8位自动重装, 3: 16位自动重装, 不可屏蔽中断
TMOD &= ~0x04; //定时
INTCLKO &= ~0x01; //不输出时钟
// 设置定时器0的时钟--小于则1T else 12T
if (clk < 65536UL)
{
AUXR |= 0x80; // 1T mode
TH0 = (u8)((65536UL - clk) / 256);
TL0 = (u8)((65536UL - clk) % 256);
}
else
{
AUXR &= ~0x80; // 12T mode
TH0 = (u8)((65536UL - clk/12) / 256);
TL0 = (u8)((65536UL - clk/12) % 256);
}
TR0 = 1; //开始运行
}
// 判断键值是否有效并设置触摸状态标志(固定)
void JudgeKey(u8 tkn)
{
if(tkn > 15) return; // 检查通道序号是否合法(最多支持16个通道)
if(ChannelSet & (1<<tkn)) // 如果当前通道启用
{
if(TK_cnt[tkn] > TK_zero[tkn]) // 当前通道采样值高于基准零点(无触摸状态)
{
Zero_Sub_Cnt[tkn] = 0; // 清除减计数(用于延迟下降)
// 增计数未达到上限,则继续增加
if(Zero_Add_Cnt[tkn] < 10)
{
Zero_Add_Cnt[tkn]++;
}
else
{
TK_zero[tkn] += 1; // 增量追踪:慢慢抬高零点,适应环境变化
}
// 没有触摸的话,按键计数器慢慢减小
if(TK_counter[tkn] > 0)
{
TK_counter[tkn]--;
}
else
{
// 若无持续触摸,则清除触摸标志位
JudgeFlag &= ~(1<<tkn);
tpFlag &= ~(1<<tkn);
}
}
else if(TK_cnt[tkn] <= (TK_zero[tkn] - TK_differ[tkn])) // 按键触摸有效(低于阈值)
{
Zero_Sub_Cnt[tkn] = 0;
Zero_Add_Cnt[tkn] = 0;
if(TK_cnt[tkn] <= (TK_zero[tkn] - TK_lowest[tkn])) // 太低可能是干扰
{
TK_counter[tkn] = 0; // 清除状态,干扰处理
}
else if((JudgeFlag & (1<<tkn)) == 0) // 还没有设置按键触发标志
{
TK_counter[tkn]++; // 连续触摸计数
if(TK_counter[tkn] > ScanFreq) // 达到设定的有效连续次数
{
JudgeFlag |= (1<<tkn); // 设置为有效触摸
tpFlag |= (1<<tkn); // 同步触摸标志
}
}
}
else // 处于过渡状态(无明显触摸、也不是回零)
{
Zero_Add_Cnt[tkn] = 0;
// 延迟减计数,防止频繁追踪零点
if(Zero_Sub_Cnt[tkn] < 100)
{
Zero_Sub_Cnt[tkn]++;
}
else
{
if(TK_cnt[tkn] < TK_zero[tkn]) // 如果当前值还是低于零点
{
TK_zero[tkn] -= 1; // 零点下调(自适应环境)
Zero_Sub_Cnt[tkn] = 0;
}
}
// 如果没有稳定触摸,按键计数器也逐渐递减
if(TK_counter[tkn] > 0)
{
TK_counter[tkn]--;
}
else
{
JudgeFlag &= ~(1<<tkn); // 清除有效触摸标志
tpFlag &= ~(1<<tkn);
}
}
}
}
// 获取触摸按键的零值--环境自适应校准(固定)
void ReadZeroData(void)
{
u8 i;
// 获取零值
if(B_Zero_Flag)
{
TK_TimeOut++; // 触摸按键超时计数器
if(TK_TimeOut > 250) // 查看是否打到读取间隔
{
TK_TimeOut = 0; // 重置超时计数器
read_cnt++; // 读取次数加1
}
// 如果已经读取超过100次(延迟足够长时间,环境稳定)
if(read_cnt > 100)
{
// 扫描所有的通道
for(i=0; i<16; i++)
{
TK_zero = TK_cnt; // 保存当前计数为零点参考值
TK_counter = 0; // 清除运行中的计数器
}
B_Zero_Flag = 0;
}
}
}
// 触摸按键初始化
void TK_init(void)
{
TSRT = 0x00; // 没有LED分时扫描
TSCHEN1 = 0x03; // TK00~TK07
TSCHEN2 = 0x00; // TK08~TK15
TSCFG1 = (7<<4) + 3; // 开关电容工作频率 = fosc/(2*(TSCFG1[6:4]+1)), 放电时间(系统时钟周期数) 0(125) 1(250) 2(500) 3(1000) 4(2000) 5(2500) 6(5000) 7(7500) 最小3
TSCFG2 = 2; // 配置触摸按键控制器的内部参考电压(AVCC的分压比), 0(1/4) 1(1/2) 2(5/8) 3(3/4)
TSCTRL = 0xA0; // 开始自动扫描, 无平均, B7: TSGO, B6: SINGLE, B5: TSWAIT, B4: TSWUCS, B3: TSDCEN, B2: TSWUEN, B1 B0: TSSAMP
IE2 |= 0x80; // 使能触摸中断
UART1_config(MAIN_Fosc, Baudrate);
EA = 1; // 允许全局中断
for(i=0; i<16; i++)
{
TK_differ = DIFF; // 设置初始差值
TK_lowest = DIFF*3; // 设置初始下限值, 检测下限 = 阈值*3
TK_counter = 0;
Zero_Sub_Cnt = 0;
Zero_Add_Cnt = 0;
}
tpFlag = 0;
JudgeFlag = 0;
read_cnt=0;
TK_TimeOut = 0;
B_Zero_Flag = 1;
ChannelSet = ((u16)TSCHEN2 << 8) | TSCHEN1;
ScanFreq = 50; // 设置有效触摸按键持续检测时间
}
// 系统初始化
void system_init(void)
{
P_SW2 |= 0x80; // 允许访问XSF
P1M0 &= ~0x03; P1M1 |= 0x03; // P10 11 高阻输入
P3M0 |= 0xc0; P3M1 &= ~0xc0; // P36 37 推挽输出
P_K0_LED = 1; // 灭灯
P_K1_LED = 1;
Timer0_init(MAIN_Fosc/1000); // Timer 0 中断频率, 1000次/秒
TK_init(); // 触摸按键初始化
}
// 主函数
void main(void)
{
system_init(); // 系统初始化
while (1)
{
ReadZeroData();
if(B_1ms)
{
B_1ms = 0;
for(i=0;i<16;i++)
{
JudgeKey(i); // 判断键值
}
B = (u8)tpFlag;
if(B & 0x01) P_K0_LED = ~P_K0_LED; // bit0
if(B & 0x02) P_K1_LED = ~P_K1_LED; // bit1
tpFlag = 0;
UART1_TxByte((u8)(TK_cnt[15] >> 8)); //发送触摸按键0的键值到串口绘图
UART1_TxByte((u8)(TK_cnt[15]));
}
}
}
// timer0中断函数(固定)
void timer0_int (void) interrupt 1
{
B_1ms = 1; //1ms标志
}
// 串口1中断函数----
void UART1_int (void) interrupt 4
{
if(RI)
{
RI = 0;
if(RX1_Cnt >= 64) RX1_Cnt = 0;
RX1_Buffer[RX1_Cnt] = SBUF;
RX1_Cnt++;
}
if(TI)
{
TI = 0;
B_TX1_Busy = 0;
}
}
// 触摸按键中断(固定)
void TKSU_Interrupt(void) interrupt 13
{
u8 j;
j = TSSTA2;
if(j & 0x40) //数据溢出, 错误处理(略)
{
TSSTA2 |= 0x40; //写1清零
}
if(j & 0x80) //扫描完成
{
j &= 0x0f;
TK_cnt[j] = TSDAT; //保存某个通道的读数
TSSTA2 |= 0x80; //写1清零
read_cnt++; //读次数+1, 用于延时或读键计数
TK_TimeOut = 0;
}
}
以上整个代码已经可以用于触摸按键,但是触摸状态不太理想,想调试一下灵敏度
但是现在使用上位机读取不到数据 |
|