Exploring STC 8051 Microcontrollers – Coding

One Wire and Bit-Banging Technique

One wire is not a standard system of communication unlike I2C, SPI and UART because it varies from device to device. Thus, this form of communication needs more coding rather than hardware-design consideration.

DS18B20 is a digital temperature sensor that uses one wire communication to communicate with a host microcontroller. One wire technique relies on time-slotting mechanism in which logical ones and zeroes are represented by pulses of variable widths. The same technique is used in infrared remote controllers, various sensors like SONAR sensors, relative humidity and temperature sensors like DHT22, etc and many other devices. The strategic advantage of one-wire technique is low pin count but the disadvantage is heavy software implementation and background processing.

Like one wire devices, sometimes we have to deal with hardware devices that do not follow conventional communication techniques as mentioned earlier and yet again we have to drive these devices using bit-banging techniques. TM1637 seven-segment display driver is such a device. Apparently, it uses something similar to I2C as two wires (clock and data) are needed for communication but in practice it has no similarity with I2C communication at all.  

The best way to deal with such cases is to study the device datasheet and look for its timing diagrams. Timing diagrams along with proprietary protocols are all that would be needed to make libraries for these devices on our own. 

Code

one_wire.h

 #define DS18B20_GPIO_init()             P30_quasi_bidirectional_mode
 
#define DS18B20_IN()                    P30_get_input
 
#define DS18B20_OUT_LOW()               P30_low
#define DS18B20_OUT_HIGH()              P30_high
 
#define TRUE                            1
#define FALSE                           0
 
unsigned char onewire_reset(void); 
void onewire_write_bit(unsigned char bit_value);
unsigned char onewire_read_bit(void);
void onewire_write(unsigned char value);    
unsigned char onewire_read(void);

one_wire.c

 #include "one_wire.h"
    
 
