main.c
/** * 产品名称:环境监控设备 * 产品定义: * (1)单片机每隔1秒通过DS18B20温度传感器采集一次温度。 * (2)单片机每隔200毫秒采集一次DS1302时钟芯片的时间。 * (3)通过数码管实时显示时间,格式为日-小时-分-秒。 * (4)通过串口向上位机发送格式提示字符串,设置温度上限和下限; * 格式为xx-yy * (5)可以通过串口设置温度上限和下限。 * (6)单片机将每次采集到的温度实时通过串口传送给上位机; * 如果温度超过阈值,都将给出警告提示。 * 版本:debug * 完成时间:2015.4.5 17:00 * 作者:宁静致远 */ #include <stc15f2k60s2.h> #include <stdio.h> #include "stdint.h" #include "DS1302.h" #include "DS18B20.h" #include "UART.h" #include "digitalTube.h" #include "timer.h" //#include "main.h" volatile bit flag1s = 0, flag200ms = 0; uint8_t serialBuf[10]; volatile uint8_t front = 0, rear = 0; void main() { int16_t temperature; uint8_t intPart, decPart; uint8_t maxTempInt = 21, maxTempDec = 0; uint8_t minTempInt = 20, minTempDec = 0; uint8_t intPart0, intPart1, decPart1; //bit tempSign; sTime_t stime; uint8_t preSec; configTmr0(2); configUART(9600); //设置正确的波特率 startDS18B20(); //!! DS1302init(); ES = 0; TI = 1; printf("input format: xx-yy\n"); while (TI == 0); TI = 0; ES = 1; while (1) { if (flag1s) { //温度每隔1s采样一次 flag1s = 0; getDS18B20temperature(&temperature); startDS18B20(); //!! intPart = getIntPart(temperature); decPart = getDecPart(temperature); intPart1 = intPart / 10 + '0'; intPart0 = intPart % 10 + '0'; decPart1 = decPart + '0'; if (intPart > maxTempInt || (intPart == maxTempInt && decPart > maxTempDec)) { ES = 0; TI = 1; printf("warning: too high\n"); while (TI == 0); TI = 0; ES = 1; } else if (intPart < minTempInt || (intPart == minTempInt && decPart < minTempDec)) { ES = 0; TI = 1; printf("warning: too low\n"); while (TI == 0); TI = 0; ES = 1; } else { ES = 0; TI = 1; //printf("The temp is %u %u\n", intPart, decPart); //printf("decPart: %hu\n", decPart); printf("The temperature is %c%c.%c\n", intPart1, intPart0, decPart1); while (TI == 0); TI = 0; ES = 1; } } if (flag200ms) { //时间每隔200ms采样一次 flag200ms = 0; getRealTime(&stime); if (stime.sec != preSec) { //BCD码!为16进制! dspBuf[0] = stime.day >> 4; dspBuf[1] = stime.day & 0x0F; dspBuf[2] = stime.hour >> 4; dspBuf[3] = stime.hour & 0x0F; dspBuf[4] = stime.minute >> 4; dspBuf[5] = stime.minute & 0x0F; dspBuf[6] = stime.sec >> 4; dspBuf[7] = stime.sec & 0x0F; preSec = stime.sec; } } if (rear == 5) { rear = 0; minTempInt = (serialBuf[0] - '0') * 10 + serialBuf[1] - '0'; maxTempInt = (serialBuf[3] - '0') * 10 + serialBuf[4] - '0'; } } } void UARTreceive() { serialBuf[rear] = SBUF; if (rear == 9) rear = 0; else rear++; } void UARTtransmit() { } void tmr0ISR() interrupt 1 { static uint16_t cnt = 0; TL0 = tmr0LowByte; TH0 = tmr0HighByte; digitalTubeScan(); if (cnt == 499) { cnt = 0; flag1s = 1; } else { cnt++; } if ((cnt+1) % 100 == 0) flag200ms = 1; }
DS1302.h
#ifndef _DS1302_H #define _DS1302_H sbit DS1302_CE = P1^3; //!!!检查引脚是否正确! sbit DS1302_SCLK = P1^7; sbit DS1302_IO = P2^3; typedef struct sTime { //日期时间结构体定义 uint16_t year; //年 uint8_t mon; //月 uint8_t day; //日 uint8_t hour; //时 uint8_t minute; //分 uint8_t sec; //秒 uint8_t week; //星期 } sTime_t; void DS1302byteWrite(uint8_t dat); uint8_t DS1302byteRead(); void DS1302singleWrite(uint8_t reg, uint8_t dat); uint8_t DS1302singleRead(uint8_t reg); void DS1302burstWrite(uint8_t * dat); void DS1302burstRead(uint8_t * dat); void getRealTime(sTime_t * time); void setRealTime(sTime_t * time); void DS1302init(); #endif // _DS1302_H
DS1302.c
/* * 文件名:DS1302.c * 描 述:实时时钟芯片DS1302驱动模块 */ #include <stc15f2k60s2.h> #include "stdint.h" #include "DS1302.h" /* 发送一个字节到DS1302通信总线上 */ void DS1302byteWrite(uint8_t dat) { uint8_t mask; for (mask = 0x01; mask != 0; mask <<= 1) { //低位在前,逐位移出 if ((mask & dat) != 0) //首先输出该位数据 DS1302_IO = 1; else DS1302_IO = 0; DS1302_SCLK = 1; //然后拉高时钟 DS1302_SCLK = 0; //再拉低时钟,完成一个位的操作 } DS1302_IO = 1; //最后确保释放IO引脚 } /* 由DS1302通信总线上读取一个字节 */ uint8_t DS1302byteRead() { uint8_t mask, dat = 0; for (mask = 0x01; mask != 0; mask <<= 1) { //低位在前,逐位读取 if (DS1302_IO != 0) { //首先读取此时的IO引脚,并设置dat中的对应位 dat |= mask; } DS1302_SCLK = 1; //然后拉高时钟 DS1302_SCLK = 0; //再拉低时钟,完成一个位的操作 } return dat; //最后返回读到的字节数据 } /* 用单次写操作向某一寄存器写入一个字节,reg-寄存器地址,dat-待写入字节 */ void DS1302singleWrite(uint8_t reg, uint8_t dat) { DS1302_CE = 1; //使能片选信号 DS1302byteWrite((reg << 1) | 0x80); //发送写寄存器指令 DS1302byteWrite(dat); //写入字节数据 DS1302_CE = 0; //除能片选信号 } /* 用单次读操作从某一寄存器读取一个字节,reg-寄存器地址,返回值-读到的字节 */ uint8_t DS1302singleRead(uint8_t reg) { uint8_t dat; DS1302_CE = 1; //使能片选信号 DS1302byteWrite((reg << 1) | 0x81); //发送读寄存器指令 dat = DS1302byteRead(); //读取字节数据 DS1302_CE = 0; //除能片选信号 return dat; } /* 用突发模式连续写入8个寄存器数据,dat-待写入数据指针 */ void DS1302burstWrite(uint8_t * dat) { uint8_t i; DS1302_CE = 1; DS1302byteWrite(0xBE); //发送突发写寄存器指令 for (i = 0; i < 8; i++) { //连续写入8字节数据 DS1302byteWrite(dat[i]); } DS1302_CE = 0; } /* 用突发模式连续读取8个寄存器的数据,dat-读取数据的接收指针 */ void DS1302burstRead(uint8_t * dat) { uint8_t i; DS1302_CE = 1; DS1302byteWrite(0xBF); //发送突发读寄存器指令 for (i = 0; i < 8; i++) { //连续读取8个字节 dat[i] = DS1302byteRead(); } DS1302_CE = 0; } /* 获取实时时间,即读取DS1302当前时间并转换为时间结构体格式 */ void getRealTime(sTime_t * time) { uint8_t buf[8]; DS1302burstRead(buf); time->year = buf[6] + 0x2000; time->mon = buf[4]; time->day = buf[3]; time->hour = buf[2]; time->minute = buf[1]; time->sec = buf[0]; time->week = buf[5]; } /* 设定实时时间,时间结构体格式的设定时间转换为数组并写入DS1302 */ void setRealTime(sTime_t * time) { uint8_t buf[8]; buf[7] = 0; buf[6] = time->year; buf[5] = time->week; buf[4] = time->mon; buf[3] = time->day; buf[2] = time->hour; buf[1] = time->minute; buf[0] = time->sec; DS1302burstWrite(buf); } /* DS1302初始化,如发生掉电则重新设置初始时间 */ void DS1302init() { uint8_t dat; sTime_t code InitTime[] = { //2015年4月5日 12:30:00 星期日 //2013年10月8日 12:30:00 星期二 0x2015, 0x04, 0x05, 0x12, 0x30, 0x00, 0x07 //0x2013, 0x10, 0x08, 0x12, 0x30, 0x00, 0x02 }; DS1302_CE = 0; //初始化DS1302通信引脚 DS1302_SCLK = 0; dat = DS1302singleRead(0); //读取秒寄存器 if ((dat & 0x80) != 0) { //由秒寄存器最高位CH的值判断DS1302是否已停止 DS1302singleWrite(7, 0x00); //撤销写保护以允许写入数据 setRealTime(&InitTime); //设置DS1302为默认的初始时间 } }
DS18B20.h
#ifndef _DS18B20_H #define _DS18B20_H sbit DS18B20_IO = P1^4; //DS18B20通信引脚 //#ifndef _1T //void delayX10us(uint8_t t); //#else void delayX10us(uint16_t t); //#endif // _1T bit getDS18B20ack(); void writeDS18B20(uint8_t dat); uint8_t readDS18B20(); bit startDS18B20(); bit getDS18B20temperature(int16_t * temp); uint8_t getIntPart(int16_t temp); uint8_t getDecPart(int16_t temp); bit getSign(int16_t temp); #endif // _DS18B20_H
DS18B20.c
/** * 文件名:DS18B20.c * 描 述:温度传感器DS18B20驱动模块 */ #include <stc15f2k60s2.h> #include <intrins.h> #include "stdint.h" #include "DS18B20.h" /* 软件延时函数,延时时间(t*10)us */ //#ifndef _1T /* void delayX10us(uint8_t t) { //时钟晶振11.0592MHz do { _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); } while (--t); } */ //#else void delayX10us(uint16_t t) { //注意!uint8_t会溢出! t *= 6; do { _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); } while (--t); } //#endif // 1T /* 复位总线,获取存在脉冲,以启动一次读写操作 */ bit getDS18B20ack() { bit ack; EA = 0; //禁止总中断 DS18B20_IO = 0; //产生500us复位脉冲 delayX10us(50); DS18B20_IO = 1; delayX10us(6); //延时60us ack = DS18B20_IO; //读取存在脉冲 while (!DS18B20_IO); //等待存在脉冲结束 EA = 1; //重新使能总中断 return ack; } /* 向DS18B20写入一个字节,dat-待写入字节 */ void writeDS18B20(uint8_t dat) { uint8_t mask; EA = 0; //禁止总中断 //低位在先,依次移出8个bit for (mask = 0x01; mask != 0; mask <<= 1) { DS18B20_IO = 0; //产生2us低电平脉冲 _nop_(); _nop_(); if ((mask & dat) == 0) //输出该bit值 DS18B20_IO = 0; else DS18B20_IO = 1; delayX10us(6); //延时60us DS18B20_IO = 1; //拉高通信引脚 } EA = 1; //重新使能总中断 } /* 从DS18B20读取一个字节,返回值-读到的字节 */ uint8_t readDS18B20() { uint8_t dat, mask; EA = 0; //禁止总中断 //低位在先,依次采集8个bit for (mask = 0x01; mask != 0; mask <<= 1) { DS18B20_IO = 0; //产生2us低电平脉冲 _nop_(); _nop_(); DS18B20_IO = 1; //结束低电平脉冲,等待18B20输出数据 _nop_(); //延时2us _nop_(); if (DS18B20_IO) //读取通信引脚上的值 dat |= mask; else dat &= ~mask; delayX10us(6); //再延时60us } EA = 1; //重新使能总中断 return dat; } /* 启动一次18B20温度转换,返回值-表示是否启动成功 */ bit startDS18B20() { bit ack; ack = getDS18B20ack(); //执行总线复位,并获取18B20应答 //如18B20正确应答,则启动一次转换 if (ack == 0) { writeDS18B20(0xCC); //跳过ROM操作 writeDS18B20(0x44); //启动一次温度转换 } return ~ack; //ack==0表示操作成功,所以返回值对其取反 } /* 读取DS18B20转换的温度值,返回值-表示是否读取成功 */ bit getDS18B20temperature(int16_t * temp) { //温度有正负,有符号 bit ack; uint8_t lowByte, highByte; //16bit温度值的低字节和高字节 ack = getDS18B20ack(); //执行总线复位,并获取18B20应答 //如18B20正确应答,则读取温度值 if (ack == 0) { writeDS18B20(0xCC); //跳过ROM操作 writeDS18B20(0xBE); //发送读命令 lowByte = readDS18B20(); //读温度值的低字节 highByte = readDS18B20(); //读温度值的高字节 *temp = ((int16_t)highByte << 8) | lowByte; //合成为16bit有符号整型数 } return ~ack; //ack==0表示操作应答,所以返回值为其取反值 } uint8_t getIntPart(int16_t temp) { //温度是16位有符号数 if (temp & 0x8000) //温度是负数 temp = -temp; //取绝对值 temp >>= 4; return (uint8_t)temp; } uint8_t getDecPart(int16_t temp) { //精确到十进制的一位小数(截尾处理) uint8_t lowByte; if (temp & 0x8000) //温度是负数 temp = -temp; //取绝对值 lowByte = temp; //取低字节 lowByte &= 0x0F; //高4位清0 return (lowByte * 10) >> 4; //乘以10,除以16 } bit getSign(int16_t temp) { //温度是16位有符号数 return temp & 0x8000; }
UART.h
/** * 文件名:UART.h * 描 述:UART驱动模块 */ #ifndef _UART_H_ #define _UART_H_ void configUART(uint16_t baud); extern void UARTreceive(); extern void UARTtransmit(); #endif // _UART_H_
UART.c
/** * 文件名:UART.c * 描 述:UART驱动模块 */ #include <stc15f2k60s2.h> #include "stdint.h" #include "UART.h" /* 串口配置函数,baud-通信波特率 */ void configUART(uint16_t baud) { PCON &= 0x7F; //波特率不倍速 SCON = 0x50; //配置串口为模式1,并使能串行接收(SM0=0,SM1=1,REN=1) AUXR &= 0xBF; //定时器1时钟为Fosc/12,即12T AUXR &= 0xFE; //串口1选择定时器1为波特率发生器 TMOD &= 0x0F; //清零T1的控制位 TMOD |= 0x20; //配置T1为模式2 TL1 = 256 - 11059200 / 12 / 32 / baud; //计算T1初值 TH1 = TL1; //重载值等于初值 EA = 1; //使能总中断 ET1 = 0; //禁止T1中断 ES = 1; //使能串口中断 TR1 = 1; //启动T1 } /* UART中断服务函数 */ void UART_ISR() interrupt 4 { if (RI) { //接收到字节 RI = 0; //手动清零接收中断标志位 UARTreceive(); //从SBUF中取出数据 } if (TI) { //字节发送完毕 TI = 0; //手动清零发送中断标志位 UARTtransmit(); //将数据赋值给SBUF } }
#ifndef _DIGITAL_TUBE_H #define _DIGITAL_TUBE_H extern uint8_t code tab[]; extern uint8_t dspBuf[8]; void digitalTubeScan(); #endif
digitalTube.c
#include <stc15f2k60s2.h> #include "stdint.h" #include "digitalTube.h" uint8_t code tab[] = { // 0 1 2 3 4 5 6 7 8 9 null 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xFF }; uint8_t dspBuf[8] = { 10, 10, 10, 10, 10, 10, 10, 10 }; //显示缓冲区 void digitalTubeScan() { static uint8_t index = 0; P2 = (P2 & 0x1F) | 0xE0; //使能具体值 P0 = 0xFF; //消隐 P2 &= 0x1F; //锁存 P2 = (P2 & 0x1F) | 0xC0; //使能位选 P0 = (1 << index); //数码管选择是1有效 P2 &= 0x1F; P2 = (P2 & 0x1F) | 0xE0; //使能具体值 P0 = tab[dspBuf[index]]; // P2 &= 0x1F; index = (index + 1) & 0x07; }
timer.h
#ifndef _TIMER_H #define _TIMER_H #define SYS_MCLK 11059200 extern uint8_t tmr0LowByte, tmr0HighByte; //extern uint8_t tmr1LowByte, tmr1HighByte; void configTmr0(uint8_t ms); //void configTmr1(uint8_t ms); #endif
timer.c
#include <stc15f2k60s2.h> #include "stdint.h" #include "timer.h" uint8_t tmr0LowByte, tmr0HighByte; //uint8_t tmr1LowByte, tmr1HighByte; void configTmr0(uint8_t ms) { //!!8bits uint32_t tmp; //小心溢出 tmp = ms * SYS_MCLK / 12 / 1000; tmp = 65536 - tmp; tmr0LowByte = (uint8_t)tmp; tmr0HighByte = (uint8_t)(tmp >> 8); AUXR &= 0x7F; //定时器时钟12T模式 TMOD &= 0xF0; TMOD |= 0x01; TL0 = tmr0LowByte; TH0 = tmr0HighByte; EA = 1; ET0 = 1; TR0 = 1; } /* void configTmr1(uint8_t ms) { //!!8bits uint32_t tmp; tmp = ms * SYS_MCLK / 12 / 1000; tmp = 65536 - tmp; tmr1LowByte = (uint8_t)tmp; tmr1HighByte = (uint8_t)(tmp >> 8); AUXR &= 0xBF; TMOD &= 0x0F; TMOD |= 0x10; TL1 = tmr1LowByte; TH1 = tmr1HighByte; EA = 1; ET1 = 1; TR1 = 1; } */
stdint.h
#ifndef STDINT_H_INCLUDED #define STDINT_H_INCLUDED typedef unsigned char uint8_t; typedef unsigned int uint16_t; typedef unsigned long uint32_t; typedef signed char int8_t; typedef signed int int16_t; typedef signed long int32_t; #endif // STDINT_H_INCLUDED