乐高
发表于 2025-1-28 21:01:11
第十五集 定时器做计数器
void INT1_Count_Init(void)
{
IT1 =1;
EX1 = 1;
}
void INT1_Isr() interrupt 2
{
Count1 = Count_T1;
if(Count1>5)
{
Count = Count1/10;
Count_INT1++;
}
Count_T1 = 0;
}
void Timer1_Isr(void) interrupt 3
{
Count_T1++;
}
void Timer1_Init(void) //10毫秒@24.000MHz
{
AUXR &= 0xBF; //定时器时钟12T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0xE0; //设置定时初始值
TH1 = 0xB1; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1 = 1; //使能定时器1中断
}579
乐高
发表于 2025-1-30 13:22:15
第十六集 DS18B20测温课后小任务
把温度传感器的数值显示改成两位数,加一小数点,然后小数点后再显示一位。
先在程序中加入转换精度可预设的代码:
void DS18B20_Init(void)
{
DS18B20_Reset(); //1.发送复位命令
DS18B20_WriteByte(0xcc); //2.跳过ROM命令
DS18B20_WriteByte(0x4e); //3.写暂存器指令4E
DS18B20_WriteByte(0x4b); //4.写TH最高限值温度
DS18B20_WriteByte(0x00); //5.写TL最低限值温度
DS18B20_WriteByte(0x7f); //写配置寄存器4
//0x1f : 0.5000°C转换时间93.75ms
//0x3f : 0.2500°C转换时间187.5ms
//0x5f : 0.1250°C转换时间375ms
//0x7f : 0.0625°C转换时间750ms
} Temp_18b20 = Temp_18b20/1000; //除1000,再在十位数之后加上小数点,则显示出需要的数值。
u8 SEG_NUM1[]= //带小数点
{
0xBF, /*'0', 0*/
0x86, /*'1', 1*/
0xDB, /*'2', 2*/
0xCF, /*'3', 3*/
0xE6, /*'4', 4*/
0xED, /*'5', 5*/
0xFD, /*'6', 6*/
0x87, /*'7', 7*/
0xFF, /*'8', 8*/
0xEF, /*'9', 9*/
0xF7, /*'A', 10*/
0xFC, /*'B', 11*/
0xB9, /*'C', 12*/
0xDE, /*'D', 13*/
0xF9, /*'E', 14*/
0xF1, /*'F', 15*/
0x40, /*'-', 16*/
0x00, /*' ', 17*/
0x80, /*'.', 18*/
};只显示三位数字
void SEG_Show_U32(u32 num)
{
u8 i;
for(i=0;i<3;i++) //只显示三位数字
{
passward = num%10;
num /= 10;
}
}
乐高
发表于 2025-1-30 18:34:54
第十六集 DS18B20测温课后小练
鉴于实验箱没有真正的电源开关机按键,所以采用右侧的某个键盘象征性的作为开关机键使用,但不能真正切断电源。
由于四个独立按键P32\P33\P34\P35都已经被占用,所以考虑用矩阵键盘来实现此课后小练的功能。
此处采用按键1(P01)作为开关机键,按键3(P03)作为摄氏华氏温度显示方式切换键。
下面是main主函数
while(1)
{
//
// if (bUsbOutReady) //如果接收到了数据
// {
// //USB_SendData(UsbOutBuffer,OutNumber); //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
//
// usb_OUT_done(); //
// }
Task_Pro_Handler_Callback(); //执行功能函数
if(C_F == 0)
SEG_Show_U32(Temp_18b20); //如果C_F=0,则显示摄氏温度
else
SEG_Show_U32(Temp_F); //如果C_F=1,则显示华氏温度
if(P01==0) //按键1用作开关机键
{
Delay20ms();
if(P01==0)
{
run = !run;
Key_num=0;
}
while(P01==0);
}
}
}
void Key_Task(void)
{
Key_num++;
if(P03==0) //按键3用作摄氏/华氏显示切换键
{
Key_Vol++;
Key_num = 0;
if(Key_Vol==5)
{
C_F = !C_F;
}
}
else
Key_Vol = 0;
if(Key_num==3000) //30秒无操作,关机
run = 0;
}void SEG_Task(void)
{
u8 num = 0;
if(run==0) //未按P01键,熄屏,代表未开机
{
tim =0;
SEG_Show0(); //未开机,熄屏
}
else
{
tim++;
if(tim>2000) //2秒钟后
{
if(C_F)
SEG_Show4(); //如果C_F=1,则显示华氏温度
else
SEG_Show3(); //如果C_F=0,则显示摄氏温度
tim = 2000;
}
else //2秒内
{
if(C_F)
SEG_Show2(); //如果C_F=1,显示”---F"
else
SEG_Show1(); //如果C_F=0,显示”---C“
}
}
Seg_no ++;
if( Seg_no>7 )
Seg_no=0;
}583
乐高
发表于 2025-1-31 19:06:21
第十六集 DS18B20测温数码管闪烁问题
数码管闪烁是由于18B20的读写中采用了阻塞延时程序(软件延时)影响了主程序的运行,要采用非阻塞程序才能使数码管不闪烁。
非阻塞写法有多种,我现在找到了一种:定时器中断方式。用定时器中断来实现精确延时。
本程序中使用定时器1,设置定时长度为60us, 480us就是8个定时周期,420us正好是7个定时周期。
18B20全部的读写过程都在定时中断中实现
void Timer1_Isr(void) interrupt 3 //定时时间60us,就是60us走一步
{
switch(t1_num)
{
case 0 :
case 33 :
bz = 1; DQ = 0; break;
case 8 : //0-8步时间间隔480us
case 41 :
DQ = 1; break;
case 9 : //8-9步时间间隔60us
case 42 :
bz = DQ; break;
case 16 : //9-16步时间间隔420us
case 49 :
if(bz) //如果bz为1,则初始化失败,关闭定时器1,等待下次采集。
{
TR1 = 0;
break;}
else //如果bz为0,则开始写入命令cmd
{
dat = cmd;
DQ = 0;
Delay1us();
dat >>= 1;
DQ = CY;
break;
}
case 17 : //cmd
case 18 :
case 19 :
case 20 :
case 21 :
case 22 :
case 23 :
case 25 : //cmd
case 26 :
case 27 :
case 28 :
case 29 :
case 30 :
case 31 :
case 50 : //cmd
case 51 :
case 52 :
case 53 :
case 54 :
case 55 :
case 56 :
case 58 : //cmd
case 59 :
case 60 :
case 61 :
case 62 :
case 63 :
case 64 :
DQ = 1;
Delay1us();
DQ = 0;
Delay1us(); //开始下一位
dat >>= 1;
DQ = CY;
break;
case 24 :
case 57 :
DQ = 1;
Delay1us(); //写第一个字节结束
dat = cmd;
DQ = 0;
Delay1us(); //写第二个字节
dat >>= 1;
DQ = CY;
break;
case 32 :
DQ = 1;
Delay1us(); //写第二个字节结束 0x44
case 65 :
DQ = 1;
Delay1us(); //0xbe 命令发完
dat = 0;
dat >>= 1;
DQ = 0;
Delay1us();
DQ = 1;
Delay1us();
if(DQ) dat |= 0x80;
break;
case 66 :
case 67 :
case 68 :
case 69 :
case 70 :
case 71 :
case 72 :
case 74 :
case 75 :
case 76 :
case 77 :
case 78 :
case 79 :
case 80 :
dat >>= 1;
DQ = 0;
Delay1us();
DQ = 1;
Delay1us();
if(DQ) dat |= 0x80;
break;
case 73 :
TempL = dat;
dat = 0;
dat >>= 1;
DQ = 0;
Delay1us();
DQ = 1;
Delay1us();
if(DQ) dat |= 0x80;
break;
case 81 :
TempH = dat;
TR1 = 0;
break;
default : break;
}
t1_num++;
}void Timer1_Init(void) //60微秒@24.000MHz
{
AUXR &= 0xBF; //定时器时钟12T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x88; //设置定时初始值
TH1 = 0xFF; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1 = 1; //使能定时器1中断
}温度值计算放在main函数内,因为怕60us时间不够用。
while(1)
{
//
// if (bUsbOutReady) //如果接收到了数据
// {
// //USB_SendData(UsbOutBuffer,OutNumber); //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
//
// usb_OUT_done(); //
// }
Task_Pro_Handler_Callback(); //执行功能函数
if( TempH & 0x80 ) //如果最高位是1,这个就是负数
{
MinusFlag = 1;
Temp = (((u16)TempH << 8) | ((u16)TempL << 0));
Temp = (~Temp) +1;
Temp_18b20 = (u32)Temp*625;
}
else
{
MinusFlag = 0;
Temp = (((u16)TempH << 8) | ((u16)TempL << 0));
Temp_18b20 = (u32)Temp*625;
Temp_18b20 = Temp_18b20/1000; //除1000,再在十位数之后加上小数点,则显示出需要的数值。
Temp_F = Temp_18b20*9/5 + 320; //转换成华氏温度值
}
if(C_F == 0)
SEG_Show_U32(Temp_18b20); //如果C_F=0,则显示摄氏温度
else
SEG_Show_U32(Temp_F); //如果C_F=1,则显示华氏温度
if(P01==0) //按键1用作开机键
{
Delay20ms();
if(P01==0)
{
run = !run;
Key_num=0;
}
while(P01==0);
}
}
}u8 t1_num = 0;
u8 cmd_n = 0;
void Key_Task(void)
{
Key_num++;
if(Key_num%50==0) //本处设置温度采集周期,500ms采集一次温度
{
//num = 0;
t1_num = 0; cmd_n = 0; //nodevice = 0;
TR1 = 1;
}
if(P03==0) //按键3用作摄氏/华氏显示切换键
{
Key_Vol++;
Key_num = 0;
if(Key_Vol==5)
{
C_F = !C_F;
}
}
else
Key_Vol = 0;
if(Key_num==3000) //30秒无操作,关机
run = 0;
}
其他的程序跟前面课后小练中的一样,DS18B20_ReadTemp函数不用了。
585
silas3082
发表于 2025-2-1 16:00:49
<p>楼主没考虑用掉电模式PD=1来实现关机吗?</p>
乐高
发表于 2025-2-2 13:56:19
silas3082 发表于 2025-2-1 16:00
楼主没考虑用掉电模式PD=1来实现关机吗?
谢谢同学的提醒,我还没有用过MCU的掉电功能呢,这次尝试一下。
乐高
发表于 2025-2-2 15:06:29
第十六集 DS18B20测温 掉电及掉电唤醒
主程序main函数while(1)循环前加入掉电及掉电唤醒功能
P0IM0 = 0x00; //下降沿中断
P0IM1 = 0x00;
//PINIPH=0x01; //设置P0口GPIO中断为最高优先级
//PINIPL=0x01;
P0INTE = 0x02; //使能P01中断功能
P0WKUE = 0x02; //使能P01中断掉电唤醒功能
EA = 1; //IE |= 0X80;
while (DeviceState != DEVSTATE_CONFIGURED); //等待USB完成配置
//P40 = 0;
P06 = 0; //给P01,P03提供低电平
_nop_();
_nop_();
PD = 1 ; //MCU进入掉电模式 , PCON = 0x02;
_nop_(); //掉电模式被唤醒后,MCU首先会执行此语句
_nop_(); //然后再进入中断服务程序
_nop_();
_nop_();
while(1)void common_irs() interrupt 37 //使用GPIO,P01口中断
{
u8 intf;
intf = P0INTF;
P0INTF = 0x00; //清空中断标志位,以正常保证中断请求
if(intf & 0x02)
{
run = !run;
}
}void SEG_Task(void)
{
if(run==0) //未按P01键,熄屏,代表未开机
{
tim =0;
SEG_Show0(); //未开机,熄屏
PD = 1; //熄屏以后再掉电,不然数码管还有残余显示
Key_num = 0;
}
else
{
PD = 0;
tim++;
if(tim>2000) //2秒钟后
{
if(C_F)
SEG_Show4(); //如果C_F=1,则显示华氏温度
else
SEG_Show3(); //如果C_F=0,则显示摄氏温度
tim = 2000;
}
else //2秒内
{
if(C_F)
SEG_Show2(); //如果C_F=1,显示”---F"
else
SEG_Show1(); //如果C_F=0,显示”---C“
}
}
Seg_no ++;
if( Seg_no>7 )
Seg_no=0;
}
silas3082
发表于 2025-2-4 12:38:56
<p>https://www.stcaimcu.com/forum.php?mod=viewthread&tid=12763&extra=&page=4</p>
<p>感谢提供的思路,我也完成作业了</p>
香河英茂工作室
发表于 2025-2-26 09:37:18
感谢分享,期待15、16集作业后续。
香河英茂工作室
发表于 2025-2-26 09:37:26
感谢分享,期待15、16集作业后续。