Experiment 5 Temperature collection and keyboard scanning

1. Experimental purpose

*Master the method of controlling interrupts and timers in C51

*Master the principles and methods of using CH451 for keyboard input

*Master the interface and programming of DS18B20 temperature sensor

2. Experimental equipment

*MCU experiment box

*CH451 display module

*Temperature acquisition module (at the lower left of the SWITCH module area of the experimental box)

*Keil development system

3. Experimental Principle

CH451 keyboard input principle and working method

Principle of DS18B20 temperature sensor

Principles and methods of single-line communication

4. Experimental content

Experimental content

Use C51 to write a program to implement a countdown table with temperature function

*The collected temperature is displayed on the right side of the digital tube, using decimal display, 1 digit after the decimal point, and refreshed regularly.

* Implement a countdown watch on the left side of the digital tube to display minutes (one digit is sufficient) and seconds for countdown.

*Use the keyboard to enter the initial value of the countdown table and control its operation

Experiment specific requirements

1. The keyboard control of the countdown watch includes the following functions:

* There are 10 numeric keys defined on the keyboard, and you can use the numeric keys to input a 3-digit initial value

* Customize several function keys to achieve: output initial value, start counting, pause, terminate counting and other functions

*The countdown watch must be able to be used continuously, not one-time use

* Robustness must be ensured, that is, the program must execute normally in response to incorrect key presses and other situations.

2. The temperature value should display a decimal point

5. Experimental steps

1. Design circuit connection scheme and connect equipment

2. It is recommended to follow the following steps for programming and debugging.

(1) Write a collection program to collect temperature and display it;

(2) Use the internal timer interrupt to write a countdown table program to display the countdown time;

(3) Write a keyboard input program to read the key codes and convert them;

(4) Write control logic to control the running status of the entire system so that all the above parts work together.

Reference circuit connection diagram

The DIN of the CH451 module is connected to SDA (corresponding to C51 chip pin P1.1), DCLK is connected to SCL (corresponding to C51 chip pin P1.2), LOAD is connected to PWM0 (corresponding to C51 chip pin P1.3), DOUT is connected to INT0, That is interrupt 0. The DQ end of DS18B20 is connected to P0.0, which is IO1.

reference code

//ch451.h
#ifndef __CH451_H__
#define __CH451_H__
#include<reg51.h>
#define CH451_RESET 0x0201 //Reset
#define CH451_LEFTMOV 0x0300 //Set the movement mode - move
#define CH451_LEFTCYC 0x0301 //Set the movement mode-left rotation
#define CH451_RIGHTMOV 0x0302 //Set the movement method-right shift
#define CH451_RIGHTCYC 0x0303 //Set movement mode-right rotation
#define CH451_SYSOFF 0x0400 //Turn off display, keyboard, and watchdog
#define CH451_SYSON1 0x0401 //Open display
#define CH451_SYSON2 0x0403 //Turn on display and keyboard
#define CH451_SYSON3 0x0407 //Enable display, keyboard, and watchdog functions
#define CH451_DSP 0x0500 //Set the default display mode
#define CH451_BCD 0x0580 //Set BCD decoding method
#define CH451_TWINKLE 0x0600 //Set flicker control
#define CH451_DIG0 0x0800 //Nigital tube bit 0 display
#define CH451_DIG1 0x0900 //Nigital tube bit 1 display
#define CH451_DIG2 0x0a00 //Nigital tube bit 2 display
#define CH451_DIG3 0x0b00 //Nigital tube bit 3 display
#define CH451_DIG4 0x0c00 //Nigital tube bit 4 display
#define CH451_DIG5 0x0d00 //Nigital tube bit 5 display
#define CH451_DIG6 0x0e00 //Nigital tube bit 6 display
#define CH451_DIG7 0x0f00 //Nigital tube bit 7 display
 
#define USE_KEY 1//If you use keyboard interrupt, please define
sbit ch451_load = P1^3; //Serial command loading, rising delay activated
sbit ch451_din = P1^1; // Serial data output, connected to the data input of CH451
sbit ch451_dclk = P1^2; //Serial data clock rising delay is activated
sbit ch451_dout = P3^2; //INT0 corresponds to P3.2, keyboard interrupt and key value data input, connected to the data output of CH451
extern unsigned char ch451_key;// Global variable, stores the key value read in the keyboard interrupt
void ch451_init();
void ch451_write(unsigned int command);
unsigned char ch451_read();
void ch451_inter();

#endif 
//ch451.c
#include "ch451.h"

//Initialize subroutine

