|
【單片機實際應用】開機時在MySQL數據庫中自動記錄開機時間和室內溫度(由DS18B20通過串口提供) |
一派護法 十九級 |
最終效果:
|
一派護法 十九級 |
数据表結構圖:
|
一派護法 十九級 |
數據表結構: CREATE TABLE IF NOT EXISTS `PowerLog` ( `LogID` int(11) NOT NULL, `LogTime` datetime NOT NULL, `LogFlag` varchar(20) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'TEST', `LogTimeZone` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'UTC+8', `ComputerIP` varchar(20) COLLATE utf8_unicode_ci NOT NULL DEFAULT '192.168.0.4', `Temperature` varchar(100) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'NO DATA' ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
一派護法 十九級 |
我使用的操作系統:Fedora Linux 22 單片機型號:AVR ATMega16
|
一派護法 十九級 |
單片機所用晶振:7.3728MHz 串口線:USB轉串口線(設備地址:/dev/ttyUSB0)
|
一派護法 十九級 |
下面公佈程序源代碼。 【單片機部分】 [文件列表] delay.c delay.h DS18B20.c DS18B20.h IR.c IR.h pce.c UART.c UART.h
|
一派護法 十九級 |
[delay.c] #include "delay.h"
//延时n毫秒 void delay(unsigned int n) { unsigned int i; while (n--) for (i=0;i<1140;i++); //1ms基准延时 }
void delay_us(unsigned int n) { if (n==0) return; while (--n); }
//延时0.5ms void delay500us(void) { unsigned int i; for (i=0;i<570;i++); //1140*0.5=570 }
//延迟0.56ms void delay560us(void) { unsigned int i; for (i=0;i<638;i++); //1140*0.56=638.4 }
|
一派護法 十九級 |
[delay.h] void delay(unsigned int n); void delay_us(unsigned int n); void delay500us(void); void delay560us(void);
|
一派護法 十九級 |
[DS18B20.c] #include <iom16v.h> #include <macros.h> #include "delay.h" #include "DS18B20.h"
unsigned char DSFlags=0xff; unsigned char DSTN,DSTD;
void DS18B20_Init(void) { DQ_OUT; DQ_1; delay_us(6); DQ_0; delay_us(600); DQ_1; delay_us(120); if (DQ) DSFlags|=BIT(DSERR); else DSFlags&=~BIT(DSERR); delay_us(620); }
void DS18B20_Read(unsigned char* dat) { unsigned char i; for (i=0;i<8;i++) { DQ_OUT; DQ_1; delay_us(2); DQ_0; delay_us(4); DQ_1; delay_us(6); *dat>>=1;
if (DQ) *dat|=0x80; else *dat&=0x7f; delay_us(60); } }
void DS18B20_Write(unsigned char dat) { unsigned char i; for (i=0;i<8;i++) { DQ_OUT; DQ_1; delay_us(2); DQ_0; if (dat&0x01) DQ_IN; else DQ_OUT; delay_us(30); DQ_1; delay_us(3); dat>>=1; } delay_us(10); }
void DS18B20_ReadyReadTemp(void) { DS18B20_Init(); DS18B20_Write(0xcc); DS18B20_Write(0x44); delay_us(20);
DS18B20_Init(); DS18B20_Write(0xcc); DS18B20_Write(0xbe); }
void DS18B20_Measure(void) { unsigned char tl,th,tltemp; DS18B20_ReadyReadTemp(); DS18B20_Read(&tl); DS18B20_Read(&th);
if ((th&0xf8)!=0x00) { DSFlags|=BIT(DSNEG); tl=~tl; th=~th; tltemp=tl+1; tl=tltemp; if (tltemp>=255) th++; } else DSFlags&=~BIT(DSNEG);
DSTN=th*16+tl/16; DSTD=(tl&0x0f)*625/100; //*6.25 }
|
一派護法 十九級 |
[DS18B20.h] #define DSNEG 4 #define DSERR 6
#define DQ (PINB&BIT(1)) #define DQ_0 PORTB&=~BIT(1) #define DQ_1 PORTB|=BIT(1) #define DQ_IN DDRB&=~BIT(1) //write #define DQ_OUT DDRB|=BIT(1) //read
extern unsigned char DSFlags; extern unsigned char DSTN; extern unsigned char DSTD;
void DS18B20_Init(void); void DS18B20_Read(unsigned char* dat); void DS18B20_Write(unsigned char dat); void DS18B20_ReadyReadTemp(void); void DS18B20_Measure(void);
|
一派護法 十九級 |
[IR.c] #include <iom16v.h> #include <macros.h> #include "delay.h" #include "UART.h" #include "IR.h"
unsigned int hightime,lowtime; unsigned char tlow; unsigned char IRCode[4];
unsigned char IRDataBuffer[8]; unsigned int IRDataLength=0; unsigned int IRStrID=0;
void IR_Init(void) { SEI(); IR_INT_ON; IR_OFF; MCUCR=0x02; //外中断0下降沿触发
TCCR1A=0x00; TCCR1B=0x02; //定时器1设为8分频,也就相当于51单片机接12M晶振 }
//键码引导码 void IR_KeyCodeBegin(void) { IR_ON; delay(9); //9ms高电平 IR_OFF; delay(4); //4.5ms低电平 delay500us(); }
//数据引导码 void IR_DataBegin(void) { IR_ON; delay(3); IR_OFF; delay(1); }
//发送0 void IR_Send0(void) { IR_ON; delay560us(); IR_OFF; delay560us(); }
//发送1 void IR_Send1(void) { IR_ON; delay560us(); IR_OFF; delay560us(); delay560us(); delay560us(); }
//发送尾码,使接收端能确定最后一位是0还是1 void IR_Stop(void) { IR_ON; delay560us(); IR_OFF; }
//发送一个字节 void IR_SendByte(unsigned char Data) { unsigned char i; for (i=0;i<8;i++) { if (Data&BIT(i)) IR_Send1(); else IR_Send0(); } }
//发送一个字符串 void IR_SendString(char* pStr) { unsigned int i; unsigned int len=0;
char* tp=pStr; while (*tp!='\0') { len++; tp++; }
IR_DataBegin(); IR_SendByte(len%256); IR_SendByte(len/256); for (i=0;*pStr!='\0';i++) { if (i>5 && i%8==0) { IR_Stop(); delay(108); IR_DataBegin(); } IR_SendByte(*pStr); pStr++; } IR_Stop(); }
//★发送一个完整的按键码 //usercode为用户码,16位 //keycode为键码,8位 void IR_SendKeyCode(unsigned int usercode, unsigned char keycode) { IR_KeyCodeBegin(); IR_SendByte(usercode/256); //用户码高8位 IR_SendByte(usercode%256); //用户码低8位 IR_SendByte(keycode); //键码 IR_SendByte(~keycode); //键码的反码 IR_Stop(); //使接收端能区分最后一位是1还是0 }
void IR_PressKey(unsigned char keycode) { IR_SendKeyCode(IR_USERCODE,keycode); }
//红外接收中断 void IR_Receive(void) { unsigned char tmp; unsigned char flag=0x00; IR_INT_OFF; IR_ReceiveBegin(); if (lowtime>2650 && lowtime<3350 && hightime>650 && hightime<1350) { //红外数据接收 if (IRStrID==0) { IR_ReceiveByte(&tmp); IRDataLength=tmp; IR_ReceiveByte(&tmp); IRDataLength+=tmp*256; flag|=BIT(7); } for (tmp=0;tmp<8;tmp++) { IR_ReceiveByte(&IRDataBuffer[tmp]); IRStrID++; if (IRStrID>=IRDataLength) { IRStrID=0; flag|=BIT(6); break; } }
//在108ms的间隔时间中趁机把数据发给电脑 if (flag&BIT(7)) { //起始码 UART_Send(0x40); UART_Send(0xf5); UART_Send(IRDataLength/256); UART_Send(IRDataLength%256); } for (tmp=0;tmp<8;tmp++) UART_Send(IRDataBuffer[tmp]); if (flag&BIT(6)) { //终止码 UART_Send('E'); UART_Send(0x00); } } else if (lowtime>9200 && lowtime<9500 && hightime>4600 && hightime<4900) { //红外遥控解码 IR_ReceiveByte(&IRCode[0]); IR_ReceiveByte(&IRCode[1]); IR_ReceiveByte(&IRCode[2]); IR_ReceiveByte(&IRCode[3]); UART_Send(0x40); UART_Send(0xf1); //finish UART_Send(IRCode[0]); UART_Send(IRCode[1]); UART_Send(IRCode[2]); UART_Send(IRCode[3]); } else { //UART_Send(0x40); //UART_Send(0x4f); } IR_INT_ON; }
void IR_ReceiveBegin(void) { TCNT1H=0x00; TCNT1L=0x00; while (!IR); //给低电平计时 tlow=TCNT1L; lowtime=TCNT1H; lowtime=lowtime*256+tlow; IR_CLEAR_TIMER; //定时器1清除标志位,注意是写1清零
TCNT1H=0x00; TCNT1L=0x00; while (IR && !IR_TIMER_OUT); //给高电平计时 tlow=TCNT1L; hightime=TCNT1H*256+tlow; IR_CLEAR_TIMER; }
void IR_ReceiveByte(unsigned char* Data) { unsigned char i; for (i=0;i<8;i++) { TCNT1H=0x00; TCNT1L=0x00; while (!IR); //给低电平计时 tlow=TCNT1L; lowtime=TCNT1H; lowtime=lowtime*256+tlow; IR_CLEAR_TIMER;
TCNT1H=0x00; TCNT1L=0x00; while (IR && !IR_TIMER_OUT); //给高电平计时 tlow=TCNT1L; hightime=TCNT1H*256+tlow; IR_CLEAR_TIMER;
if (hightime>1150) *Data|=BIT(i); else *Data&=~BIT(i); } }
|
一派護法 十九級 |
[IR.h] #define IR (PIND&BIT(2)) //receiver #define IR_ON PORTB|=BIT(0) //sender #define IR_OFF PORTB&=~BIT(0) #define IR_USERCODE 0x18e0 #define IR_INT_ON GICR|=BIT(INT0) #define IR_INT_OFF GICR&=~BIT(INT0) #define IR_TIMER_OUT (TIFR&BIT(TOV1)) #define IR_CLEAR_TIMER TIFR|=BIT(TOV1)
void IR_Init(void); void IR_KeyCodeBegin(void); void IR_DataBegin(void); void IR_Send0(void); void IR_Send1(void); void IR_Stop(void);
void IR_SendByte(unsigned char Data); void IR_SendString(char* pStr); void IR_SendKeyCode(unsigned int usercode, unsigned char keycode); void IR_PressKey(unsigned char keycode);
#pragma interrupt_handler IR_Receive:iv_INT0; void IR_Receive(void); void IR_ReceiveBegin(void); void IR_ReceiveByte(unsigned char* Data);
|
一派護法 十九級 |
[pce.c] #include <iom16v.h> #include <macros.h> #include "delay.h" #include "IR.h" #include "UART.h" #include "DS18B20.h"
void UART_Execute(void) { switch (UART_Buffer[0]) { case 0x80: //发送测试信息 UART_SendString("****************\n"); UART_SendString("It works. -- PCE\n"); UART_SendString(" -- By Octpus\n"); UART_SendString("**********************\n"); UART_ClearBuffer(); break; case 0x81: switch (UART_Buffer[1]) { case 0xf0: //反码查询 if (UART_Pos>=3) { UART_Send(UART_Buffer[0]); UART_Send(UART_Buffer[1]); UART_Send(~UART_Buffer[1]); UART_ClearBuffer(); } break; default: if (UART_Pos>=2) UART_ThrowError(); break; } break; case 0x82: //测试红外遥控(适用于12864液晶显示) if (UART_Pos>=2) { if (UART_Buffer[1]==0x40) IR_SendString("****************It works. -- PCE -- By Octpus&&&&简体中文^^$$$"); else if (UART_Buffer[1]==0x20) IR_SendString("Yes"); else IR_PressKey(0x00); UART_Send(0xa5); UART_ClearBuffer(); } break; case 0x83: //读取温度 DS18B20_Measure(); UART_Send(DSFlags); UART_Send(DSTN); UART_Send(DSTD); UART_ClearBuffer(); break;
case 0x00: //无命令 break; case '*': case '#': default: UART_ThrowError(); break; } }
void main(void) { DDRB=0xff; PORTB=0xff; DDRD=0xf3; //两个外中断口设为输入 PORTD=0xff;
IR_Init(); UART_Init(); //UART_SendString("#### POWER ON ####\n");
UART_Send(0x60); UART_Send(0x85); DS18B20_Init(); UART_Send(DSFlags);
while (1) { UART_Execute(); } }
|
一派護法 十九級 |
回復:13樓 那個Octopus都拼錯了。算了,反正沒什麼影響。
|
一派護法 十九級 |
[UART.c] #include <iom16v.h> #include <macros.h> #include "UART.h"
unsigned char UART_Buffer[256]={0x00}; unsigned char UART_Pos=0;
void UART_Init(void) { UCSRB=0x00; //禁止发送和接收 UCSRA=0x02; //倍速异步模式USX=1 UCSRC=0x06;
UBRRL=(FOSC/8/(BAUD+1))%256; //设置波特率 UBRRH=(FOSC/8/(BAUD+1))/256;
UCSRB|=BIT(RXEN); //允许接收 UCSRB|=BIT(TXEN); //允许发送 UCSRB|=BIT(RXCIE); //允许接收中断 }
void UART_Send(unsigned char Data) { while (!(UCSRA&BIT(UDRE))); //如果数据寄存器不为空(0)就等待 UDR=Data; while (!(UCSRA&BIT(TXC))); //等待数据发送完毕 UCSRA|=BIT(TXC); //清除发送标志 }
void UART_SendString(char* pStr) { while (*pStr!='\0') { UART_Send(*pStr); pStr++; } }
void UART_ClearBuffer(void) { UART_Pos=0; UART_Buffer[0]=0x00; }
void UART_ThrowError(void) { UART_Send('?'); UART_Send(UART_Buffer[0]); UART_ClearBuffer(); }
void UART_Receive(void) { while (!(UCSRA&BIT(RXC))); UART_Buffer[UART_Pos]=UDR; UART_Pos++; UART_Send(UDR); }
|
一派護法 十九級 |
[UART.h] #define FOSC 7372800 #define BAUD 9600
extern unsigned char UART_Buffer[256]; extern unsigned char UART_Pos;
void UART_Init(void); void UART_Send(unsigned char Data); void UART_SendString(char* pStr); void UART_ClearBuffer(void); void UART_ThrowError(void);
#pragma interrupt_handler UART_Receive:iv_USART_RX void UART_Receive(void);
|
一派護法 十九級 |
單片機端程序完畢 程序編譯用的軟件是ACC AVR(我在Linux系統下開了個XP虛擬機運行的)
|
一派護法 十九級 |
|
一派護法 十九級 |
【計算機部分】 [文件列表] autorun.sh (設為開機自動運行) conn.h logview.c Makefile powerlog2.c temperature.c temperature.h UART.c UART.h
|
一派護法 十九級 |
[autorun.sh] cd /home/octopus/Programs/powerlog ./powerlog2 on
注意:第一行要指定程序文件所在目錄!
|
一派護法 十九級 |
[conn.h] #define SERVER_NAME "localhost" #define DB_USER "root" #define DB_PASSWD "YOUR_PASSWORD" #define DB_NAME "server"
|
一派護法 十九級 |
[logview.c] #include <stdio.h> #include <stdlib.h> #include <string.h> #include <mysql.h> #include "conn.h"
int main(int argc, char* argv[]) { MYSQL conn; mysql_init(&conn); if (!mysql_real_connect(&conn, SERVER_NAME, DB_USER, DB_PASSWD, DB_NAME, 0, NULL, 0)) { printf("Cannot connect to the database server.\n"); return 1; } mysql_query(&conn, "SET NAMES utf8"); printf("Powerlog Version 2.2\n"); FILE* file = fopen("powerlog.txt", "w"); char title[] = "ID\tTIME\n"; printf(title); fwrite(title, strlen(title), 1, file); char sql[] = "SELECT LogID, LogTime FROM PowerLog WHERE LogFlag = 'POWER ON' AND ComputerIP = '192.168.0.4' ORDER BY LogTime DESC"; mysql_query(&conn, sql); MYSQL_RES* rs = mysql_store_result(&conn); MYSQL_ROW row; char line[100]; unsigned int i; for (i = 0; row = mysql_fetch_row(rs); i++) { int id = atoi(row[0]); char timestr[50]; sprintf(timestr, "%s", row[1]); sprintf(line, "%d\t%s\n", id, timestr); if (i < 20) printf(line); fwrite(line, strlen(line), 1, file); } fclose(file); mysql_free_result(rs); mysql_close(&conn); return 0; }
|
一派護法 十九級 |
[Makefile] MYSQL=-I/usr/include/mysql -L/usr/lib64/mysql -lmysqlclient
powerlog2: powerlog2.o logview.o gcc powerlog2.o temperature.o UART.o -o powerlog2 $(MYSQL) gcc logview.o -o logview $(MYSQL)
powerlog2.o: powerlog2.c conn.h temperature.o UART.o gcc -c powerlog2.c $(MYSQL) temperature.o: temperature.c temperature.h gcc -c temperature.c
UART.o: UART.c UART.h gcc -c UART.c
logview.o: logview.c conn.h gcc -c logview.c $(MYSQL) clean: rm powerlog2 *.o
|
一派護法 十九級 |
[powerlog2.c] //Version: 2.2 #include <stdio.h> #include <string.h> #include <mysql.h> #include <unistd.h> #include "conn.h" #include "temperature.h" #include "UART.h"
int main(int argc, char* argv[]) { char uartopened = 1; if (UART_Open() == 0) uartopened = 0; MYSQL conn; mysql_init(&conn); if (!mysql_real_connect(&conn, SERVER_NAME, DB_USER, DB_PASSWD, DB_NAME, 0, NULL, 0)) { printf("Cannot connect to the database server.\n"); return 1; } mysql_query(&conn, "SET NAMES utf8"); printf("Powerlog Version 2.2\n"); printf("Welcome\n"); char sql[200]; memset(sql, 0, sizeof(char) * 200); char sql2[100]; // the Backup Query, which is executed when the main sql is corrupted memset(sql2, 0, sizeof(char) * 100); char temp[8]; int i = 10; float f; if (argc >= 2 && strcmp(argv[1], "on") == 0) { strcat(sql, "INSERT INTO PowerLog (LogTime, LogFlag, Temperature) VALUES (NOW(), 'POWER ON', TRIM('"); strcat(sql2, "INSERT INTO PowerLog (LogTime, LogFlag, Temperature) VALUES (NOW(), 'POWER ON', 'Query Error')"); } else { // only for development strcat(sql, "INSERT INTO PowerLog (LogTime, Temperature) VALUES (NOW(), TRIM('"); strcat(sql2, "INSERT INTO PowerLog (LogTime, Temperature) VALUES (NOW(), 'Query Error')"); } if (uartopened == 1) { get_temperature(&f); // avoid the first possible error value while (i--) { usleep(10000); get_temperature(&f); tempstr(&f, temp); strcat(sql, temp); } } else strcat(sql, "UART Unavailable"); strcat(sql, "'))"); int result = mysql_query(&conn, sql); if (result == 1) mysql_query(&conn, sql2); //Display the time mysql_query(&conn, "SELECT now()"); MYSQL_RES* rs = mysql_store_result(&conn); MYSQL_ROW row = mysql_fetch_row(rs); char buffer[100]; sprintf(buffer, "%s", row[0]); printf("MySQL Time: %s\n", buffer); mysql_free_result(rs); mysql_close(&conn); if (uartopened == 1) USART_Close(); return 0; }
|
一派護法 十九級 |
[temperature.c] #include <stdio.h> #include <unistd.h> #include "UART.h"
#define DSNEG 4 #define DSERR 6
#define BIT(n) 1<<n
char buffer[513];
void get_temperature(float* temp) { UART_Send(0x83); usleep(100000); UART_Receive(buffer); if (buffer[1] & BIT(DSERR)) *temp = -999.99; else { *temp = buffer[2] * 1.00 + buffer[3] * 0.01; if (buffer[1] & BIT(DSNEG)) *temp = -*temp; } }
void tempstr(float* temp, char* str) { sprintf(str, "%.2f℃\t", *temp); }
|
一派護法 十九級 |
[temperature.h] void get_temperature(float* temp); void tempstr(float* temp, char* str);
|
一派護法 十九級 |
[UART.c] #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> // UNIX Standard Function Definitions #include <fcntl.h> // File Control Definitions #include <errno.h> // File Control Definitions #include <termios.h> // POSIX Terminal Control Definitions #include "UART.h"
struct termios tty; struct termios tty_old; int UART_id = 0;
int UART_Open(void) { memset(&tty, 0, sizeof(tty)); UART_id = open(UART_PORT, O_RDWR | O_NOCTTY | O_NDELAY); if (UART_id < 0) { printf("Warning: Cannot open the serial port!\n"); return 0; } /* Error Handling */ if (tcgetattr(UART_id, &tty) != 0) { printf("Error: %d from tcgetattr: %s \n", errno,strerror(errno)); return 0; } /* Save old tty parameters */ tty_old = tty; /* Set Baud Rate */ cfsetospeed(&tty, (speed_t)B9600); cfsetispeed(&tty, (speed_t)B9600); /* Setting other Port Stuff */ tty.c_cflag &= ~PARENB; // Make 8n1 tty.c_cflag &= ~CSTOPB; tty.c_cflag &= ~CSIZE; tty.c_cflag |= CS8; tty.c_cflag &= ~CRTSCTS; // no flow control tty.c_cc[VMIN] = 1; // read doesn't block tty.c_cc[VTIME] = 5; // 0.5 second read timeout tty.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines /* Make raw */ cfmakeraw(&tty); /* Flush Port, then applies attributes */ tcflush(UART_id, TCIFLUSH); if (tcsetattr(UART_id, TCSANOW, &tty) != 0) { printf("Error: %d from tcgetattr \n", errno); return 0; } return UART_id; }
void UART_Send(unsigned char Byte) { write(UART_id, &Byte, 1); // It was definitely not necessary to write byte per byte, also int n_written = write( UART_id, cmd, sizeof(cmd) -1) worked fine. }
int UART_Receive(char* buf) { int len = read(UART_id, buf, 512); buf[len] = '\0'; return len; }
void USART_Close() { close(UART_id); }
|
一派護法 十九級 |
[UART.h] #define UART_PORT "/dev/ttyUSB0"
int UART_Open(void); void UART_Send(unsigned char Byte); int UART_Receive(char* buf); void USART_Close();
|
一派護法 十九級 |
單片機端程序完畢
|
一派護法 十九級 |
補充:PHP讀取DS18B20溫度值的示例程序 <?php define("DSNEG", 4); define("DSERR", 6); function BIT($n) { return 1 << $n; } function decodeTemperature($flags, $TN, $TD) { if ($flags & BIT(DSERR)) { $value = "Error"; } else { $value = $TN * 1.00 + $TD * 0.01; if ($flags & BIT(DSNEG)) { $value = -$value; } } return $value; }
$filename = "/dev/ttyUSB0"; $fp = fopen($filename, "a+"); fwrite($fp, "\x83"); $flag = fread($fp, 1); $data = fread($fp, 3); printf("<b>Flag:</b> 0x%x (Useless)<br>\n", $flag); printf("<b>Data:</b> 0x%x, 0x%x, 0x%x<br>\n", ord($data{0}), ord($data{1}), ord($data{2})); $temperature = decodeTemperature(ord($data{0}), ord($data{1}), ord($data{2})); echo "<b>Temperature:</b> $temperature°C"; fclose($fp); ?>
輸出: Flag: 0x0 (Useless)
Data: 0xaf, 0x1b, 0x25
Temperature: 27.37°C
|