CListery
发表于 2024-7-1 17:23:33
本帖最后由 CListery 于 2024-7-9 10:28 编辑
第十八课:
1. 代码可以根据功能分成多个文件,按名称划分,如 xxx.h 和 xxx.c 相对应
2. 通过Keil 的 include path 功能将路径引入
3. 通过 extern 关键字可以将变量暴露出去,让其他源文件使用变量
4. 通过 static 关键字可以在函数中声明一个只初始化一次的变量,后续再次进入函数将会使用最后一次的值
复习心得:
1. 重点关注关键字的不同
在.h中声明变量
extern u8 bdata LED_STATES;
extern bit LED0;
extern bit LED_SW;
在.c中定义变量
u8 bdata LED_STATES= 0xFF;
sbit LED0 = LED_STATES^0;
sbit LED_SW = P4^0;
CListery
发表于 2024-7-1 17:25:20
本帖最后由 CListery 于 2024-7-10 15:42 编辑
第十九和二十课:
1. 通过 bdata 关键字修饰的变量可以通过 sbit 再对该变量的位起别名,这样就可以更加方便的进行单独某一位的修改
2. 数码管和 LED 的 IO 口相同时,为了避免刷新 LED 或数码管时互相影响,可以通过在定时器中断的不同的瞬间开关控制脚的高低电平实现 IO 口的复用
3. 为了程序的可移植性,不要直接使用 IO 口,而是通过重新定义的方式给 IO 口重新命名,如果更换其他引脚不同的芯片时,只需要修改定义的地方就可以了
复习心得:
1. 可以通过 #define 来直接访问数组的某一位,类似 sbit 功能
u8 SEG_DATA = {0xFF, 0xFF};
#define SEG0 SEG_DATA
#define SEG1 SEG_DATA
2. 使用 #define 时需要注意
#define LED_EN P40 // 这里不能使用 P4^0
3. 代码
led.h
#ifndef __LED_H
#define __LED_H
#include "stc32g.h"
#include "stc32_stc8_usb.h"
// 数码管使能
#define SEG_ENABLE P7
// 数码管显示控制
#define SEG_SHOW_CONTROL P6
// 发光二极管使能
#define LED_ENABLE P40
// 发光二极管显示控制
#define LED_SHOW_CONTROL P6
// 数码管显示位总控制变量
extern u8 SEG_POS_ENABLE;
// 数码管分位控制变量
#define SPE0 SEG_POS_ENABLE
#define SPE1 SEG_POS_ENABLE
#define SPE2 SEG_POS_ENABLE
#define SPE3 SEG_POS_ENABLE
#define SPE4 SEG_POS_ENABLE
#define SPE5 SEG_POS_ENABLE
#define SPE6 SEG_POS_ENABLE
#define SPE7 SEG_POS_ENABLE
// 数码管显示的数据总控制变量,对应 SEG_NUM_MAP 的值
extern u8 SEG_SHOW_DATA;
// 数码管每位显示的数据变量,对应 SEG_NUM_MAP 的值
#define SSD0 SEG_SHOW_DATA
#define SSD1 SEG_SHOW_DATA
#define SSD2 SEG_SHOW_DATA
#define SSD3 SEG_SHOW_DATA
#define SSD4 SEG_SHOW_DATA
#define SSD5 SEG_SHOW_DATA
#define SSD6 SEG_SHOW_DATA
#define SSD7 SEG_SHOW_DATA
// 发光二极管使能总控制变量
extern u8 bdata LED_SHOW_DATA;
// 发光二极管使能每位控制变量
extern bit LSD0;
extern bit LSD1;
extern bit LSD2;
extern bit LSD3;
extern bit LSD4;
extern bit LSD5;
extern bit LSD6;
extern bit LSD7;
void refreshLight();
#endifled.c
#include "led.h"
// 数码管显示数据
u8 SEG_NUM_MAP = {
0xC0, // 0
0xF9, // 1
0xA4, // 2
0xB0, // 3
0x99, // 4
0x92, // 5
0x82, // 6
0xF8, // 7
0x80, // 8
0x90, // 9
0xFF// 不显示
};
u8 SEG_POS_DATA = {0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F};
u8 SEG_POS_ENABLE = {1, 1, 1, 1, 1, 1, 1, 1};
u8 SEG_SHOW_DATA = {11, 11, 11, 11, 11, 11, 11, 11};
u8 bdata LED_SHOW_DATA = 0xFF;
sbit LSD0 = LED_SHOW_DATA^0;
sbit LSD1 = LED_SHOW_DATA^1;
sbit LSD2 = LED_SHOW_DATA^2;
sbit LSD3 = LED_SHOW_DATA^3;
sbit LSD4 = LED_SHOW_DATA^4;
sbit LSD5 = LED_SHOW_DATA^5;
sbit LSD6 = LED_SHOW_DATA^6;
sbit LSD7 = LED_SHOW_DATA^7;
void refreshLight()
{
static u8 num = 0;
if (num <= 7)
{
LED_ENABLE = 1;
if (SEG_SHOW_DATA == 11 || SEG_POS_ENABLE)
{
SEG_ENABLE = 0xFF;
}
else
{
SEG_ENABLE = SEG_POS_DATA;
SEG_SHOW_CONTROL = SEG_NUM_MAP];
}
}
else if (num <= 14)
{
SEG_ENABLE = 0xFF;
LED_ENABLE = 0;
LED_SHOW_CONTROL = LED_SHOW_DATA;
}
else
{
SEG_ENABLE = 0xFF;
LED_ENABLE = 1;
}
num++;
if (num > 15)
{
num = 0;
}
}key.h
#ifndef __KEY_H_
#define __KEY_H_
#include "STC32G.H"
#include "STC32_STC8_USB.H"
extern u16 KEY_STATES;
#define KEY1 2 // P32
#define KEY2 3 // P33
#define KEY3 4 // P34
#define KEY4 5 // P35
#define KEY_NORMAL 1 // 未点击
#define KEY_CLICK 2 // 单击
#define KEY_DOUBAL_CLICK 3 // 双击
#define KEY_LONG_CLICK 4 // 长按
#define KEY_SHAKE_TIMEOUT 30
#define KEY_DOUBLE_CLICK_TIMEOUT 500 // 双击超时时间
#define KEY_DOUBLE_CLICK_MASK 10000 // 双击检测掩码位
#define KEY_DOUBLE_CLICK_THRESHOLD (KEY_DOUBLE_CLICK_TIMEOUT + KEY_DOUBLE_CLICK_MASK) // 双击检测阈值
#define KEY_LONG_CLICK_TIMEOUT 3000
void refreshKeyStatus();
u8 readKeyStatus(u8 keyId);
#endifkey.c
#include "key.h"
#define KEY_GPIO P3
u16 KEY_STATES = {0, 0, 0, 0, 0, 0, 0, 0};
/**
* 按键状态刷新
* 每10ms执行一次
*/
void refreshKeyStatus()
{
u8 pos = 0;
for (pos = 0; pos < 8; pos++)
{
if (~KEY_GPIO & (1 << pos))
{
// 判断按键是否已经进入双击检测
if (KEY_STATES > KEY_DOUBLE_CLICK_MASK)
{
// 标记为双击状态
KEY_STATES = KEY_DOUBLE_CLICK_THRESHOLD + 1;
}
else
{
KEY_STATES += 10;
if (KEY_STATES > KEY_LONG_CLICK_TIMEOUT)
{
// 标记为双击状态
KEY_STATES = KEY_LONG_CLICK_TIMEOUT + 1;
}
}
}
else
{
// 30 < s < 3000 进入双击检测
if (KEY_STATES > KEY_SHAKE_TIMEOUT && KEY_STATES < KEY_LONG_CLICK_TIMEOUT)
{
// 按键按下过,并且按键没有触发过长按事件
// 进入双击超时检测
// 11000
KEY_STATES = KEY_DOUBLE_CLICK_THRESHOLD;
KEY_STATES -= 10;
}
else if (KEY_STATES > KEY_DOUBLE_CLICK_MASK && KEY_STATES < KEY_DOUBLE_CLICK_THRESHOLD)
{
// 双击超时计数
KEY_STATES -= 10;
if (KEY_STATES <= KEY_DOUBLE_CLICK_MASK)
{
// 双击超时,重置计数
KEY_STATES = 0;
}
}
else
{
KEY_STATES = 0;
}
}
}
}
u8 readKeyStatus(u8 keyId)
{
if (KEY_STATES > KEY_DOUBLE_CLICK_THRESHOLD)
{
return KEY_DOUBAL_CLICK;
}
else if (KEY_STATES > KEY_LONG_CLICK_TIMEOUT && KEY_STATES < KEY_DOUBLE_CLICK_MASK)
{
return KEY_LONG_CLICK;
}
else if (KEY_STATES > KEY_SHAKE_TIMEOUT && KEY_STATES < KEY_DOUBLE_CLICK_MASK)
{
return KEY_CLICK;
}
return KEY_NORMAL;
}
main.c
#include <STC32G.H>
#include <STC32_STC8_USB.H>
#include "delay_24mhz.h"
#include "led.h"
#include "key.h"
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";
void sys_init()
{
WTST = 0;
EAXFR = 1;
CKCON = 0;
}
void set_interrupt()
{
AUXR &= 0xBF; // 清理 T1 速度控制位
AUXR |= 0x40; // 设置 T1 为 1T 模式,即不分频
TMOD &= 0x0F; // 清理 T1 模式寄存器
TMOD |= 0x40; // 设置 T1 为16位自动重载计数器模式且无需上拉 INT1 引脚
// 设置外部 P3.5 拉低一次触发一次 T1 计数器中断
TL1 = 0xFF; // 计数器T1设置低八位起始值
TH1 = 0xFF; // 计数器T1设置高八位起始值
TF1 = 0; // T1 溢出标志
TR1 = 1; // T1 运行控制位
ET1 = 1; // T1 开关
AUXR &= 0x7F; // 清理 T0 速度控制位
AUXR |= 0x00; // 设置 T0 为 12T 模式
TMOD &= 0xF0; // 清理 T0 模式寄存器
TMOD |= 0x00; // 设置 T0 为16位自动重载定时器模式且无需上拉 INT0 引脚
// 设置 1 毫秒触发一次 T0 计时器中断
TL0 = 0x30; // 计数器T1设置低八位起始值
TH0 = 0xF8; // 计数器T1设置高八位起始值
TF0 = 0; // T0 溢出标志
TR0 = 1; // T0 运行控制位
ET0 = 1; // T0 开关
EA = 1; // 中断总开关
}
void main()
{
sys_init();
usb_init();
set_interrupt();
P4M0 = 0;
P4M1 = 0;
P6M0 = 0;
P6M1 = 0;
P7M0 = 0;
P7M1 = 0;
// P3PU = 1;
// SPE0 = 0;
// SPE1 = 1;
// SPE2 = 0;
// SPE3 = 1;
// SPE4 = 0;
// SPE5 = 1;
// SPE6 = 0;
// SPE7 = 1;
// SSD0 = 8;
// SSD1 = 7;
// SSD2 = 6;
// SSD3 = 5;
// SSD4 = 4;
// SSD5 = 3;
// SSD6 = 2;
// SSD7 = 1;
// LSD0 = 0;
while (1)
{
u8 num = 0;
// delay_1s();
// for (num = 0; num < 8; num++)
// {
// SEG_POS_ENABLE = !SEG_POS_ENABLE;
// }
// LED_SHOW_DATA = ~((~LED_SHOW_DATA) << 1);
// if (LED_SHOW_DATA == 0xFF)
// {
// LSD0 = 0;
// }
if (readKeyStatus(KEY1) == KEY_DOUBAL_CLICK)
{
LSD7 = 0;
delay_ms(100);
LSD7 = 1;
delay_ms(100);
LSD7 = 0;
delay_ms(100);
}
else if (readKeyStatus(KEY1) == KEY_LONG_CLICK)
{
LED_SHOW_DATA = 0xFE;
for (num = 0; num < 8; num++)
{
LED_SHOW_DATA = (LED_SHOW_DATA << 1);
delay_ms(100);
}
}
else if (readKeyStatus(KEY1) == KEY_CLICK)
{
LSD2 = 0;
}
else
{
LED_SHOW_DATA = 0xFF;
}
}
}
void T0_run(void) interrupt 1
{
static u8 count = 0;
if (count >= 9)
{
refreshKeyStatus();
count = 0;
}
count++;
refreshLight();
}
void T1_run(void) interrupt 3
{
// LSD0 = !LSD0;
}
CListery
发表于 2024-7-4 10:51:09
本帖最后由 CListery 于 2024-7-11 15:54 编辑
第二十一课:
通过自增变量记录状态切换(状态机),这样就可以省略掉主函数中的延时操作,从而达到多任务的处理效果
复习:
通过计数实现延时功能
#include <STC32G.H>
#include <STC32_STC8_USB.H>
#include "delay_24mhz.h"
#include "led.h"
#include "key.h"
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";
bit T_100MS_FLAG;
void sys_init()
{
WTST = 0;
EAXFR = 1;
CKCON = 0;
}
void set_interrupt()
{
AUXR &= 0xBF; // 清理 T1 速度控制位
AUXR |= 0x40; // 设置 T1 为 1T 模式,即不分频
TMOD &= 0x0F; // 清理 T1 模式寄存器
TMOD |= 0x40; // 设置 T1 为16位自动重载计数器模式且无需上拉 INT1 引脚
// 设置外部 P3.5 拉低一次触发一次 T1 计数器中断
TL1 = 0xFF; // 计数器T1设置低八位起始值
TH1 = 0xFF; // 计数器T1设置高八位起始值
TF1 = 0; // T1 溢出标志
TR1 = 1; // T1 运行控制位
ET1 = 1; // T1 开关
AUXR &= 0x7F; // 清理 T0 速度控制位
AUXR |= 0x00; // 设置 T0 为 12T 模式
TMOD &= 0xF0; // 清理 T0 模式寄存器
TMOD |= 0x00; // 设置 T0 为16位自动重载定时器模式且无需上拉 INT0 引脚
// 设置 1 毫秒触发一次 T0 计时器中断
TL0 = 0x30; // 计数器 T0 设置低八位起始值
TH0 = 0xF8; // 计数器 T0 设置高八位起始值
TF0 = 0; // T0 溢出标志
TR0 = 1; // T0 运行控制位
ET0 = 1; // T0 开关
EA = 1; // 中断总开关
}
void main()
{
sys_init();
usb_init();
set_interrupt();
P4M0 = 0;
P4M1 = 0;
P6M0 = 0;
P6M1 = 0;
P7M0 = 0;
P7M1 = 0;
// P3PU = 1;
// SPE0 = 0;
// SPE1 = 1;
// SPE2 = 0;
// SPE3 = 1;
// SPE4 = 0;
// SPE5 = 1;
// SPE6 = 0;
// SPE7 = 1;
// SSD0 = 8;
// SSD1 = 7;
// SSD2 = 6;
// SSD3 = 5;
// SSD4 = 4;
// SSD5 = 3;
// SSD6 = 2;
// SSD7 = 1;
// LSD0 = 0;
LED_SHOW_DATA = 0xFF;
while (1)
{
u8 num = 0;
u16 s = 0;
// delay_1s();
// for (num = 0; num < 8; num++)
// {
// SEG_POS_ENABLE = !SEG_POS_ENABLE;
// }
// LED_SHOW_DATA = ~((~LED_SHOW_DATA) << 1);
// if (LED_SHOW_DATA == 0xFF)
// {
// LSD0 = 0;
// }
if (readKeyStatus(KEY1) == KEY_DOUBAL_CLICK)
{
while (readKeyStatus(KEY1) == KEY_DOUBAL_CLICK)
;
s = 0;
do
{
if (T_100MS_FLAG)
{
LSD7 = !LSD7;
T_100MS_FLAG = 0;
while (T_100MS_FLAG)
;
s++;
}
} while (s < 6);
}
else if (readKeyStatus(KEY1) == KEY_LONG_CLICK)
{
LED_SHOW_DATA = 0xFE;
delay_ms(100);
for (num = 0; num < 7; num++)
{
LED_SHOW_DATA = (LED_SHOW_DATA << 1);
delay_ms(100);
}
}
else if (readKeyStatus(KEY1) == KEY_CLICK)
{
LSD2 = 0;
}
else
{
LED_SHOW_DATA = 0xFF;
}
}
}
void T0_run(void) interrupt 1
{
static u8 count = 0;
count++;
if (count % 200 == 0)
{
T_100MS_FLAG = 1;
}
if (count % 10 == 0)
{
refreshKeyStatus();
}
if (count >= 200)
{
count = 0;
}
refreshLight();
}
void T1_run(void) interrupt 3
{
// LSD0 = !LSD0;
}
CListery
发表于 2024-7-22 17:23:38
本帖最后由 CListery 于 2024-7-22 17:24 编辑
第二十二课:
1.学习矩阵键盘的使用
2.学习通过异或位运算实现行列数据扫描
3.学习 switch case 和 #define 的结合使用
/**
* 读取矩阵按键
*/
u8 readMatrxKeyStatus(void)
{
u8 status = 0;
// 行扫描
MATRX_KEY = 0xC0; // 1100 0000P07,P06 拉高
wait();
status = MATRX_KEY ^ 0xC0; // 使用位运算的异或算出哪个行被拉低,假设 P06 被拉低,1000 0000 ^ 1100 0000 = 0100 0000
// 列扫描
MATRX_KEY = 0x0F; // 0000 1111P00-P03 拉高
wait();
status |= MATRX_KEY ^ 0x0F; // 使用位运算的异或算出哪个列被拉低,假设 P03 被拉低,0000 0111 ^ 0000 1111 = 0000 1000
// 1100 1111
// 0000 0000
// if (status != 0)
// {
// printf("0x%02X\r\n", status);
// }
return status;
}
CListery
发表于 2024-7-23 11:07:15
第二十三课:
1. 学习外部中断
void initExtInt0()
{
IT0 = 1; // 下降沿触发
EX0 = 1; // 外部中断 0 使能
IE0 = 0; // 重置中断标志位
}
void initExtInt1()
{
IT1 = 1; // 下降沿触发
EX1 = 1; // 外部中断 1 使能
IE1 = 0; // 重置中断标志位
}
void initExtInt2()
{
EX2 = 1; // 使能位
INT2IF = 0; // 重置中断标志位
}
void initExtInt3()
{
EX3 = 1; // 使能位
INT3IF = 0; // 重置中断标志位
}
void initExtInt4()
{
EX4 = 1; // 使能位
INT4IF = 0; // 重置中断标志位
}
CListery
发表于 2024-7-23 11:27:09
本帖最后由 CListery 于 2024-7-29 10:54 编辑
第二十四课:
1.学习 GPIO 中断
2.学习中断向量超过31时如何处理
3.学习设置中断优先级
CListery
发表于 2024-7-24 10:40:21
用 8051U + SHT40-BD1F 整了个温湿度检测仪
attach://52227.mp4
项目链接 https://oshwhub.com/clistery/temperature-and-humidity-detector-6246631a
CListery
发表于 2024-7-29 16:31:10
第二十五和二十六课:
1. 学习 ADC 模数转换及其寄存器的操作
2. 学习查询方式和中断方式获取 ADC 数值(中断方式只是比查询方式多操作一个 EADC 中断标志位)
void initADC(bit enInterrupt)
{
P1M0 = 0x00;
P1M1 = 0x01;
ADCCFG = 0x2F;
ADCTIM = 0x2F;
ADC_CONTR = 0x80;
EADC = enInterrupt;
}
u16 queryADC(u8 channel)
{
ADC_RES = 0;
ADC_RESL = 0;
ADC_CONTR |= channel;
ADC_START = 1;
ADC_FLAG = 0;
_nop_();
_nop_();
while (!ADC_FLAG)
;
return (ADC_RES << 8) | ADC_RESL;
}
u16 convertVoltage(u16 adcRatio)
{
return 2.5 / 4096 * adcRatio * 1000;
}
CListery
发表于 2024-8-2 08:39:02
fztyp 发表于 2024-8-2 01:09
感谢,学习了~~~~~~~~~
共勉
CListery
发表于 2024-8-2 10:09:35
本帖最后由 CListery 于 2024-8-2 11:42 编辑
第二十七集:
1.通过内部基准电压反推Vref电压
#define VREFH_ADDR CHIPID7
#define VREFL_ADDR CHIPID8
u16 vrefVoltage()
{
u16 innerVref, outVref = 0;
u8 i;
innerVref = (VREFH_ADDR << 8) | VREFL_ADDR;
queryADC(15);
queryADC(15);
for (i = 0; i < 8; i++)
{
outVref += queryADC(15);
}
outVref /= 8;
return 4096L * innerVref / outVref;
}2.读取按键按下
#define ADC_UNIT 256
#define ADC_OFFSET (ADC_UNIT / 4)
#define ADC_MIN_RATIO (ADC_UNIT - ADC_OFFSET)
#define ADC_MAX_RATIO (ADC_UNIT * 16 + ADC_OFFSET)
char adcKeyRead(u8 channel)
{
u16 adcRatio;
char keyIndex = -1;
adcRatio = queryADC(channel);
if (adcRatio < ADC_MIN_RATIO || adcRatio > ADC_MAX_RATIO)
{
return keyIndex;
}
for (keyIndex = 1; keyIndex <= 16; keyIndex++)
{
if(adcRatio > ADC_UNIT * keyIndex - ADC_OFFSET && adcRatio < ADC_UNIT * keyIndex + ADC_OFFSET)
{
return keyIndex;
}
}
return keyIndex;
}
3.每100ms调用一次按键读取,按键按下3秒触发长按
#define ADC_UNIT 256
#define ADC_OFFSET (ADC_UNIT / 4)
#define ADC_MIN_RATIO (ADC_UNIT - ADC_OFFSET)
#define ADC_MAX_RATIO (ADC_UNIT * 16 + ADC_OFFSET)
char adcKeyRead(u8 channel)
{
u16 adcRatio;
u8 i;
char keyIndex = -1;
static u8 times = 0;
adcRatio = queryADC(channel);
if (adcRatio < ADC_MIN_RATIO || adcRatio > ADC_MAX_RATIO)
{
times = 0;
return keyIndex;
}
for (i = 1; i <= 16; i++)
{
if (adcRatio > ADC_UNIT * i - ADC_OFFSET && adcRatio < ADC_UNIT * i + ADC_OFFSET)
{
keyIndex = i;
times++;
if (times > 30)
{
times = 30;
keyIndex = ~keyIndex;
}
return keyIndex;
}
}
return keyIndex;
}