未元星系
发表于 2024-5-27 22:57:59
本帖最后由 未元星系 于 2024-5-27 22:59 编辑
自学开天斧第⑤课:
学习内容:1、外部中断
学习简介:STC8H8K64U单片机内置五个外部中断,INT0,INT1,INT2,INT3,INT4;分别对应引脚:P3.2,P3.3,P3.6,P3.7,P3.0;分别对应中断号:interrupt 0,2,10,11,16;
其中INT0和INT1即支持上升沿/下降沿中断(同时支持两种方式引发中断),也支持下降沿中断,INT2,INT3,INT4只支持下降沿中断。
外部INT0,INT1所连引脚正好为两个独立按键,本次实验:主函数执行死循环,设INT0,INT1为下降沿触发,若P3.3按键按下,LED交替闪烁两回合;若P3.2按下,LED闪烁两次
程序代码:
#define MAIN_Fosc 24000000L
void Delay_ms(unsigned int ms)
{
unsigned int i;
do{
i = MAIN_Fosc / 10000;
while(--i);
}while(--ms);
}
void main()
{
P2M0 = 0x00; P2M1 = 0x00; //P2口设为准双向口模式
P3M0 = 0x00; P3M1 = 0x00; //P3口设为准双向口模式
P2 = 0xff; //熄灭全部LED
P2 |= 0x30; //P3.2, P3.3口设为高电平
IT0 = 1; //外部中断0设置为下降沿触发(若IT0=1则设置为上升沿+下降沿触发)
EX0 = 1; //使能INT0
IT1 = 1; //外部中断1设置为下降沿触发(若IT1=1则设置为上升沿+下降沿触发)
EX1 = 1; //使能INT1
EA = 1; //打开总中断
while(1){} //死循环
}
void INT0_Routine(void)interrupt 0
{
P2 = 0x55;Delay_ms(150);
P2 = 0xAA;Delay_ms(150);
P2 = 0x55;Delay_ms(150);
P2 = 0xAA;Delay_ms(150);
P2 = 0xff;
}
void INT1_Routine(void)interrupt 2
{
P2 = 0x00;Delay_ms(100);
P2 = 0xff;Delay_ms(100);
P2 = 0x00;Delay_ms(100);
P2 = 0xff;Delay_ms(100);
}
代码现象:P3.3按键按下,LED交替闪烁两回合;P3.2按下,LED闪烁两次
存在问题:按键按下会产生抖动,目前外部中断里的消抖本人目前也没完全弄明白,所以此程序可能会导致按一下按键触发多次中断的现象。
未元星系
发表于 2024-5-28 23:23:29
本帖最后由 未元星系 于 2024-5-28 23:26 编辑
自学开天斧第⑥课:
学习内容:1、定时器延时
学习简介:前期我学习的延时函数是严重占用单片机进程的,实际编程中不是十分提倡,今天开始学习定时器延时。
实验内容:用定时器0进行延时,流水灯
程序代码:
bit bit_1ms;
unsigned int delay;
void Timer0_Init(void)//定时1ms
{
AUXR |= 0x80;//设置定时器为1T模式,即不分频
TMOD &= 0xF0;//保留高四位(定时器1),低四位全为0:设置定时器/计数器0为定时器、GATE置0、16位自动重装模式
TL0 = 0x3F; //初始值65535 - 24000 = 41535 = 1010 0010 0011 1111,即高8位为0xA2,低8位为0x3F
TH0 = 0xA2; //
TF0 = 0; //清零TF0中断标志位
TR0 = 1; //定时器开始计时
ET0 = 1; //开启定时器0中断
}
void main()
{
P2M0 = 0x00; P2M1 = 0x00; //P2口设为准双向口模式
Timer0_Init();
EA = 1; //打开总中断
while(1)
{
static unsigned char j;
if(bit_1ms == 1)
{
bit_1ms = 0;
delay++;
}
if(delay >= 100)//流水灯流动速度:100ms
{
delay = 0;
P2 = ~(0x01 << j);
j ++ ;
if(j > 7){j = 0;}
}
}
}
void Timer0_Routine(void) interrupt 1 //定时器0中断
{
bit_1ms = 1;//设置标志位,bit_1ms清零后置1说明时间过了1ms
}
代码现象:循环流水灯,速度100ms
未元星系
发表于 2024-5-31 22:08:13
本帖最后由 未元星系 于 2024-5-31 23:11 编辑
自学开天斧第⑦课:
学习内容:1、串口通信
学习简介:STC8H8K64U单片机有四个串口,都为全双工异步串行通信接口,串口1有四种工作模式,串口2、3、4有两种(本次学习串口1的模式1),串口1可切换4组引脚,串口2、3、4能切换2组
实验项目:1.单片机向电脑发送数据:开天斧4个板载按键,按下分别向电脑发送 “你好呀!”“很高兴认识你!”“祝你好运!”“再见!”
2.电脑向单片机发送数据:STC-ISP串口助手向单片机发送16进制数,8个板载LED按照2进制点亮(注意将串口发送调为HEX模式)
程序代码:
#define MAIN_Fosc 24000000L
sbit KEY1 = P3^2; sbit KEY2 = P3^3; sbit KEY3 = P3^4; sbit KEY4 = P3^5; // 板载的4个按键
bit B_1ms; // 1毫秒标志
bit key1flag; bit key2flag; bit key3flag; bit key4flag; // 按键1按下标志
bit blinkflag; // 闪烁标志
bit busy; //忙状态
char key1delay; char key2delay; char key3delay; char key4delay; // 4个按键计时
void Timer0_Init(void) // 1毫秒@24MHz
{
AUXR |= 0x80; // 定时器时钟1T模式
TMOD &= 0xF0; // 设置定时器模式
TL0 = 0x3F; // 设置定时初始值
TH0 = 0xA2; // 设置定时初始值
TF0 = 0; // 清除TF0标志
TR0 = 1; // 定时器0开始计时
ET0 = 1; // 使能定时器0中断
}
void Uart1Init() // UART1初始化
{
P_SW1 &= 0x3F; //设置串口1引脚为P3.0,P3.1
SCON = 0x50; // 模式1(8位数据)、接收使能
PCON |= 0x00; //设置波特率不加倍,关闭帧错检测功能
T2L = BRT;
T2H = BRT >> 8; // 波特率对应的重装载值
AUXR |= 0x15; // 定时器2做波特率发生器 1T模式 启动定时器2
busy = 0; // 清零忙标志
}
void UartSend(char dat) // 发送字符函数
{
while (busy); // 当串口忙时,等待
busy = 1; // 忙标志置1
SBUF = dat; // 发送数据dat
}
void UartSendStr(char *p) // 发送字符串函数
{while (*p) {UartSend(*p++);}} // 逐个发送字符串的字符
void main()
{
P2M0 = 0x00; P2M1 = 0x00;
P3M0 = 0x00; P3M1 = 0x00;
P2 = 0xff; P3 = 0xff;
Timer0_Init(); // 定时器0初始化
Uart1Init(); // 串口1初始化
ES = 1; // 使能串口1中断
EA = 1; // 使能EA总中断
while (1)
{
if(KEY1 == 0) // P3.2按键按下
{
if(key1flag == 0) // 当按下标志为0时
{
if(B_1ms == 1) key1delay++; // 按键延时,每1毫秒+1
if(key1delay >=10) // 按下时间大于10毫秒
{key1delay = 0;key1flag = 1;UartSendStr("你好呀!\r\n");} // 清零按键延时按下标志置1发送字符串“你好呀!”
}
}
if(KEY1 == 1){key1flag = 0;key1delay = 0;} // 按键松开清零按键按下标志清零按键延时
if(KEY2 == 0) // P3.3按键按下
{
if(key2flag == 0) // 当按下标志为0时
{
if(B_1ms == 1) key2delay++; // 按键延时,每1毫秒+1
if(key2delay >=10) // 按下时间大于10毫秒
{key2delay = 0;key2flag = 1;UartSendStr("很高兴认识你!\n");} // 清零按键延时按下标志置1发送字符串“很高兴认识你!”
}
}
if(KEY2 == 1){key2flag = 0;key2delay = 0;} // 按键松开清零按键按下标志清零按键延时
if(KEY3 == 0) // P3.3按键按下
{
if(key3flag == 0) // 当按下标志为0时
{
if(B_1ms == 1) key3delay++; // 按键延时,每1毫秒+1
if(key3delay >=10) // 按下时间大于10毫秒
{key3delay = 0;key3flag = 1;UartSendStr("祝你好运!\n");} // 清零按键延时按下标志置1发送字符串“祝你好运!”
}
}
if(KEY3 == 1){key3flag = 0;key3delay = 0;} // 按键松开清零按键按下标志清零按键延时
if(KEY4 == 0) // P3.3按键按下
{
if(key4flag == 0) // 当按下标志为0时
{
if(B_1ms == 1) key4delay++; // 按键延时,每1毫秒+1
if(key4delay >=10) // 按下时间大于10毫秒
{key4delay = 0;key4flag = 1;UartSendStr("再见!\n");} // 清零按键延时按下标志置1发送字符串“再见!”
}
}
if(KEY4 == 1){key4flag = 0;key4delay = 0;} // 按键松开清零按键按下标志清零按键延时
}
void Timer0_Isr(void) interrupt 1
{
B_1ms = 1; // 1毫秒标志
}
void Uart1Isr() interrupt 4
{
if (TI == 1) // 发送中断标志置1时
{
TI = 0; // 清零发送中断标志
busy = 0; // 清零忙状态
}
if (RI == 1) // 接收中断标志置1时
{
RI = 0;
P2 = ~SBUF; //P2口赋值SBUF取反结果
}
}
未元星系
发表于 2024-6-1 20:23:37
本帖最后由 未元星系 于 2024-6-1 20:25 编辑
自学开天斧第⑧课:
学习内容:1、掉电模式(也叫省电模式/停电模式/主时钟停振)2、空闲模式
学习简介:STC8H8K64U单片机有两种低功耗模式:掉电模式和空闲模式。
1、掉电模式:CPU和外设都停止工作。 2、空闲模式:CPU停止工作,外设正常运行。
掉电模式可以通过定时器、中断、串口、比较器中断、低压检测中断、掉电唤醒定时器、I2C等方式唤醒
使用低功耗模式需要调用电源控制寄存器PCON
实验项目:1.单片机一遍流水灯后进入掉电/空闲模式,P3.2口外部中断唤醒继续一边流水灯,再次进入掉电/空闲模式,循环这个过程。
程序代码:
#include <STC8H.H>
#define MAIN_Fosc24000000L
void Delay_ms(unsigned int ms)
{unsigned int i;do{i = MAIN_Fosc / 10000;while(--i);}while(--ms);}
void main()
{
unsigned char i;
unsigned char a = 0x01;
P2M0 = 0x00; P2M1 = 0x00;
P3M0 = 0x00; P3M1 = 0xc3;
P2 = 0x01;
IT0 = 1; //外部中断0设置为下降沿触发
EX0 = 1; //使能INT0
EA = 1; //打开总中断
while(1)
{
for(i = 0;i < 8;i++)
{
P2 = ~(a << i);
Delay_ms(100);
P2 = 0xff;
}
PCON |= 0x02;//开启掉电模式/ |= 0x01为省电模式
}
}
void INT0_Routine(void) interrupt 0
{}
未元星系
发表于 2024-6-2 23:35:58
本帖最后由 未元星系 于 2024-6-2 23:41 编辑
自学开天斧第⑨课:
学习内容:1、printf函数串口打印
学习简介:我们初学C语言的第一课通常是printf打印"Hello World!",STC8H8K64U单片机也可以调用printf函数,将信息打印在串口助手里,使用这一功能需要以下操作:
1、添加#inculde <stdio.h>头文件
2、配置串口
3、重构putchar函数
4、调用printf函数
实验项目:单片机每隔1秒在串口助手打印一次数据;串口助手向单片机发送数据时,P2口LED按数据2进制亮灭。
程序代码:
#include <STC8H.H>
#include <stdio.h>
#define MAIN_Fosc 24000000L
#define BRT (65536 - (MAIN_Fosc / 115200+2) / 4)
bit busy;
bit B_1ms; // 1毫秒标志
bit x; //串口接收标志
sbit KEY1 = P3^5;
int delay; // 计时
unsigned char number1; // 测试变量,发送次数 unsigned char 类型
char number2; // 测试变量,char 类型
unsigned int humidi; // 测试变量,unsigned int 类型
int temper1; // 测试变量,int类型
int temper2; // 测量变量,int类型
float temper3; // 测试变量,float类型
float temper4; // 测试变量,float类型
unsigned char str[] = "STC8H8K64U-开天斧3.2"; // 字符串数组
void Delay_ms(unsigned int ms)
{
unsigned int i;
do{
i = MAIN_Fosc / 10000;
while(--i);
}while(--ms);
}
void Timer0_Init(void) // 1毫秒@24MHz
{
AUXR |= 0x80; // 定时器时钟1T模式
TMOD &= 0xF0; // 设置定时器模式
TL0 = 0x3F; // 设置定时初始值
TH0 = 0xA2; // 设置定时初始值
TF0 = 0; // 清除TF0标志
TR0 = 1; // 定时器0开始计时
ET0 = 1; // 使能定时器0中断
}
void Uart1Init() // UART1初始化
{
P_SW1 &= 0x3F; //设置串口1引脚为P3.0,P3.1
SCON = 0x50; // 模式1(8位数据)、接收使能
PCON |= 0x00; //设置波特率不加倍,关闭帧错检测功能
T2L = BRT;
T2H = BRT >> 8; // 波特率对应的重装载值
AUXR |= 0x15;
busy = 0; // 清零忙标志
}
void UartPutc(unsigned char dat)
{
busy = 1;
SBUF = dat;
while(!TI);
TI = 0;
busy = 0;
}
char putchar(char c) //重构putchar函数
{
UartPutc(c);
return c;
}
void main()
{
P2M0 = 0x00; P2M1 = 0x00;
P3M0 = 0x00; P3M1 = 0x00;
P2 = 0xff; P3 = 0xff;
ES = 1; // 使能串口1中断
EA = 1; // 使能EA总中断
Timer0_Init();
Uart1Init();
temper1 = 25; // 测试变量,温度1 = 25度 int类型,正数
temper2 = -2; // 测试变量,温度2 = -2度 int类型,负数
temper3 = 25.6; // 测试变量,温度3 = 25.6度 float类型,正数
temper4 = -2.5; // 测试变量,温度4 = -2.5度 float类型,负数
humidi = 50; // 测试变量,湿度= 50 % unsigned int类型
while(1)
{
if(B_1ms == 1) // 1毫秒到
{
B_1ms = 0; // 清零1毫秒标志
delay++; // 按键延时,每1毫秒+1
}
if(delay >= 1000) // 按下时间大于1000毫秒
{
delay = 0; // 清零按键延时
number1++; // 每次发送后number1+1
number2++; // 每次发送后number2+1
// Keil 扩展了 b h l,对应字节宽度,b-8位,h-16位(默认),l-32位
// printf中有符号对应 %d,无符号对应%u 或 %x, 浮点型:%f,字符串 %s
// u8对应 %bu 或 %bx, char 对应 %bd
// u16 对应 %hu 或 %hx, int对应 %d 或 %hd
// u32 对应 %lu 或 %lx,long 对应 %ld
printf("\n");
printf("第%bu次发送\r ",number1); // 发送unsigned char类型,number1是u8类型,需要使用 %bu 或 %bx
printf("第%bd次打印\r ",number2); // 发送 char类型,number2是 char类型,需要使用 %bd
printf("湿度:%hu%%\r ",humidi); // 发送unsigned int类型,humidi是 u16 类型,需要使用 %hu 或 %hx
printf("温度1:%hd℃\r ",temper1); // 发送int 类型,temper是 int类型,需要使用 %hd
printf("温度2:%hd℃\r ",temper2); // 发送int 类型,temper是 int 类型,需要使用 %hd
printf("温度3:%4.1f℃\r ",temper3); // 发送float类型,temper是 int 类型,需要使用 %f,2位整数,1位小数 %4.1f
printf("温度4:%3.1f℃\r\n",temper4); // 发送 float类型,temper是 int类型,需要使用 %f,1位整数,1位小数 %3.1f
printf("%s\r ",str); // 发送字符串"STC8H8K64U-开天斧三.2"
printf("HelloWorld!\r\n\n"); // 发送字符串"HelloWorld!"
}
if(x == 1)
{
x = 0;
P2 = ~SBUF;
}
}
}
void Timer0_Routine(void) interrupt 1
{
B_1ms = 1; // 1毫秒标志
}
void Uart1Isr() interrupt 4
{
if (TI == 1) // 发送中断标志置1时
{
TI = 0; // 清零发送中断标志
busy = 0; // 清零忙状态
}
if (RI == 1) // 接收中断标志置1时
{
RI = 0;
x = 1;
}
}
(备注:视频演示发送的串口数据中,湿度出了错误,原因是一开始定义湿度取成了char类型,printf引用为int型,代码已纠正。)
soma
发表于 2024-6-3 11:34:36
学的真好
未元星系
发表于 2024-6-3 11:52:59
zhaoye818 发表于 2024-6-3 07:55
{:4_196:}{:4_196:}
未元星系
发表于 2024-6-3 11:53:32
soma 发表于 2024-6-3 11:34
学的真好
感谢认可,会继续坚持学习{:4_196:}
未元星系
发表于 2024-6-3 23:10:49
2024年6月3日,今天计划学习串口DMA发送与接收,DMA发送实验成功,但DMA接收代码仍有错误,暂未找出原因,明日继续学习。
vb2002
发表于 2024-6-4 00:58:54
为什么你学的这么快,我学的那么慢啊