用AI编程及分析(程序设计处于调整阶段)
功能概述:
8个触摸按键(TKx)和8个数码管联动,P3.0~P3.3用作串口、蜂鸣器和LED
问题:
该工程程序使用串口UART_SendByte就异常了
main.c
/*============================================================================
** 模块名称 : main.c
** 功能描述 : STC8H4K64TL 数码管 + 触摸按键 + 串口
**============================================================================*/
#include "config.h"
#include "led_disp.h"
#include "touch_key.h"
#include "uart.h"
#include "beep_led.h"
/* 全局开关定义 */
SysSw_t g_sw;
/* 各按键对应显示的数字(与通道号一致) */
static const unsigned char code key_digit[TOUCH_NUM] = {0,1,2,3,4,5,6,7};
/* 切换某位显示状态 */
static void ToggleDigit(unsigned char pos);
void main(void)
{
unsigned char i;
unsigned char chn;
unsigned short running_ctn;
const unsigned char *seg_tbl = LED_GetSegTbl();
g_sw.disp_on = TRUE;
g_sw.touch_on = TRUE;
for (i = 0; i < DISP_TOTAL_DIGITS; i++)
{
g_sw.digit[i] = TRUE;
g_sw.touch_en[i] = TRUE;
}
BeepLed_Init();
LED_Init();
UART_Init();
// NOP5();
// UART_SendString("UART_Init(); Ready\r\n");
Touch_Init();
// NOP5();
// UART_SendString("Touch_Init(); Ready\r\n");
/* 初始: 所有位显示仅小数点 */
for (i = 0; i < DISP_TOTAL_DIGITS; i++)
{
LED_SetCode(i, SEG_DP_ONLY);
disp_is_number[i] = FALSE;
}
LED_Refresh();
EA = 1; /* 开总中断 */
// UART_SendString("EA=1;\r\n");
// UART_SendByte('A');
while (1)
{
running_ctn++;
// UART_SendString("MCU Running\r\n");
UART_ProcessCmd();
if(running_ctn == 1)
{
// UART_SendByte('R');
// COMEN = _crol_(COMEN, 1);
}
if (g_sw.touch_on)
{
chn = Touch_Scan();
if (chn < TOUCH_NUM)
{
if (g_sw.touch_en[chn])
{
ToggleDigit(chn);
Beep_Once();
// UART_SendString("Key");
// UART_SendByte(('0'+chn));
// UART_SendByte((chn));
// UART_SendString("\r\n");
}
}
}
LED_Refresh();
Led_Update();
}
}
static void ToggleDigit(unsigned char pos)
{
if (disp_is_number[pos])
{
LED_SetCode(pos, SEG_DP_ONLY);
disp_is_number[pos] = FALSE;
}
else
{
LED_SetCode(pos, LED_GetSegTbl()[key_digit[pos]]);
disp_is_number[pos] = TRUE;
}
}
~~~
config.h
/*============================================================================
** 模块名称 : config.h
** 芯片型号 : STC8H4K64TL-40I-LQFP32
** 系统时钟 : 11.0592MHz (内部IRC)
** 编译器 : Keil C51
** 说明 : 工程全局配置
**============================================================================*/
#ifndef __CONFIG_H__
#define __CONFIG_H__
#include "STC8H.h"
/*------------------ 系统时钟 / 波特率 ------------------*/
#define FOSC 11059200UL /* 系统时钟 11.0592MHz */
#define BAUDRATE 115200UL /* 串口1 波特率 */
/*------------------ 显示位数:两模块共 8 位 ----------------*/
#define DISP_TOTAL_DIGITS 8
/*------------------ 触摸按键总数 TK0~TK7 ----------------*/
#define TOUCH_NUM 8
/*------------------ 蜂鸣器 / LED 引脚 -------------------
蜂鸣器: P3.3, 高电平响
LED : P3.2, 高电平亮 */
#define BEEP_PORT P3
#define BEEP_PIN 3
#define LED_PORT P3
#define LED_PIN 2
/* 宏:控制蜂鸣器/LED */
#define BEEP_ON() (BEEP_PORT |= (1 << BEEP_PIN))
#define BEEP_OFF() (BEEP_PORT &= ~(1 << BEEP_PIN))
#define LED_ON() (LED_PORT |= (1 << LED_PIN))
#define LED_OFF() (LED_PORT &= ~(1 << LED_PIN))
/*------------------ 全局开关结构体 ------------------
disp_on : 数码管显示总开关
touch_on : 触摸按键总开关
digit[8] : 每个席位(按键)的显示单独开关 1=启用
touch_en[8] : 每个触摸按键单独开关 1=启用 */
typedef struct
{
unsigned char disp_on;
unsigned char touch_on;
unsigned char digit[DISP_TOTAL_DIGITS];
unsigned char touch_en[TOUCH_NUM];
} SysSw_t;
extern SysSw_t g_sw; /* 定义在 main.c */
/*------------------ 逻辑常量 ------------------*/
#define FALSE 0
#define TRUE 1
#endif /* __CONFIG_H__ */
led_disp.c
/*============================================================================
** 模块名称 : led_disp.c
** 功能描述 : STC8H4K64TL 内建 LED 数码管驱动(共阴模式)
** 段位: SEG8=a,9=b,10=c,11=d,12=e,13=f,14=g,15=dp
** ==> 8段全部位于 COMn_DC_H(高8位), bit0=SEG8(a)...bit7=SEG15(dp)
**============================================================================*/
#include "config.h"
#include "led_disp.h"
static unsigned char disp_code[DISP_TOTAL_DIGITS];
/* 共阴 7段段码表(0~9): bit0=a,bit1=b,bit2=c,bit3=d,bit4=e,bit5=f,bit6=g */
static const unsigned char code SEG_TBL[10] =
{
0x3F, 0x06, 0x5B, 0x4F, 0x66,
0x6D, 0x7D, 0x07, 0x7F, 0x6F
};
/* 该位当前是否显示数字(1) 或 仅小数点(0) */
unsigned char disp_is_number[DISP_TOTAL_DIGITS];
void LED_Init(void)
{
P_SW2 |= 0x80;
LEDCTRL = 0x00;
COMEN = 0xFF;
// COMEN = 0xFE;
// COMEN = 0xEE;
SEGENH = 0xFF;
SEGENL = 0x00;
LEDCTRL = 0x80; /* 共阴, 100%, LEDON=1 */
LEDCKS = 0x07;
TSRT = 0x00;
}
void LED_SetCode(unsigned char pos, unsigned char seg_code)
{
if (pos >= DISP_TOTAL_DIGITS) return;
disp_code[pos] = seg_code;
}
void LED_Refresh(void)
{
unsigned char i;
unsigned char val;
P_SW2 |= 0x80;
for (i = 0; i < DISP_TOTAL_DIGITS; i++)
{
if (g_sw.disp_on && g_sw.digit[i])
val = disp_code[i];
else
val = SEG_OFF;
switch (i)
{
case 0: COM0_DC_L = 0x00; COM0_DC_H = val; break;
case 1: COM1_DC_L = 0x00; COM1_DC_H = val; break;
case 2: COM2_DC_L = 0x00; COM2_DC_H = val; break;
case 3: COM3_DC_L = 0x00; COM3_DC_H = val; break;
case 4: COM4_DC_L = 0x00; COM4_DC_H = val; break;
case 5: COM5_DC_L = 0x00; COM5_DC_H = val; break;
case 6: COM6_DC_L = 0x00; COM6_DC_H = val; break;
case 7: COM7_DC_L = 0x00; COM7_DC_H = val; break;
}
}
}
const unsigned char *LED_GetSegTbl(void) { return SEG_TBL; }
led_disp.h
/*============================================================================
** 模块名称 : led_disp.h
** 功能描述 : STC8H4K64TL 内建 LED 数码管驱动(共阴, 硬件自动刷新)
**============================================================================*/
#ifndef __LED_DISP_H__
#define __LED_DISP_H__
/* 仅小数点段码 (bit7=dp) */
#define SEG_DP_ONLY 0x80
/* 全灭 */
#define SEG_OFF 0x00
extern unsigned char disp_is_number[DISP_TOTAL_DIGITS];
/* 初始化 LED 驱动 */
void LED_Init(void);
/* 设置某位(0~7) 段码 */
void LED_SetCode(unsigned char pos, unsigned char seg_code);
/* 刷新显示 */
void LED_Refresh(void);
/* 获取 0~9 共阴段码表指针 */
const unsigned char *LED_GetSegTbl(void);
#endif /* __LED_DISP_H__ */
touch_key.c
/*============================================================================
** 模块名称 : touch_key.c
** 功能描述 : STC8H4K64TL 触摸按键 TK0~TK7
** 采用轮询 TSIF 标志, 每扫完一个通道根据数据与基准差值判断按下。
** 按键按下时 16 位计数数据会下降(手册: 无触摸20000左右, 有触摸下降5%以上)
**============================================================================*/
#include "config.h"
#include "touch_key.h"
static unsigned int base_val[TOUCH_NUM]; /* 每个通道无触摸基准值 */
static unsigned char press_flag[TOUCH_NUM]; /* 当前是否处于按下状态 */
static unsigned char busy_mode = FALSE; /* 是否已完成基准采样 */
/* 按下判定阈值: 基准值下降超过该值认为按下(需按实际板子标定) */
#define TOUCH_TH 800
/*---------------------------------------------------------------------------
** 内部函数: 等待并读取一通道扫描数据
** 返回 0xFF 表示无扫描完成
**-------------------------------------------------------------------------*/
static unsigned char Touch_GetData(unsigned int *dat)
{
unsigned char chn;
if (TSSTA2 & 0x80) /* TSIF=1, 扫描完成 */
{
chn = (unsigned char)(TSSTA2 & 0x0F); /* TSDNCHN[3:0] */
*dat = ((unsigned int)TSDATH << 8) | (unsigned int)TSDATL;
TSSTA2 |= 0x80; /* 写1清 TSIF */
TSSTA2 |= 0x40; /* 写1清 TSDOV(如有) */
return chn;
}
return 0xFF;
}
/*---------------------------------------------------------------------------
** 函 数 名 : Touch_Init
** 功能描述 : 配置并启动触摸控制器, 采集基准值
**-------------------------------------------------------------------------*/
void Touch_Init(void)
{
unsigned char i;
unsigned int d;
unsigned char chn;
P_SW2 |= 0x80; /* 访问 XFR */
/* 使能 TK0~TK7 (TSCHEN1.bit0~bit7) */
TSCHEN1 = 0xFF;
TSCHEN2 = 0x00;
/* 不分时复用 LED */
TSRT = 0x00;
/* TSCFG1: SCR=000(开关频率=SYSclk/2=5.53MHz,<12.5MHz), DT=000 */
TSCFG1 = 0x00;
/* TSCFG2: TSVR=00(参考电压1/4 AVcc) */
TSCFG2 = 0x00;
/* TSCTRL: SINGLE=0(连续), TSWAIT=1(扫完暂停), TSSAMP=00(1次) */
TSCTRL = 0x20;
/* 启动扫描 */
// TSGO = 1;
TSCTRL |= 0x80;
/* 采集各通道基准值(每通道需多次以确保稳定) */
for (i = 0; i < TOUCH_NUM; i++)
{
chn = 0xFF;
while (chn != i) /* 等待采集到对应通道 */
{
chn = Touch_GetData(&d);
}
base_val[i] = d;
}
busy_mode = TRUE; /* 基准采集完成, 进入正常扫描 */
}
/*---------------------------------------------------------------------------
** 函 数 名 : Touch_Scan
** 功能描述 : 扫描一次, 若某通道由"未按下"变为"按下", 返回该通道号并置按下状态
** 返回 0xFF 表示本轮无按下事件
**-------------------------------------------------------------------------*/
unsigned char Touch_Scan(void)
{
unsigned int d;
unsigned char chn;
// unsigned char i;
chn = Touch_GetData(&d);
if (chn == 0xFF) return 0xFF; /* 无通道完成 */
/* 根据扫描到的通道号判断是否按下 */
if (chn < TOUCH_NUM)
{
/* 按下阈值判断: 数据显著低于基准 */
if (base_val[chn] > TOUCH_TH) /* 防溢出 */
{
if (d < (base_val[chn] - TOUCH_TH)) /* 检测到按下 */
{
if (press_flag[chn] == FALSE) /* 边沿: 未按下->按下 */
{
press_flag[chn] = TRUE;
return chn; /* 报告按下事件 */
}
}
else
{
press_flag[chn] = FALSE; /* 释放 */
}
}
}
return 0xFF;
}
touch_key.h
/*============================================================================
** 模块名称 : touch_key.h
** 功能描述 : STC8H4K64TL 触摸按键 TK0~TK7 (轮询 TSIF 方式)
**============================================================================*/
#ifndef __TOUCH_KEY_H__
#define __TOUCH_KEY_H__
/* 初始化触摸控制器并采集各通道基准值 */
void Touch_Init(void);
/* 扫描触摸: 返回最近一次检测到"按下事件"的通道号(0~7), 无事件返回 0xFF */
unsigned char Touch_Scan(void);
#endif /* __TOUCH_KEY_H__ */
uart.c
/*============================================================================
** 模块名称 : uart.c
** 功能描述 : 串口1 (P3.0 RxD / P3.1 TxD)
** 命令协议:
** T0~T7 : 单独切换对应触摸按键 启用/禁用
** TON/TOFF : 触摸按键总开关
** S0~S7 : 单独切换对应数码管位 显示/熄灭
** SON/SOFF : 数码管显示总开关
** STATUS/? : 回显当前按键和数码管状态表
** 波特率: 115200 (系统时钟11.0592MHz, 定时器1模式0 1T)
**============================================================================*/
#include "config.h"
#include "uart.h"
#include "led_disp.h"
bit busy; // 发送忙标志
/* 接收缓冲 */
static unsigned char rx_buf[32];
static unsigned char rx_cnt = 0;
/*---------------------------------------------------------------------------
** 函 数 名 : UART_Init
** 功能描述 : 初始化串口1
**-------------------------------------------------------------------------*/
void UART_Init(void)
{
/* P3.0 RxD, P3.1 TxD 默认准双向即可; 配置串口1模式1(8位可变波特率) */
SCON = 0x50; /* SM0=0,SM1=1(模式1), REN=1(允许接收) */
/* 定时器1作为波特率发生器, 模式0(16位自动重载), 1T */
TMOD = (TMOD & 0x0F) | 0x00; /* T1: GATE=0,C/T=0,M1=0,M0=0(16位) */
AUXR = (AUXR & 0x7F) | 0x40; /* T1x12=1 (1T模式) */
/* 波特率 = SYSclk / (4 * (65536 - 重载值))
11059200 / (4*115200) = 24 => 重载值 = 65536-24 = 0xFFE8 */
TH1 = 0xFF;
TL1 = 0xE8;
TR1 = 1; /* 启动定时器1 */
ES = 1; /* 使能串口1中断 */
// EA = 1; /* 开总中断 */
}
/*---------------------------------------------------------------------------
** 函 数 名 : UART_SendString
** 功能描述 : 发送字符串
**-------------------------------------------------------------------------*/
void UART_SendString(const char *s)
{
while (*s)
{
while(busy == 1);
busy = 1;
SBUF = *s++;
// while (!TI);
// TI = 0;
}
}
///*---------------------------------------------------------------------------
//** 函 数 名 : UART_SendByte (供中断/回显内部使用)
//**-------------------------------------------------------------------------*/
//static void UART_SendByte(unsigned char dat)
void UART_SendByte(unsigned char dat)
{
while(busy == 1);
busy = 1;
SBUF = dat;
// while (!TI);
// TI = 0;
}
/*---------------------------------------------------------------------------
** 函 数 名 : UART_ProcessCmd
** 功能描述 : 若收到完整命令执行并回显; 这里按回车(0x0D)作为命令结束
**-------------------------------------------------------------------------*/
void UART_ProcessCmd(void)
{
unsigned char i;
char c;
/* 在中断里已经把字符放入 rx_buf, 这里检查是否有回车结束 */
if (rx_cnt == 0) return;
if (rx_buf[rx_cnt-1] != 0x0D) return; /* 未收到回车, 等待 */
/* 命令以 '\r' 结束, 去除结尾 */
rx_buf[rx_cnt-1] = '\0';
c = rx_buf[0]; /* 首字符 */
/* ---- 首字符 'T' : 触摸按键控制 ---- */
if (c == 'T' || c == 't')
{
/* TON / TOFF */
if ((rx_buf[1]=='O'||rx_buf[1]=='o') && (rx_buf[2]=='N'||rx_buf[2]=='n'))
{ g_sw.touch_on = TRUE; }
else if ((rx_buf[1]=='O'||rx_buf[1]=='o') && (rx_buf[2]=='F'||rx_buf[2]=='f'))
{ g_sw.touch_on = FALSE; }
else if (rx_buf[1] >= '0' && rx_buf[1] <= '7')
{
i = rx_buf[1] - '0';
/* 不改变硬件使能, 仅逻辑开关; 在 main 里由 g_sw.touch_en 控制是否响应 */
g_sw.touch_en[i] = (g_sw.touch_en[i]) ? FALSE : TRUE; /* 切换 */
}
UART_SendString("Touch OK\r\n");
rx_cnt = 0;
return;
}
/* ---- 首字符 'S' : 数码管控制 ---- */
if (c == 'S' || c == 's')
{
if ((rx_buf[1]=='O'||rx_buf[1]=='o') && (rx_buf[2]=='N'||rx_buf[2]=='n'))
{ g_sw.disp_on = TRUE; }
else if ((rx_buf[1]=='O'||rx_buf[1]=='o') && (rx_buf[2]=='F'||rx_buf[2]=='f'))
{ g_sw.disp_on = FALSE; }
else if (rx_buf[1] >= '0' && rx_buf[1] <= '7')
{
i = rx_buf[1] - '0';
g_sw.digit[i] = (g_sw.digit[i]) ? FALSE : TRUE; /* 切换显示/熄灭 */
}
UART_SendString("Disp OK\r\n");
rx_cnt = 0;
return;
}
/* ---- 状态查询 STATUS 或 ? ---- */
if ((rx_buf[1]=='T'||rx_buf[1]=='t') || (rx_buf[1]=='A'||rx_buf[1]=='a'))
{
/* STATUS: 回显状态表 */
UART_SendString("\r\n--- Status ---\r\n");
/* 按键状态 */
for (i = 0; i < TOUCH_NUM; i++)
{
UART_SendString("Touch");
UART_SendByte('0'+i);
UART_SendString(": ");
UART_SendString(g_sw.touch_en[i] ? "ON " : "OFF");
UART_SendString(" ");
}
UART_SendString("\r\nDisp:");
for (i = 0; i < DISP_TOTAL_DIGITS; i++)
{
UART_SendString(g_sw.digit[i] ? "1" : "0");
}
UART_SendString("\r\nDispTotal:");
UART_SendString(g_sw.disp_on ? "ON" : "OFF");
UART_SendString(" TouchTotal:");
UART_SendString(g_sw.touch_on ? "ON" : "OFF");
UART_SendString("\r\n");
rx_cnt = 0;
return;
}
/* 未知命令 */
UART_SendString("?\r\n");
rx_cnt = 0;
}
/*---------------------------------------------------------------------------
** 串口1 中断: 接收字节到缓冲
**-------------------------------------------------------------------------*/
void UART1_ISR(void) interrupt 4
{
if (RI)
{
RI = 0;
if (rx_cnt < sizeof(rx_buf)-1)
{
rx_buf[rx_cnt++] = SBUF;
}
else
{
rx_cnt = 0; /* 防溢出 */
}
}
if (TI)
{
TI = 0;
busy = 0;
}
}
uart.h
/*============================================================================
** 模块名称 : uart.h
** 功能描述 : 串口1 (P3.0 RxD / P3.1 TxD) 收发 + 命令解析
**============================================================================*/
#ifndef __UART_H__
#define __UART_H__
/* 初始化串口1 (定时器1模式0, 1T, 波特率115200) */
void UART_Init(void);
void UART_SendByte(unsigned char dat);
/* 发送一个字符串 */
void UART_SendString(const char *s);
/* 串口命令处理(在主循环调用): 解析并执行 T/S 命令, 回显状态 */
void UART_ProcessCmd(void);
#endif /* __UART_H__ */
beep_led.c
/*============================================================================
** 模块名称 : beep_led.c
** 功能描述 : 蜂鸣器(P3.3 高电平响) + LED状态灯(P3.2 高电平亮)
**============================================================================*/
#include "config.h"
#include "beep_led.h"
/*---------------------------------------------------------------------------
** 函 数 名 : BeepLed_Init
** 功能描述 : P3.2/P3.3 配置为推挽输出, 初始关闭
**-------------------------------------------------------------------------*/
void BeepLed_Init(void)
{
/* P3M1=0, P3M0: bit2,bit3 置1 => 推挽输出 */
P3M1 &= ~((1<<BEEP_PIN) | (1<<LED_PIN));
P3M0 |= ((1<<BEEP_PIN) | (1<<LED_PIN));
BEEP_OFF();
LED_OFF();
}
/*---------------------------------------------------------------------------
** 函 数 名 : Beep_Once
** 功能描述 : 蜂鸣器短鸣一次 (约80ms)
**-------------------------------------------------------------------------*/
void Beep_Once(void)
{
unsigned int i;
BEEP_ON();
for (i = 0; i < 30000; i++); /* 粗略延时 */
BEEP_OFF();
}
/*---------------------------------------------------------------------------
** 函 数 名 : Led_Update
** 功能描述 : 按键总开关开启则LED亮, 关闭则LED灭
**-------------------------------------------------------------------------*/
void Led_Update(void)
{
if (g_sw.touch_on)
LED_ON();
else
LED_OFF();
}
beep_led.h
/*============================================================================
** 模块名称 : beep_led.h
** 功能描述 : 蜂鸣器(P3.3 高电平响) + LED状态灯(P3.2 高电平亮)
**============================================================================*/
#ifndef __BEEP_LED_H__
#define __BEEP_LED_H__
/* 初始化蜂鸣器和LED引脚为推挽输出 */
void BeepLed_Init(void);
/* 蜂鸣器鸣叫指定毫秒数(粗略, 基于延时) */
void Beep_Once(void);
/* 根据按键总开关刷新LED状态灯 */
void Led_Update(void);
#endif /* __BEEP_LED_H__ */