unsigned char onewire_reset(void)  
{                                         
     unsigned char res = FALSE; 
     
     DS18B20_GPIO_init();
    
     DS18B20_OUT_LOW();
     delay_us(480);        
     DS18B20_OUT_HIGH();
     delay_us(60);        
 
     res = DS18B20_IN();
     delay_us(480);       
     
     return res; 

 
void onewire_write_bit(unsigned char bit_value)
{
    DS18B20_OUT_LOW(); 
    
    if(bit_value)
    {       
        delay_us(104);
        DS18B20_OUT_HIGH();   
    }              
}     
 
unsigned char onewire_read_bit(void)        
{     
    DS18B20_OUT_LOW();  
    DS18B20_OUT_HIGH();  
    delay_us(15); 
 
    return(DS18B20_IN());    
}
 
void onewire_write(unsigned char value) 
{                    
     unsigned char s = 0; 
 
     while(s < 8)    
     {                              
          if((value & (1 << s)))
          {
              DS18B20_OUT_LOW(); 
              _nop_();
              DS18B20_OUT_HIGH();  
              delay_us(60);   
          }       
                              
          else
          {
            DS18B20_OUT_LOW();           
              delay_us(60);           
              DS18B20_OUT_HIGH();   
              _nop_();
          }
 
          s++;
     }
}                                      
 
unsigned char onewire_read(void)
{
     unsigned char s = 0x00;
     unsigned char value = 0x00;
    
     while(s < 8) 
     {
          DS18B20_OUT_LOW();
          _nop_();
          DS18B20_OUT_HIGH();  
          
          if(DS18B20_IN())  
          {                                      
              value |=  (1 << s);                         
          }        
           
          delay_us(60);
 
          s++;
     }     
     
     return value;
}    

DS18B20.h

 #include "one_wire.c"  
 
#define convert_T                       0x44
#define read_scratchpad                 0xBE            
#define write_scratchpad                0x4E 
#define copy_scratchpad                 0x48   
#define recall_E2                       0xB8 
#define read_power_supply               0xB4    
#define skip_ROM                        0xCC
 
#define resolution                      12
 
void DS18B20_init(void);
float DS18B20_get_temperature(void);  

DS18B20.c

 #include "DS18B20.h"
 
void DS18B20_init(void)                             
{                                       
    onewire_reset();
    delay_ms(100);
}              
 
float DS18B20_get_temperature(void)
{                                               
    unsigned char msb = 0x00;
    unsigned char lsb = 0x00;
    register float temp = 0.0;  
                           
    onewire_reset();     
    onewire_write(skip_ROM);        
    onewire_write(convert_T); 
    
    switch(resolution)   
    {                                                  
        case 12:
        {                                            
            delay_ms(750);
            break;
        }                
        case 11:                                     
        {              
            delay_ms(375);
            break;
        }           
        case 10:                             
        {                                 
            delay_ms(188);   
            break;
        }                                        
        case 9:                                   
        {                                                
            delay_ms(94);                 
            break;                            
        }                        
    }                  
 
    onewire_reset(); 
 
    onewire_write(skip_ROM);                 
    onewire_write(read_scratchpad);
 
    lsb = onewire_read();
    msb = onewire_read();
 
    temp = msb;                           
    temp *= 256.0;
    temp += lsb;
    
    
    switch(resolution)   
    {                                  
        case 12:            
        {                                                
            temp *= 0.0625;                 
            break;                            
        }       
        case 11: 
        {           
            temp *= 0.125;      
            break;
        }                
        case 10:
        {            
            temp *= 0.25;       
            break;
        }  
        case 9:                                 
        {                                 
            temp *= 0.5;        
            break;      
        }                          
    }  
    
    delay_ms(40);       
    
    return (temp);       
}

TM1637.h

 #define TM1637_CLK_HIGH             P41_high     
#define TM1637_CLK_LOW              P41_low 
 
#define TM1637_DAT_HIGH             P42_high
#define TM1637_DAT_LOW              P42_low
                                                                                   
#define TM1637_DELAY_US             4
 
#define TM1637_BRIGHTNESS_MIN       0
#define TM1637_BRIGHTNESS_1         1
#define TM1637_BRIGHTNESS_2         2
#define TM1637_BRIGHTNESS_3         3
#define TM1637_BRIGHTNESS_4         4
#define TM1637_BRIGHTNESS_5         5                                           
#define TM1637_BRIGHTNESS_6         6
#define TM1637_BRIGHTNESS_MAX       7
                                                             
#define TM1637_POSITION_MAX         4
 
#define TM1637_CMD_SET_DATA      0x40
#define TM1637_CMD_SET_ADDR      0xC0
#define TM1637_CMD_SET_DSIPLAY   0x80                           
 
#define TM1637_SET_DATA_WRITE    0x00
#define TM1637_SET_DATA_READ     0x02
#define TM1637_SET_DATA_A_ADDR   0x00
#define TM1637_SET_DATA_F_ADDR   0x04
#define TM1637_SET_DATA_M_NORM   0x00
#define TM1637_SET_DATA_M_TEST   0x10
#define TM1637_SET_DISPLAY_OFF   0x00    
#define TM1637_SET_DISPLAY_ON    0x08   
 
const unsigned char seg_data[10] =
{
    0x3F, // 0
    0x06, // 1
    0x5B, // 2
    0x4F, // 3
    0x66, // 4
    0x6D, // 5
    0x7D, // 6
    0x07, // 7
    0x7F, // 8
    0x6F  // 9                                                          
}; 
 
void TM1637_init(void);
void TM1637_start(void);
void TM1637_stop(void);
unsigned char TM1637_write_byte(unsigned char value);
void TM1637_send_command(unsigned char value);
void TM1637_clear(void);
void TM1637_display_segments(unsigned char position, unsigned char segment_value, unsigned char colon_state);

TM1637.c

 #include "TM1637.h"
 
void TM1637_init(void)
{
    P41_push_pull_mode;
    P42_push_pull_mode;
 
    TM1637_DAT_LOW;
    TM1637_CLK_LOW;  
    TM1637_send_command(TM1637_CMD_SET_DSIPLAY | TM1637_BRIGHTNESS_2 | TM1637_SET_DISPLAY_ON);
    TM1637_clear();
}
 
void TM1637_start(void)
{
    TM1637_DAT_HIGH;
    TM1637_CLK_HIGH;  
    delay_us(TM1637_DELAY_US);
    TM1637_DAT_LOW;
}
 
void TM1637_stop(void)
{
 
    TM1637_CLK_LOW;
    delay_us(TM1637_DELAY_US);
 
    TM1637_DAT_LOW;
    delay_us(TM1637_DELAY_US);
 
    TM1637_CLK_HIGH;
    delay_us(TM1637_DELAY_US);
 
    TM1637_DAT_HIGH;
}
 
unsigned char TM1637_write_byte(unsigned char value)
{
    unsigned char i = 0x08;
    unsigned char ack = 0x00;
 
    while(i)
    {
        TM1637_CLK_LOW;
        delay_us(TM1637_DELAY_US);
 
        if(value & 0x01)
        {
            TM1637_DAT_HIGH;
        } 
 
        else 
        {
            TM1637_DAT_LOW;
        }
 
        TM1637_CLK_HIGH;
        delay_us(TM1637_DELAY_US);
 
        value >>= 1;
        i--;
    }
 
    TM1637_CLK_LOW;
 
    delay_us(TM1637_DELAY_US);
                                 
    ack = P42_get_input;
 
    if(ack != 0)
    {
        TM1637_DAT_LOW;
    }
 
    delay_us(TM1637_DELAY_US);
 
    TM1637_CLK_HIGH;
    delay_us(TM1637_DELAY_US);
 
    TM1637_CLK_LOW;
    delay_us(TM1637_DELAY_US);
 
    return (ack);
}
 
void TM1637_send_command(unsigned char value)
{
 
    TM1637_start();
    TM1637_write_byte(value);
    TM1637_stop();
}
 
void TM1637_clear(void)
{
    signed char i = (TM1637_POSITION_MAX - 1);
 
    while(i > -1)
    {
        TM1637_display_segments(i, 0x00, 0x00);
        i--;
    };
}





void TM1637_display_segments(unsigned char position, unsigned char segment_value, unsigned char colon_state)
{
    if(position == 1)
    {
        switch(colon_state)
        {
            case 1:
            {
                segment_value |= 0x80;
                break;
            }
 
            default:
            {
                segment_value &= 0x7F;
                break;
            }
        }
    }
 
    TM1637_send_command(TM1637_CMD_SET_DATA | TM1637_SET_DATA_F_ADDR);
    TM1637_start();
    TM1637_write_byte(TM1637_CMD_SET_ADDR | (position & (TM1637_POSITION_MAX - 1)));
    TM1637_write_byte(segment_value);
    TM1637_stop();
}

main.c

 #include "STC8xxx.h"
#include "BSP.h"
#include "DS18B20.c"
#include "TM1637.c"
 
void setup(void);
 
void main(void)
{
  signed long t = 0;
  
  setup();
 
  while(1)
  {                                    
    t = ((signed long)DS18B20_get_temperature());                                                                                                                       
                                                                         
    if((t > 999) && (t >= 0))                                                                                                                    
    {
        TM1637_display_segments(0, 0x40, 0);                              
        TM1637_display_segments(1, 0x40, 0);        
    }                                                                                             
      
    else
    {
        TM1637_display_segments(0, seg_data[(t / 10)], 0);
        TM1637_display_segments(1, seg_data[(t % 10)], 0);          
    }   
    
    TM1637_display_segments(2, 0x63, 0);      
    TM1637_display_segments(3, 0x39, 0);                                                              
    
    delay_ms(600); 
  };
}
 
void setup(void)
{
  CLK_set_sys_clk(IRC_24M, 2, MCLK_SYSCLK_no_output, MCLK_out_P54);
  
  TM1637_init();
  DS18B20_init();
}

Schematic

Explanation

DS18B20’s one communication basics are shown below. Notice how time-slotting technique is being employed.

These timing diagrams are what that have been use to compose the following codes:

 void onewire_write_bit(unsigned char bit_value)
{
    DS18B20_OUT_LOW(); 
    
    if(bit_value)
    {       
        delay_us(104);
        DS18B20_OUT_HIGH();   
    }              
}     
 
unsigned char onewire_read_bit(void)        
{     
    DS18B20_OUT_LOW();  
    DS18B20_OUT_HIGH();  
    delay_us(15); 
 
    return(DS18B20_IN());    
}
 
void onewire_write(unsigned char value) 
{                    
     unsigned char s = 0; 
 
     while(s < 8)    
     {                              
          if((value & (1 << s)))
          {
              DS18B20_OUT_LOW(); 
              _nop_();
              DS18B20_OUT_HIGH();  
              delay_us(60);   
          }       
                              
          else
          {
            DS18B20_OUT_LOW();           
              delay_us(60);           
              DS18B20_OUT_HIGH();   
              _nop_();
          }
 
          s++;
     }
}                                      
 
unsigned char onewire_read(void)
{
     unsigned char s = 0x00;
     unsigned char value = 0x00;
    
     while(s < 8) 
     {
          DS18B20_OUT_LOW();
          _nop_();
          DS18B20_OUT_HIGH();  
          
          if(DS18B20_IN())  
          {                                      
              value |=  (1 << s);                         
          }        
           
          delay_us(60);
 
          s++;
     }     
     
     return value;
}        

Details of DS18B20’s one wire communication can be found in the following application notes from Maxim.

https://www.maximintegrated.com/en/app-notes/index.mvp/id/126

https://www.maximintegrated.com/en/app-notes/index.mvp/id/162

These notes are all that are needed for implementing the one wire communication interface for DS18B20. Please go through these notes. The codes are self-explanatory and are implemented from the code examples in these app notes. It is worth mentioning that pin declarations should be checked before actually hooking any device.

 #define DS18B20_GPIO_init()             P30_quasi_bidirectional_mode
 
#define DS18B20_IN()                    P30_get_input
 
#define DS18B20_OUT_LOW()               P30_low
#define DS18B20_OUT_HIGH()              P30_high

Bit-banging TM1637 is achieved by implementing what have been documented in the Interface interpretation section of the datasheet.

The following codes are the coded representation of the communication timing diagram of the device. Again, I insist readers to go through device datasheet for details and explanation.

 void TM1637_start(void)
{
    TM1637_DAT_HIGH;
    TM1637_CLK_HIGH;  
    delay_us(TM1637_DELAY_US);
    TM1637_DAT_LOW;
}
 
void TM1637_stop(void)
{
 
    TM1637_CLK_LOW;
    delay_us(TM1637_DELAY_US);
 
    TM1637_DAT_LOW;
    delay_us(TM1637_DELAY_US);
 
    TM1637_CLK_HIGH;
    delay_us(TM1637_DELAY_US);
 
    TM1637_DAT_HIGH;
}
 
unsigned char TM1637_write_byte(unsigned char value)
{
    unsigned char i = 0x08;
    unsigned char ack = 0x00;
 
    while(i)
    {
        TM1637_CLK_LOW;
        delay_us(TM1637_DELAY_US);
 
        if(value & 0x01)
        {
            TM1637_DAT_HIGH;
        } 
 
        else 
        {
            TM1637_DAT_LOW;
        }
 
        TM1637_CLK_HIGH;
        delay_us(TM1637_DELAY_US);
 
        value >>= 1;
        i--;
    }
 
    TM1637_CLK_LOW;
 
    delay_us(TM1637_DELAY_US);
                                 
    ack = P42_get_input;
 
    if(ack != 0)
    {
        TM1637_DAT_LOW;
    }
 
    delay_us(TM1637_DELAY_US);
 
    TM1637_CLK_HIGH;
    delay_us(TM1637_DELAY_US);
 
    TM1637_CLK_LOW;
    delay_us(TM1637_DELAY_US);
 
    return (ack);
}

The demo here is a simple DS18B20-based thermometer. The temperature sensed by the DS18B20 sensor is displayed on a TM1637-based seven segment displays.

Demo

Pages: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

Related Posts

6 comments

  • Thanks for these tutorials. I’m getting back into STCmicro coding now, having left them alone for the past several years. Back then I only used the STC89C52RC (and C54RD) but this time I’m also using the more powerful STC15 and STC8 types. Your blogs provide a wealth of useful information.

  • Hello,

    You have done great job with all these tutorials. I am an electronics engineer trying to learn some new stuff. I am located in Greece , Europe and I would like to purchase the development board that you are using and download some datasheets in English if possible but I cannot find them anywhere. Could you please help me?

  • i always get excited when you release new tutorials ,you are really doing a great job i wish i could write code and develop libraries like you.

  • Well, this is very nice and thorough tutorial indeed, many thanks!
    Unfortunately I doubt there is good any reason to learn the STC platform beyond curiosity.
    The STC 8051, although pretty evolved from the original 8051 ISA, does not offer anything crucial to justify the relatively high price of these micros and development tools along with certain cumbersomeness of this ancient platform.
    They simply can not compete even with the legacy Cortex M0 in any way. I am even not aware about any affordable debugger/emulator for them.
    All in all, I would never recommend anybody to start learning/using any 8051 without some very good reason to do so.

Leave a Reply to djalltra Cancel reply

Your email address will not be published. Required fields are marked *