void ch451_init()
{
  ch451_din=0; //First low and then high, select 4-wire input
  ch451_din=1;
#ifdef USE_KEY
  IT1=0; //Set falling edge trigger
  IE1=0; //clear interrupt flag
  PX1=0; //Set low priority
  EX1=1; //Enable interrupt
#endif

}
//************************************************ *****
//Output command subroutine
//Define an unsigned integer variable to store the 12-byte command word.
 void ch451_write(unsigned int command)
{
  unsigned char i;
#ifdef USE_KEY
  EX1=0; //Disable keyboard interrupts
#endif
  ch451_load=0; //Command starts
  for(i=0;i<12;i + + ){ //Send 12-bit data, low bit first
    ch451_din=command &1;
    ch451_dclk=0;
    command>>=1;
    ch451_dclk=1; //valid on rising edge
  }
  ch451_load=1; //Load data
#ifdef USE_KEY
  EX1=1;
#endif
}

#ifdef USE_KEY

//************************************************ *
//Enter the command subroutine, and the MCU reads one byte from 451
 unsigned char ch451_read()
{
  unsigned char i;
  unsigned char command,keycode; //Define the command word and data memory
  EX1=0; //Middle section
  command=0x07; //Input read 451 command word
  ch451_load=0;
  for(i=0;i<4;i + + ){
 
    ch451_din=command & amp;1; //Send in the lowest bit
    ch451_dclk=0;
    command>>=1; //Move one position to the right
    ch451_dclk=1; //Generate clock rising edge lock to notify CH451 to input bit data
 }
  ch451_load=1; // Generate a loading rising edge to notify CH451 to process the command data
  keycode=0; //Clear keycode
  for(i=0;i<7;i + + ){
    keycode<<=1; //Move data into keycode, high bit first, low bit last
    keycode|=ch451_dout; //Read the data of 451 from high to low
    ch451_dclk=0; //Generate the rising edge of the clock to notify CH451 to output the next bit
    ch451_dclk=1;
 }
  IE1=0; //clear interrupt flag
  EX1=1;
  return(keycode); //Return the key value
}
//************************************************ *
//Interrupt subroutine uses interrupt 0

void ch451_inter() interrupt 0//External interrupt 0 (INT0) corresponds to interrupt 0, external interrupt 1 (INT1) corresponds to interrupt 2
{
  unsigned char i; //Define loop variables
  unsigned char command,keycode; //Define control word register and intermediate variable timer
  command=0x07; //Read the high 4 bits of the key value command 0111B
  ch451_load=0; //Command starts
  for(i=0;i<4;i + + ){
    ch451_din=command & amp;1; //low bit first, high bit last
    ch451_dclk=0;
    command>>=1; //Shift right one bit
    ch451_dclk=1; //Generate clock rising edge lock to notify CH451 to input bit data
 }
  ch451_load=1; // Generate a loading rising edge to notify CH451 to process the command data
  keycode=0; //Clear keycode
  for(i=0;i<7;i + + ){
    keycode<<=1; //The data is shifted by one bit, with the high bit in front and the low bit in the back
    keycode|=ch451_dout; //Read the data of 451 from high to low
    ch451_dclk=0; //Generate the rising edge of the clock to notify CH451 to output the next bit
    ch451_dclk=1;
 }
  ch451_key=keycode; //Save the last key value. The relationship between the read key code and the key value needs to be tested.
  IE0=0; //clear interrupt flag
}

//**********************************************

#endif
//ds18b20.h
#ifndef __DS18B20_H__
#define __DS18B20_H__
#include<reg51.h>
//Define bit variable DQ as a single bus for communication with 18B20
// Currently using P0.0, you can modify it as needed
sbit DQ = P0^0;
#define DQ_H DQ = 1;
#define DQ_L DQ = 0;

//Function prototype declaration
void reset_ds18b20(void);
void write_byte(unsigned char dat);
unsigned char read_byte(void);

#endif
//ds18b20.c
#include "ds18b20.h"
#include <intrins.h>


/*
Execution time: approximately equal to 10*n microseconds, when the main frequency is 12MHz, add a nop
If you consider the execution of other statements, you can subtract another nop
*/
void delay_10us(unsigned char n){
unsigned char i;
for(i=n; i>0; --i){
_nop_();
//_nop_(); //The main frequency is 12MHz
}
}


/****************************************************** *************************************************** *******
Function name: DS18B20 initialization subroutine
Input parameters: None
Output parameters: none
Function returns: None
*************************************************** *************************************************** ******/

void reset_ds18b20(void){
  DQ_H; // DQ is set high first
delay_10us(10); // Delay
  DQ_L; //Send reset pulse
delay_10us(60); // Delay (480us - 960us)
  DQ_H; //Pull the data line high
delay_10us(24); // DS18B20 will generate a response pulse, lasting 60-240us
}


