Exploring STC 8051 Microcontrollers – Coding
|
SPI
Serial Peripheral Interface (SPI) communication is another short-distance synchronous communication method like I2C. Unlike I2C, SPI requires 3 or more connections but the bus speed is significantly higher than that of I2C’s. Except chip selection pin, all SPI devices in a SPI bus can share the same set of communication pins. Typical full-duplex SPI bus requires four basic I/O pins. Their naming suggests their individual purpose.
- Master-In-Slave-Out (MIS0) connected to Slave-Data-Out (SDO).
- Master-Out-Slave-In (MOSI) connected to Slave-Data-In (SDI).
- Serial Clock (SCLK) connected to Slave Clock (SCK).
- Slave Select (SS) connected to Chip Select (CS).
Since SPI bus is very fast, it is widely used for interfacing displays, flash memories, etc. that needs fast responsiveness.
SPI is can visualized as a shift register that shifts data in and out one-by-one with clock pulse transitions. Like I2C bus, there can be one master device in a SPI bus and virtually unlimited amount of slave devices only separated by slave selection pins. Master sends commands to slave(s) by generating clock signals and selecting one slave device at a time. Selected slave responds to commands sent by the master.
In general, if you wish to know more about SPI bus here are some cool links:
- https://learn.mikroe.com/spi-bus
- https://learn.sparkfun.com/tutorials/serial-peripheral-interface-spi
- http://ww1.microchip.com/downloads/en/devicedoc/spi.pdf
- http://tronixstuff.com/2011/05/13/tutorial-arduino-and-the-spi-bus
- https://embeddedmicro.com/tutorials/mojo/serial-peripheral-interface-spi
- http://www.circuitbasics.com/basics-of-the-spi-communication-protocol
Code
SPI.h
/*
SS MOSI MISO SCLK Hex Option
P1.2 P1.3 P1.4 P1.5 0x00 option 1
P2.2 P2.3 P2.4 P2.5 0x04 option 2
P7.4 P7.5 P7.6 P7.7 0x08 option 3
P3.5 P3.4 P3.3 P3.2 0x0C option 4
*/
#define SPI_clear do{\
SPCTL = 0x00; \
SPSTAT = 0xC0; \
SPDAT = 0x00;}while(0)
#define SPI_timeout 300
#define SPI_pin_option(value) do{P_SW1 |= value;}while(0)
#define SPI_pins(value)
#define SPI_disable bit_clr(SPCTL, 6)
#define SPI_enable bit_set(SPCTL, 6)
//clk
#define SPI_clk_sysclk_div_4 0x00
#define SPI_clk_sysclk_div_8 0x01
#define SPI_clk_sysclk_div_16 0x02
#define SPI_clk_sysclk_div_32 0x03
//MS
#define SPI_slave 0x00
#define SPI_master 0x10
//data_orientation
#define SPI_MSB_first 0x00
#define SPI_LSB_first 0x20
//CPHA
#define SPI_CPHA_leading 0x00
#define SPI_CPHA_trailing 0x04
//CPOL
#define SPI_CLK_CPOL_idle_low 0x00
#define SPI_CLK_CPOL_idle_high 0x08
//ss
#define SPI_SS_not_ignored 0x00
#define SPI_SS_ignored 0x80
#define SPI_setup(clk, MS, data_orientation, CPHA, CPOL, ss) do{ \
SPI_clear; \
SPCTL |= (clk | MS \
| data_orientation \
| CPHA \
| CPOL \
| ss); \
}while(0)
unsigned char SPI_transfer(unsigned char write_value)
{
unsigned char read_value = 0x00;
unsigned int timeout = SPI_timeout;
SPDAT = write_value;
while((!check_SPI_flag) && (timeout > 0))
{
timeout--;
delay_ms(1);
};
clear_SPI_flag;
clear_SPI_write_collision_flag;
read_value = SPDAT;
return read_value;
}
DS3234.h
#define read_cmd 0x00
#define write_cmd 0x80
#define second_reg 0x00
#define minute_reg 0x01
#define hour_reg 0x02
#define day_reg 0x03
#define date_reg 0x04
#define month_reg 0x05
#define year_reg 0x06
#define alarm1sec_reg 0x07
#define alarm1min_reg 0x08
#define alarm1hr_reg 0x09
#define alarm1date_reg 0x0A
#define alarm2min_reg 0x0B
#define alarm2hr_reg 0x0C
#define alarm2date_reg 0x0D
#define control_reg 0x0E
#define status_reg 0x0F
#define ageoffset_reg 0x10
#define tempMSB_reg 0x11
#define tempLSB_reg 0x12
#define tempdisable_reg 0x13
#define sramaddr_reg 0x18
#define sramdata_reg 0x19
#define am 0
#define pm 1
#define hr24 0
#define hr12 1
#define DS3234_SS_HIGH P12_high
#define DS3234_SS_LOW P12_low
unsigned char s = 10;
unsigned char min = 10;
unsigned char hr = 10;
unsigned char ampm = pm;
unsigned char dy = 1;
unsigned char dt = 1;
unsigned char mt = 1;
unsigned char yr = 1;
unsigned char hr_format = hr12;
unsigned char bcd_to_decimal(unsigned char d);
unsigned char decimal_to_bcd(unsigned char d);
void DS3234_init(void);
unsigned char DS3234_read(unsigned char addr);
void DS3234_write(unsigned char addr, unsigned char value);
float DS3234_get_temp(void);
void DS3234_write_SRAM(unsigned char addr, unsigned char value);
unsigned char DS3234_read_SRAM(unsigned char addr);
void DS3234_get_time(unsigned short hour_format);
void DS3234_get_date(void);
void DS3234_set_time(unsigned char hSet, unsigned char mSet, unsigned char sSet, unsigned char am_pm_state, unsigned char hour_format);
void DS3234_set_date(unsigned char daySet, unsigned char dateSet, unsigned char monthSet, unsigned char yearSet);
DS3234.c
#include "DS3234.h"
unsigned char bcd_to_decimal(unsigned char d)
{
return ((d & 0x0F) + (((d & 0xF0) >> 4) * 10));
}
unsigned char decimal_to_bcd(unsigned char d)
{
return (((d / 10) << 4) & 0xF0) | ((d % 10) & 0x0F);
}
void DS3234_init(void)
{
P12_push_pull_mode;
DS3234_SS_HIGH;
SPI_pin_option(0x00);
SPI_setup(SPI_clk_sysclk_div_16, \
SPI_master, \
SPI_MSB_first, \
SPI_CPHA_trailing, \
SPI_CLK_CPOL_idle_low, \
SPI_SS_ignored);
SPI_enable;
DS3234_write(control_reg, 0x20);
DS3234_write(status_reg, 0x48);
DS3234_set_time(hr, min, s, ampm, hr_format);
DS3234_set_date(dy, dt, mt, yr);
}
unsigned char DS3234_read(unsigned char addr)
{
unsigned char value = 0;
DS3234_SS_LOW;
SPI_transfer(addr | read_cmd);
value = SPI_transfer(0x00);
DS3234_SS_HIGH;
return value;
}
void DS3234_write(unsigned char addr, unsigned char value)
{
unsigned long temp = 0;
DS3234_SS_LOW;
temp = (addr | write_cmd);
SPI_transfer(temp);
SPI_transfer(value);
DS3234_SS_HIGH;
}
float DS3234_get_temp(void)
{
float t = 0;
signed char highByte = 0;
unsigned char lowByte = 0;
highByte = DS3234_read(tempMSB_reg);
lowByte = DS3234_read(tempLSB_reg);
lowByte >>= 6;
t = (lowByte & 0x03);
t *= 0.25;
t += highByte;
return t;
}
void DS3234_write_SRAM(unsigned char addr, unsigned char value)
{
DS3234_write(sramaddr_reg, addr);
DS3234_write(sramdata_reg, value);
}
unsigned char DS3234_read_SRAM(unsigned char addr)
{
unsigned char value = 0;
DS3234_SS_LOW;
SPI_transfer(sramaddr_reg);
SPI_transfer(addr);
value = DS3234_read(sramdata_reg);
DS3234_SS_HIGH;
return value;
}
void DS3234_get_time(unsigned short hour_format)
{
unsigned char tmp = 0;
s = DS3234_read(second_reg);
s = bcd_to_decimal(s);
min = DS3234_read(minute_reg);
min = bcd_to_decimal(min);
tmp = DS3234_read(hour_reg);
switch(hour_format)
{
case hr12:
{
ampm = (tmp & 0x20);
ampm >>= 5;
hr = (tmp & 0x1F);
hr = bcd_to_decimal(hr);
break;
}
default:
{
hr = (0x3F & tmp);
hr = bcd_to_decimal(hr);
break;
}
}
}
void DS3234_get_date(void)
{
yr = DS3234_read(year_reg);
yr = bcd_to_decimal(yr);
mt = (0x1F & DS3234_read(month_reg));
mt = bcd_to_decimal(mt);
dt = (0x3F & DS3234_read(date_reg));
dt = bcd_to_decimal(dt);
dy = (0x07 & DS3234_read(day_reg));
dy = bcd_to_decimal(dy);
}
void DS3234_set_time(unsigned char hSet, unsigned char mSet, unsigned char sSet, unsigned char am_pm_state, unsigned char hour_format)
{
unsigned char tmp = 0;
DS3234_write(second_reg, (decimal_to_bcd(sSet)));
DS3234_write(minute_reg, (decimal_to_bcd(mSet)));
switch(hour_format)
{
case hr12:
{
switch(am_pm_state)
{
case pm:
{
tmp = 0x20;
break;
}
default:
{
tmp = 0x00;
break;
}
}
DS3234_write(hour_reg, ((tmp | 0x40 | (0x1F & (decimal_to_bcd(hSet))))));
break;
}
default:
{
DS3234_write(hour_reg, (0xBF & (0x3F & (decimal_to_bcd(hSet)))));
break;
}
}
}
void DS3234_set_date(unsigned char daySet, unsigned char dateSet, unsigned char monthSet, unsigned char yearSet)
{
DS3234_write(day_reg, (decimal_to_bcd(daySet)));
DS3234_write(date_reg, (decimal_to_bcd(dateSet)));
DS3234_write(month_reg, (decimal_to_bcd(monthSet)));
DS3234_write(year_reg, (decimal_to_bcd(yearSet)));
}
main.c
#include "STC8xxx.h"
#include "BSP.h"
#include "LCD.c"
#include "lcd_print.c"
#include "DS3234.c"
#define SET 1
#define INC 2
#define SAVE 3
#define keypad_dly 10
#define disp_dly 90
void setup(void);
unsigned char get_keypad(void);
void display_value(unsigned char y_pos, unsigned char x_pos, unsigned char value);
void show_temperature();
void display_time(void);
void get_date_time_data(void);
unsigned char change_value(unsigned char y_pos, unsigned char x_pos, unsigned char value, unsigned char max_value, unsigned char min_value);
void setting(void);
void show_day(unsigned char value);
void main(void)
{
setup();
while(1)
{
P55_toggle;
setting();
get_date_time_data();
show_temperature();
display_time();
delay_ms(200);
};
}
void setup(void)
{
CLK_set_sys_clk(IRC_24M, 2, MCLK_SYSCLK_no_output, MCLK_out_P54);
P40_input_mode;
P41_input_mode;
P42_input_mode;
P55_open_drain_mode;
DS3234_init();
LCD_init();
LCD_clear_home();
load_custom_symbol();
}
unsigned char get_keypad()
{
if(!P40_get_input)
{
delay_ms(keypad_dly);
return SET;
}
else if(!P41_get_input)
{
delay_ms(keypad_dly);
return INC;
}
else if(!P42_get_input)
{
delay_ms(keypad_dly);
return SAVE;
}
else
{
return 0;
}
}
void display_value(unsigned char y_pos, unsigned char x_pos, unsigned char value)
{
unsigned char ch = 0x00;
ch = (value / 10);
LCD_goto((x_pos - 1), (y_pos - 1));
LCD_putchar((ch + 0x30));
ch = (value % 10);
LCD_goto(x_pos, (y_pos - 1));
LCD_putchar((ch + 0x30));
}
void display_time()
{
display_value(1, 1, hr);
LCD_goto(2, 0);
LCD_putstr(":");
display_value(1, 4, min);
LCD_goto(5, 0);
LCD_putstr(":");
display_value(1, 7, s);
switch(hr_format)
{
case hr12:
{
switch(ampm)
{
case pm:
{
LCD_goto(9, 0);
LCD_putstr("PM");
break;
}
default:
{
LCD_goto(9, 0);
LCD_putstr("AM");
break;
}
}
break;
}
default:
{
LCD_goto(9, 0);
LCD_putstr(" ");
break;
}
}
display_value(2, 1, dt);
LCD_goto(2, 1);
LCD_putstr("/");
display_value(2, 4, mt);
LCD_goto(5, 1);
LCD_putstr("/");
display_value(2, 7, yr);
show_day(dy);
}
void show_temperature()
{
float t = 0;
unsigned char temp = 0;
t = DS3234_get_temp();
temp = t;
display_value(2, 10, temp);
LCD_goto(11, 1);
LCD_putstr(".");
temp = ((t - temp) * 100);
display_value(2, 13, temp);
print_symbol(14, 1, 0);
LCD_goto(15, 1);
LCD_putstr("C");
}
void get_date_time_data()
{
DS3234_get_date();
DS3234_get_time(hr_format);
}
unsigned char change_value(unsigned char y_pos, unsigned char x_pos, unsigned char value, unsigned char max_value, unsigned char min_value)
{
while(TRUE)
{
if(get_keypad() == INC)
{
value++;
}
if(value > max_value)
{
value = min_value;
}
LCD_goto((x_pos - 1), (y_pos - 1));
LCD_putstr(" ");
delay_ms(disp_dly);
display_value(y_pos, x_pos, value);
delay_ms(disp_dly);
if(get_keypad() == SAVE)
{
while(get_keypad() == SAVE);
return value;
}
}
}
void setting()
{
bit set_cmd;
unsigned char tmp = 0;
set_cmd = 0;
if(get_keypad() == SET)
{
while(get_keypad() == SET);
set_cmd = 1;
}
while(set_cmd)
{
while(1)
{
if(get_keypad() == INC)
{
tmp++;
}
if(tmp > 2)
{
tmp = 0;
}
LCD_goto(9, 0);
LCD_putstr(" ");
delay_ms(disp_dly);
switch(tmp)
{
case 1:
{
LCD_goto(9, 0);
LCD_putstr("AM");
ampm = am;
hr_format = hr12;
break;
}
case 2:
{
LCD_goto(9, 0);
LCD_putstr("PM");
ampm = pm;
hr_format = hr12;
break;
}
default:
{
LCD_goto(9, 0);
LCD_putstr("24");
hr_format = hr24;
break;
}
}
delay_ms(disp_dly);
if(get_keypad() == SAVE)
{
break;
}
}
s = change_value(1, 7, s, 59, 0);
min = change_value(1, 4, min, 59, 0);
switch(hr_format)
{
case hr12:
{
hr = change_value(1, 1, hr, 12, 1);
break;
}
default:
{
hr = change_value(1, 1, hr, 23, 0);
break;
}
}
dt = change_value(2, 1, dt, 31, 1);
mt = change_value(2, 4, mt, 12, 1);
yr = change_value(2, 7, yr, 99, 0);
tmp = dy;
while(1)
{
if(get_keypad() == INC)
{
tmp++;
}
if(tmp > 7)
{
tmp = 1;
}
LCD_goto(13, 0);
LCD_putstr(" ");
delay_ms(disp_dly);
show_day(tmp);
delay_ms(disp_dly);
if(get_keypad() == SAVE)
{
dy = tmp;
break;
}
}
DS3234_set_time(hr, min, s, ampm, hr_format);
DS3234_set_date(dy, dt, mt, yr);
set_cmd = 0;
}
}
void show_day(unsigned char value)
{
LCD_goto(13, 0);
switch(value)
{
case 1:
{
LCD_putstr("SUN");
break;
}
case 2:
{
LCD_putstr("MON");
break;
}
case 3:
{
LCD_putstr("TUE");
break;
}
case 4:
{
LCD_putstr("WED");
break;
}
case 5:
{
LCD_putstr("THR");
break;
}
case 6:
{
LCD_putstr("FRI");
break;
}
case 7:
{
LCD_putstr("SAT");
break;
}
}
}
Schematic
Explanation
As with other hardware, the built-in SPI hardware also has many alternative pin options. The SPI header file describes these pin combinations.
/*
SS MOSI MISO SCLK Hex Option
P1.2 P1.3 P1.4 P1.5 0x00 option 1
P2.2 P2.3 P2.4 P2.5 0x04 option 2
P7.4 P7.5 P7.6 P7.7 0x08 option 3
P3.5 P3.4 P3.3 P3.2 0x0C option 4
*/
#define SPI_pin_option(value) do{P_SW1 |= value;}while(0)
To use SPI peripheral, we have to set it up first as shown below:
SPI_setup(SPI_clk_sysclk_div_16, \
SPI_master, \
SPI_MSB_first, \
SPI_CPHA_trailing, \
SPI_CLK_CPOL_idle_low, \
SPI_SS_ignored);
SPI_enable;
SPI_setup function sets up SPI bus clock, device role, data sequence, clock phase and polarity alongside built-in hardware slave select pin operation state. After having these set up, the SPI hardware is ready to be enabled.
Since SPI functions like a ring shift-register, only one function is needed for SPI bus data transactions. The following SPI_transfer function is used for this purpose. First data is written and then read. Reading and writing SPDAT register automatically generates SPI bus clock.
unsigned char SPI_transfer(unsigned char write_value)
{
unsigned char read_value = 0x00;
unsigned int timeout = SPI_timeout;
SPDAT = write_value;
while((!check_SPI_flag) && (timeout > 0))
{
timeout--;
delay_ms(1);
};
clear_SPI_flag;
clear_SPI_write_collision_flag;
read_value = SPDAT;
return read_value;
}
SPI peripheral is demoed here using DS3234 Real-Time Clock (RTC). The first three functions are what we basically need to fully make the RTC communicate with our STC micro. The rest of the RTC library functions are extensions of the DS3234 read and write functions. The read and write functions are typically what we see with other SPI devices. I recommend readers to check the timing diagrams of DS3234 from its datasheet and check the code in order to properly realize the SPI working principle in conjunction with the code presented here. Note that since the hardware slave select pin is not used, slave selection is done in code by changing the state of a GPIO pin.
void DS3234_init(void)
{
P12_push_pull_mode;
DS3234_SS_HIGH;
SPI_pin_option(0x00);
SPI_setup(SPI_clk_sysclk_div_16, \
SPI_master, \
SPI_MSB_first, \
SPI_CPHA_trailing, \
SPI_CLK_CPOL_idle_low, \
SPI_SS_ignored);
SPI_enable;
DS3234_write(control_reg, 0x20);
DS3234_write(status_reg, 0x48);
DS3234_set_time(hr, min, s, ampm, hr_format);
DS3234_set_date(dy, dt, mt, yr);
}
unsigned char DS3234_read(unsigned char addr)
{
unsigned char value = 0;
DS3234_SS_LOW;
SPI_transfer(addr | read_cmd);
value = SPI_transfer(0x00);
DS3234_SS_HIGH;
return value;
}
void DS3234_write(unsigned char addr, unsigned char value)
{
unsigned long temp = 0;
DS3234_SS_LOW;
temp = (addr | write_cmd);
SPI_transfer(temp);
SPI_transfer(value);
DS3234_SS_HIGH;
}
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.