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
|
hello.
this is a very good effort to document all and still share with us. thank you very much.
I have one doubt . which programming tool are you using ?
Hi, I am trying to understand the STC15w408as chip, and found this site after weeks of searching for something that sets the output of the GPIO pins to a different state. I have a the 28 pin stc15w and have connected it up with a FTDI board and can write to it using PlatformIO. The thing is, the GPIO ports if just switched on or do a reset they are in the HIGH state and I am trying to make them LOW when you do a reset.
Is your BSP code doing this and for what port or GPIO pin is it setting? I could change your P52 and P55 in your SETUP to the GPIO pins on my development board but not under standing the BSP Code.
Wonder if you get this post? but any help would be gratefully received.
Hi,
How Purchase the development board. Please,give the purchase link for this Development board.
https://www.alibaba.com/product-detail/Development-board-1T-STC8A8K64S4A12-single-chip_62391507065.html
https://world.taobao.com/item/600882463994.htm
https://www.amazon.ca/STC8A8K64S4A12-Development-Controller-Module-Minimal/dp/B08D3Y3R6T
How To read and write string data using IAP into memory
void IAP_erase(unsigned int address)
{
IAP_CONTR = 0x80; //?? IAP
IAP_TPS = 12;
// IAP_CONTR = IAP_WT;
IAP_CMD = IAP_erase_command;
IAP_address(address);
IAP_trigger;
_nop_();
_nop_();
_nop_();
IAP_clear;
}
void IAP_send_string(unsigned int uc_send_addr,unsigned char *uca_send_string,unsigned int uc_number_of_bytes)
{
unsigned int buff_cntr=0;
do
{
IAP_CONTR = 0x80; //?? ISP/IAP ??
IAP_TPS = (unsigned char)(11509200 / 1000000L); //??????
IAP_CMD = IAP_write_command;
// IAP_CMD = IAP_write_command;
IAP_ADDRH = uc_send_addr / 256; //??????(??????????????)
IAP_ADDRL = uc_send_addr % 256; //??????
IAP_DATA = uca_send_string[buff_cntr]; //???? ISP_DATA,????????????
IAP_trigger;//IAP_TRIG();
_nop_();
_nop_();
_nop_();
uc_send_addr++;
// uca_send_string++;
buff_cntr++;
IAP_clear;
delay_ms(8);
}while(–uc_number_of_bytes);
}
void IAP_read_string(unsigned int uc_read_addr,unsigned char *data_read,unsigned int uc_number_of_bytes)
{
unsigned int buff_cntr=0;
do{
IAP_CONTR = 0x80; //?? ISP/IAP ??
IAP_TPS = (unsigned char)(11059200 / 1000000L); //??????
IAP_CMD = IAP_read_command;
// IAP_CMD = IAP_read_command;
IAP_ADDRH = uc_read_addr / 256; //??????(??????????????)
IAP_ADDRL = uc_read_addr % 256; //??????
IAP_trigger;//IAP_TRIG(); //?? 5AH,?? A5H ? ISP/IAP ?????,
//???????
//?? A5H ?, ISP/IAP ?????????
//CPU ?? IAP ???,?????????
_nop_();
_nop_();
_nop_();
data_read[buff_cntr] = IAP_DATA; //???????
uc_read_addr++;
// data_read++;
buff_cntr++;
IAP_clear;
delay_ms(8);
}while(–uc_number_of_bytes);
}
stores only last byte to all bytes of flash memory sector… memory sector selected is 0xF600
Hi, I am using STC MCU since 10 years. Tech support is ZERO. but they are low cost, very stable. Now I have a problem when the chip that I used is obsolete. Now start to use STC8C2K64S4-28I-LQFP32 but no stc8Cxx.h file, I am using stc8Hxx.h file which compiles but in some stage freeze, the existing firmware. With stc8hxx.h file I can compile STC8F2K64S4-28I-LQFP32 and works not bad
.
I wrote them many times for the stc8Cxx.h file never got answer. Where Can I find that file?
Thank you
Give me detail 8f2k64s281MCU read and write programmer
Give me detail 8f2k64s281reed and write programmer distal
Hi. Can you explain how to use I2C in the slave mode ?
I tried STC8G1K08A i2c in slave mode. Doesn’t work (no response). It does not enter interrupt, even on a start condition (everything according to the code in the documentation). I also tried master mode – it works.
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 suggest you buy from AliExpress or similar platform that is available in your country…. You can find the English datasheet here. English documentation can be found in STC’s official websites such as this one….
Thank you very much for your help!!!
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.