/****************************************************** *************************************************** *******
Function name: Write one byte of data to DS18B20
Input parameters: data
Output parameters: none
Function returns: None
*************************************************** *************************************************** ******/
void write_byte(unsigned char dat){
unsigned char i=0;
for (i = 8; i > 0; i--){
//To avoid adding the judgment time to the timing, put the judgment outside
if(dat & 0x01)
        {
DQ_L;
_nop_(); _nop_(); //Delay 2us
            DQ_H; //Release the bus
delay_10us(6);
        }
        else
        {
DQ_L;
delay_10us(6);
            DQ_H;
_nop_(); _nop_(); //Delay 2us
        }
dat>>=1;
}
delay_10us(3);
}
/****************************************************** *************************************************** *******
Function name: function to read one byte of data from ds18b20
Input parameters: none
Output parameters: none
Function returns: bytes read
*************************************************** *************************************************** ******/

unsigned char read_byte(void)
{
unsigned char i = 0;
unsigned char dat = 0;
bit b;
    //Read the low bit first and then the high bit
    for (i = 8; i > 0; i--)
{
DQ_L;
_nop_(); _nop_(); //Delay 2us
DQ_H; //Release the bus
dat >>= 1; //Data must be read within 15us, use the time occupancy of this expression
b=DQ;

    if (DQ){
            dat|=0x80;
        }

delay_10us(6);
}
    return(dat);
}

//main.c
#include<reg51.h>
#include <intrins.h>
#include "ch451.h"
#include "ds18b20.h"

//Global variables needed for countdown table
unsigned int cnt=0; //Record the number of interruptions
unsigned char second_i=0; //Initialize second digit digital tube
unsigned char second_t=0; //Initialize tens of seconds digital tube
unsigned char min=0; //Initialize the minutes digital tube
unsigned char first_start=1;//The countdown table starts counting for the first time
enum COUNT_DOWN_TIMER_STATE{INITIAL,INPUT1,INPUT2,INPUT3,RUN,PAUSE};
enum COUNT_DOWN_TIMER_STATE timer_state=INITIAL;

/****************************************************** **********
Countdown timer state machine: Pass in the key value to the countdown timer, and the countdown timer responds according to the current state and the incoming key value
*************************************************** *******/
void count_down_timer(unsigned char key_value){
    switch(timer_state){
            case INITIAL:
                 if(key_value==12)timer_state=INPUT1;//Input initial value
            break;
            case INPUT1:
                if(key_value>=0 & amp; & amp;key_value<=9){
                    min=key_value;
                    ch451_write(CH451_DIG7|min|0x80);
                    timer_state=INPUT2;
                }
            break;
            case INPUT2:
                if(key_value>=0 & amp; & amp;key_value<=9){
                    second_t=key_value;
                    ch451_write(CH451_DIG6|second_t);
timer_state=INPUT3;
                }
            break;
            case INPUT3:
                if(key_value>=0 & amp; & amp;key_value<=9){
                    second_i=key_value;
                    ch451_write(CH451_DIG5|second_i);
first_start=1;
                    timer_state=PAUSE;
                }
            break;
            case PAUSE:
                if(key_value==13){//Start timing
                    TMOD=0x01; //T0 mode is 1
                    if(first_start){//If it is the first time to start the countdown, rather than continuing the countdown after a pause
                        TH0=0xfc;//The main frequency of the CPU used in the experimental box is 11.0892MHz, and the frequency of the counter plus one is 11.0892/12=0.9241MHz
                        TL0=0x64;//Timing 1ms requires 10^(-3)*0.9241M=924.1,65536-924=64612=0FC64H
                        first_start=0;
                    }
ET0=1;//ES is the serial port interrupt enable bit;
                            //ET1 and ET0 are the interrupt enable bits of T1 and T0;
                            //EX1 and EX0 are the interrupt enable bits of external interrupts INT1 and INT0.
                            //When these bits are 1, the corresponding interrupt request is allowed to be responded to, and when they are 0, they are masked.
TR0=1;//Start T0
                   PT0=0; //T0 interrupt is low priority

                   timer_state=RUN;
                }
                break;
            case RUN:
                if(key_value==14){
                    TR0=0;//Close T0
                    timer_state=PAUSE;
                }
            break;
    }
    if(key_value==15){//terminate timing
        ET0=0;//Turn off interrupt
       timer_state=INITIAL;//return to initial state
    }
}

/****************************************************** **********
Timer T0 interrupt
*************************************************** *******/
void InterruptTimer0() interrupt 1
{
TH0=0xfc; //Reassign value
TL0=0x64;
cnt + + ;//Generate an interrupt every 1ms, each interrupt causes cnt + +, 1000 interrupts is 1s
    //Every thousand timer interrupts, modify the minutes and seconds
if(cnt>=1000) //Interruption accumulates 1000 1s
{
cnt=0; //clear to 0
second_i--;
if(second_i==0xFF){ //Minus one overflow
second_i=9;
second_t--;
if(second_t==0xFF){
second_t=5;
min--;
if(min==0xFF){
min=9;
                     second_t=5;
                     second_i=9;
}
}
}
}
}


/****************************************************** **********
read temperature
*************************************************** *******/
unsigned int read_temperature()
{
  unsigned int a=0,b=0,t=0;
  float tt=0;
  reset_ds18b20();
  write_byte(0xCC); //Skip the operation of reading the sequence number and column number 1100 1100
  write_byte(0x44); //Start temperature conversion 0100 0100
  reset_ds18b20();
  write_byte(0xCC); //Skip the operation of reading the serial number and column number
  write_byte(0xBE); //Read temperature register 1011 1110
  a=read_byte(); //Read the lower 8 bits
  b=read_byte(); //Read the upper 8 bits
  t=b;
  t<<=8;
  t=t|a;
  if(t & amp;0xf800)//The high five bits are the sign bits, 1 indicates negative temperature
  {
t=~t + 1;
//temperature_sign=1;//Temperature positive and negative
  }
  //else temperature_sign=0;
  tt=t*0.0625;//The default accuracy of the chip when powered on is 12 bits, and the temperature resolution is 0.0625℃
  t=(unsigned int)(tt*10 + 0.5); //Enlarge the output 10 times and round off
  return(t);
}

/****************************************************** **********
Display temperature
*************************************************** *******/
void display_temperature()
{
    unsigned char temperature_h; //hundred digit after temperature * 10
    unsigned char temperature_t;//tens digit
    unsigned char temperature_i;//units digit
    unsigned int f;
    f=read_temperature()-10; //Get the temperature value and subtract the temperature drift error of DS18B20
    temperature_i=f;
    f=f/10;
    temperature_t=f;
    f=f/10;
    temperature_h=f;
    ch451_write(CH451_DIG0|temperature_i);
    ch451_write(CH451_DIG1|temperature_t|0x80);//0x80 means adding a decimal point
    ch451_write(CH451_DIG2|temperature_h);
}


/****************************************************** *
Conversion subroutine: convert the key code obtained by interrupt into key value
*************************************************** /
unsigned char tran(unsigned char key){
    key &=0x3f; //Only keep the lower six bits
    if (key<4)return key;
    else if(key<12)return key-4;
    else if(key<20)return key-8;
    else if(key<28)return key-12;
}

int main(){
    unsigned char key_value;
unsigned int i;
    ch451_init(); //Initialization
    ch451_write(0x403);//Set system parameters: 0100000[CKHF][DPLR][WDOG][KEYB][DISP]B, common anode, low level active, DPLR=0
    ch451_write(0x580);//580H=010110000000B, the eighth digit from the right indicates the BCD decoding method
    ch451_write(CH451_DIG3|0x10);//space
    ch451_write(CH451_DIG4|0x10);//space
    //ch451_write(0x500);//500H=010100000000B, no decoding mode
    
    
    while(1){
        //The operation of DS18B20 has strict timing requirements, and many processes cannot be interrupted, so turn off interrupts first.
        EA=0; //Turn off interrupt
        display_temperature();
EA=1; //Enable interrupt
        
if(timer_state==RUN){
ch451_write(CH451_DIG7|min|0x80);//The seventh digit of ch451 displays the minutes with a decimal point
            ch451_write(CH451_DIG6|second_t);//The sixth digit of ch451 displays tens of seconds
            ch451_write(CH451_DIG5|second_i);//The fifth digit of ch451 displays the second digit
}
        
        EX0=1;//INT0 interrupt enabled
        PX0=1;//INT0 interrupt is high priority
        ch451_key=0x0ff;
for(i=10000; i>0; --i){
if(ch451_key!=0xff){
            key_value=ch451_key;
            key_value=tran(key_value);
            ch451_key=0x0ff;
            count_down_timer(key_value);
            break;
          }
}
        EX0=0;
    }
}

*Code Description:

Programming flow chart

Countdown timer state machine

*Reference materials

ch451 technical manual: CH451DS1.PDF – Nanjing Qinheng Microelectronics Co., Ltd.

ch451 development resources: CH451IF.ZIP – Nanjing Qinheng Microelectronics Co., Ltd.