Skip to main content
Knowledgebase
Home
Renesas Electronics Europe - Knowledgebase

What's the software I2C bus communication program for 78K0S?

Latest Updated:11/01/2007

Question:

Software I2C bus communication program for 78K0S (78K0S/Kx1+ common, program samples are targeted for KY1+)

Answer:

[Introduction]
The I2C bus is widely used as a serial bus that connects EEPROM, display controllers, and A/D or D/A converters to microcontrollers. In many microcontroller products, it is relatively easy to use the I2C bus with an on-chip dedicated interface circuit, such as is the case with the 78K0/Kx2. The following describes the programs used to control the I2C bus in single master mode when using ordinary ports in 78K0S devices that do not include an on-chip dedicated interface.

[Overview of I2C bus]
To perform communications, the I2C bus uses two signals: an open collector or open-drain output clock line signal (SCL) and a data line signal (SDA). Resistors are used to pull up these two signal lines, which are at high level when not in use. Devices that use the I2C bus include master devices that control communications and slave devices that are controlled by a master device. The following steps are followed when a master device uses the I2C bus.

Bus access is declared by issuing a start condition
First, an address is transmitted to specify the target slave device and to specify the communication direction
Communication starts when the specified slave device has responded
When a device receives data, it returns a response for each data unit (byte)
When the communication has ended, a stop condition is issued to release the bus.

For further description of the I2C bus, see the FAQ entitled"Overview of I2C bus"at the NEC Electronics website. In this description, operations comply with "fast mode", which supports transfer rates up to 400 kbps.

[Target ports]
The I2C bus must be driven via an open-drain output. When only an ordinary CMOS output is available, some other port usage must be devised. Specifically, the port's output latch is fixed to zero and processing is performed to switch between input and output operations. In other words, the following processing is performed.

  • 0 is set to the PM register → Output port → Low level
  • 1 is set to the PM register → Input port → High level

With the above processing, the bus status can be read by outputting to the bus with output at high level.

[Main operations and processing of I2C bus]
The I2C bus's main operations are described below. Here, communication from the master device to a slave device is described as transmission and communication from a slave device to the master device is described as reception.

(1)Acknowledgment of bus status
In cases such as when the CPU must re-execute processing due to a reset that has been set while using the bus, the slave device (SDA) may pull the bus to low level. When this occurs, the master device cannot use the bus. Accordingly, processing must begin by checking whether the bus has been released.

(2)Bus release
Unless the bus is released, a dummy clock will be output to the SCL until the SDA is set to high level. Ordinarily, SDA is set to high level within nine clock cycles (data + ACK). If the actual program has set a wait for the slave device (which is possible but highly unlikely), it is advised to set SDA to high level as soon as 256 dummy clocks have been output at normal pulse width, instead of waiting indefinitely for the bus to be released. Once it is confirmed that SDA is at high level, a stop condition is issued to release the bus.
(If a slave device outputs high level, but the next data is at low level, the slave device may output the next data (at low level) by setting the SCL to low level. To avoid this, the SDA is set to low level before being set again to high level. When SDA is started, it is assumed that a start condition has been issued, and since a stop condition is issued later, the timing standard is met.)

(3)Issuance of start condition
Once the bus has been released, a start condition is issued, and bus access is reported to the slave device.

(4)Transmission of slave address
When an actual communication starts, a total of 8 bits of data is transmitted, including the slave device's address (7 bits) and the communication direction (1 bit). If the slave whose address matches the transmitted address does not return an ACK signal, it is assumed that there is no slave device corresponding to that address, so the communication is terminated and a stop condition is issued to end it. If an ACK signal is returned, the communication continues in the specified direction.

(5)Transmission of sub-address
Although the I2C bus can specify the target slave device by transmitting the slave address, the slave device may have its own internal address information. For example, an internal address is specified after transmitting the slave address, in order to specify the internal address in EEPROM. If the slave's internal address capacity does not exceed 256 bytes, this is a one-byte specification. The specified address is usually retained and is refreshed by read and write operations.

(6)Transmission of data
After transmitting a sub-address, data is transmitted and written to the specified sub-address. (In EEPROM, the actual write operation starts after receiving a stop condition.)
When data that includes the sub-address is transmitted, the slave device returns an ACK signal if it has successfully received the data. Once this ACK signal is returned, the next processing begins, but if it is not returned, the communication is terminated and a stop condition is issued to end it.

(7)Reception of data
When reception is specified as of the communication direction specifications, data reception is performed once the slave address has been transmitted. The master that receives the data returns an ACK signal in order to receive the next data. When the received data is the final data and no more data will be received, a NACK signal must be returned to indicate to the slave device that reception has been completed.

(8)Issuance of stop condition
To release the bus after a communication has ended, a stop condition is issued by setting the SDA to high level while the SCL is at high level.

(9)Issuance of restart condition
To change the data transfer direction while maintaining the bus mastership, a second start condition is issued. Immediately after this restart, the slave address is transmitted and the communication direction can then be specified again.
Generally, this method is used to specify an internal address and then read data from the slave device. Usually, the slave address is transmitted into the transmission direction first and the slave's internal address is specified as a sub-address. Next, a restart condition is issued, after which the reception direction is specified and the slave address is transmitted. This enables reading of data from the specified internal address in the slave device, after which data can be successively read from the specified address.

[Main bus statuses (reference)]
The main bus statuses are a described below.

  Status SCL SDA
(1) Bus not used H H
(2) Data transition L Data is in transition
(3) Data is stable H H/L
(4) After issuance of start condition H L
(5) Immediately after address transmission (9-clock wait mode) L ACK→H
(6) 9-clock wait mode (standby for next transmission) L Next data
(7) After address transmission (communications starts when reception is specified) L Receive data
(8) After address transmission (communications starts when transmission is specified) L Transmit data
(9) 8-clock wait mode L L=ACK/H=NACK
(10) After issuance of stop condition (bus is not used) H H

 

  • The two signal lines (SCL and SDA) are at high level when the bus is not being used, as in steps (1) and (10), above.
  • Basically, SDA changes when SCL is at low level and SDA is stable when SCL is at high level.
  • After a start condition is issued (SDA is set to low level while SCL is at high level), SCL remains at high level and SDA is at low level. To transmit the slave device's address subsequently, set SCL to low level first.
  • At step (5) or (6) above, which occurs after SCL has sent nine clock cycles during address transmission or data communications, setup for the next communication starts when SCL is at low level. If there is no next communication, SDA is set to high level. Otherwise, the start bit of the next data is output at step (7) or (8) above.
  • After 8 bits of data has been transmitted and received at step (9), SCL is set to low level and then waits for an ACK signal response.
  • At step (6) or (9), the slave device can delay communications by pulling SCL to low level. Accordingly, the master device must confirm that SCL is at high level before proceeding to the next processing.

[Sample program]
Although the basic control is performed by the assembler program, the control functions can be called from C programs. (The sample program shown below is only for reference.)
For this program, a 78K0S/KY1+ microcontroller operates using a high-speed internal oscillator, and P2.2 is used for SDA signals while P2.3 is used for SCL signals. When other bits in P2 register are manipulated, the output latches for P2.2 and P2.3 are overwritten. When other bits in P2 register are used as output ports, the output latch's data images are set up in internal RAM. To manipulate the port output, manipulate RAM data so that the result is written to the port.

Caution 1

When processing speed is demanded, programs are often coded in assembly language whereas main control functions are coded in C language. In such cases, subroutines coded in assembly language are called as functions from the C language program, however caution is required concerning the naming of the subroutines. In assembly language, a subroutine name must always be specified starting with "__" (two underscores). When called from a C language program, they are specified using only "_" (one underscore). For example, a program that initializes the port to be used by the I2C bus might use the name "__setup_i2c_port" in its assembly code sections and might use "_setup_i2c_port();" when calling a C language program.


A byte variable (i2c_error) for the error status of execution results and a bit variable (acke) that specifies whether to return an ACK signal to the slave device are provided for internal RAM. A declaration example is shown below. These variables are all controlled by a specialized program.

comdata DSEG     SADDR      ; Saves variables to saddr area
i2c_error:  DS  1           ; Error flag
    BSEG                    ; Indicates bit variable areas
acke    DBIT                ; ACK control flag

(1)Port definitions
PMSW_SDA is the name used for the SDA signal. When this signal's value is 1, the pin is an input port, and PSW_SDA can be read to detect the status of the SDA signal. If there is no output from the slave device, the SDA signal is set to high level. When its value is "0", the pin is an output and the SDA signal is at low level since the output latch value is also 0.
PMSW_SCL is the name used for the SCL signal. When this signal's value is 1, the pin is an input port, and PSW_SCL can be read to detect the status of the SCL signal. Normally, SCL is set to high level by setting 1. Unless a wait is issued by the slave device, it is pulled to high level by an external pull-up resistor. When its value is 0, the pin is an output port and the SCL signal can be set to low level.

• Definition example for assembler (definition of port to be used)

PMSW_SDA    equ PM2.2       ; Used for controlling SDA signal 
PSW_SDA     equ P2.2        ; Used for reading SDA signal 
PSW_I2C     EQU P2          ; Port 2 is used

PMSW_SCL    equ PM2.3       ; Used for controlling SCL signal 
PSW_SCL     equ P2.3        ; Used for reading SCL signal

The following macros are defined to make the meaning of actual port operations (mode register operations) easier to understand.
• Port operation definition

_scl_hi         macro       ; Indicates "_scl_hi" macro definition
        set1    PMSW_SCL    ; Sets up PM2.3 for input
        endm                ; Indicates end of macro definition


_scl_lo         macro
        clr1    PMSW_SCL    ; Clears PM2.3 and outputs at low level to SCL
        endm


_sda_hi         macro
        set1    PMSW_SDA    ; Sets PM2.2 for input
        endm

_sda_lo         macro
        clr1    PMSW_SDA    ; Clears PM2.2 and outputs at low level to SDA
Note 1

Macro functions are used when creating programs to code typical processing more efficiently and to make the program easier to understand. In this case, the processing corresponds to the bit manipulation instructions for port mode register. In this case, macro functions are used to make the program easier to understand rather than to make coding of typical processing more efficient. By writing "_scl_lo" instead of "CLR1 PMSW_SCL", it becomes easier to intuitively grasp the type of processing to be performed.


(2) Port initialization (__setup_i2c_port)
The output latch of the port to be used is set to 0 (0 is written in byte units to the port). The port mode is then set to input mode. In this program, a public declaration using names that start with "__" is used to call from parts of the C language code.

    public  __setup_i2c_port    ; Public declaration enabling use 
                                ; from external source
__setup_i2c_port:
    MOV PSW_I2C,#0              ; Public declaration enabling use 
                                ; from external source
                                ; Data is fixed to zero during use
    _scl_hi                     ; PM is input
    _sda_hi
    RET

(3) Bus availability check (__i2c_busy)
This checks for bus availability. The result is returned using a carry flag. When the bus is being used locally or when SCL or SDA is at low level, the bus is judged to be busy and the carry flag is set (return value is "true"). If the bus has been released, the carry flag is cleared (return value is "false"). There is no processing to change the port status. Here, a special name (i2c_busy) is added to make it easier to reference in assembly language.

;bit    _i2c_busy(void)
    public  __i2c_busy
__i2c_busy:
i2c_busy:
    SET1    CY                  ; Set busy flag
    BF  PSW_SDA,$busbusy        ; Check SDA line
    BF  PSW_SCL,$busbusy        ; Check SCL line
    CLR1    CY                  ; Flag is reset when bus is released
busbusy:
    RET
Note 2

To check single-bit port status in the 78K0S, conditional branch instructions (BF or BT instruction) such as shown in the above sample program are used. When using a BF instruction, a branch occurs when the single-bit port value is 0 and does not occur when it is 1. In the above sample program, when both PSW_SDA and PSW_SCL are 1, neither of the two conditional branch instructions execute a branch and CY (carry flag) is cleared.


(4) Bus release (__free_i2c_bus)
If a check of the bus status indicates that the bus has not been released, dummy clocks are sent while waiting for bus availability. If the bus cannot be released by 256 clock pulses, the carry flag is cleared (return value is "false") to return an error.
A stop condition is issued once the SDA and SCL signals are at high level.

Caution 2

Under this status, if SCL is set to low level, the slave device may output at low level for the next data (when the slave device's output data is 1, but the next data is 0). In this case, when SCL is at high level, set SDA to low level first and then back to high level again . SDA fall to low level is interpreted as issuance of a start condition, but a stop condition is later issued by rise to high level, so it is ignored.


After a stop condition has been issued to release the bus, the carry flag is set (return value is "true") to return.
For the dummy clock transmission that is used for this, the operating clock time is held at 12 clock cycles to preserve the SCL's low level width as 1.3µs. The high level width is not necessarily maintained, since it includes overheads such as subroutine calls. If the slave device has set a wait, low level is maintained in the bus even when the master device is set to high level. If this status continues, the I2C bus cannot be used at all. Consequently, there is no waiting for SCL to go to high level. With a margin, processing is terminated after 256 times.

;bit    _free_i2c_bus(void)
    public  __free_i2c_bus
__free_i2c_bus:
free_i2c_bus:
    _sda_hi                 ; SDA is set to high level as a precaution
    _scl_hi                 ; SCL is set to high level as a precaution
    CALL    !i2c_busy       ; Check bus status 
    NOT1    CY
    BC  $free_exit1         ; Exit when bus has been released
    MOV A,#0                ; Set limit of 256 times
dumyclkloop:
    CALL    !scl_pulse      ; Dummy clock is output to SCL
    CALL    !i2c_busy       ; Bus status check
    NOT1    CY
    BC  $free_exit1         ; Exit when bus has been released
    DEC A                   ; Has limit been exceeded?
    BNZ $dumyclkloop        ; Continue if within limit
    RET                     ; Set carry flag and return if error occurs
free_exit1:
    PUSH    PSW             ; Save flag as a precaution
    CALL    !I2C_STPR       ; Issue stop condition 
    POP PSW
    RET

• A dummy clock that meets the I2C bus standard is generated.

scl_pulse:
    _scl_lo                 ; 6:SCL is set to low level
    NOP                     ; 2:To secure the low-level width of 1.3 µs,
    NOP                     ; 2:wait at least 11 clock cycles 
    NOP                     ; 2:before activation.
    _scl_hi                 ; 6:SCL is set to high level.
    RET                     ; 6:High-level period is enough.

(5) Issuance of start condition (__I2C_STR)
To prepare for issuance of a start condition, first set SDA then SCL to high level. (If SCL is already at high level, this is the same as a stop condition. For a restart, SCL must be at low level.)
Afterward, the BF instruction confirms that SCL is at high level, then SDA is set low to issue a start condition. (This is inserted in order to wait for the slave device's wait mode to be canceled at the ninth clock. In almost all cases, this is considered unnecessary. To delete the BF instruction, replace it with two NOP instructions.)

Note 3

The I2C bus has a communication pause function that operates when the slave device pulls SCL to low level. Consequently, after the master device has set SCL to high level, a check is performed to confirm that SCL is actually at high level. "BF PSW_SCL,$$" is then used. If the SCL line is at low level (as read by PSW_SCL), this command branches itself as a repeated command. This enables progress to the next processing when SCL is set to high level.
    public  __I2C_STR
__I2C_STR:
I2C_STR:
    _sda_hi                 ; 6:SDA is set high for preparation
    NOP                     ; 2:
    _scl_hi                 ; 6:SCL is set high
    BF  PSW_SCL,$$          ;10:SCL's rise is detected
    _sda_lo                 ; 6:Start condition is issued
    RET                     ; 6:

(6) Issuance of stop condition (__I2C_STPR)
Since setting SCL to low level first may cause the next transfer to start, processing is performed when SCL is already at low level. If SCL is at high level, setting SDA to low level is the same as issuing a stop condition. Nevertheless, it should not be considered inappropriate to subsequently issue a stop condition.
In this case as well, the BF instruction confirms that SCL is at high level before the next processing is performed. This is done for the same reasons as when issuing a start condition, as described above.

    public  __I2C_STPR
__I2C_STPR:
I2C_STPR:
    _sda_lo                 ; 6:SDA is set to low level. Even if this is
    NOP                     ; 2:interpreted as a start condition, a stop
    NOP                     ; 2:condition can be issued immediately.
    _scl_hi                 ; 6:SCL is set to high level as a precaution.
    BF  PSW_SCL,$$          ;10:Rise of SCL is detected 
    _sda_hi                 ; 6:Stop condition is issued
    NOP                     ; 2:
    NOP                     ; 2:
    RET                     ; 6:

(7) Output 8-bit data to I2C bus (__put_i2c)
This routine outputs to the I2C bus 8-bit data that has been passed via the X register that is used to receive and pass single-byte arguments during C language's normal mode. When SCL is at low level, output proceeds starting from the MSB, then an ACK response check is performed. If there is an ACK response, the carry flag is set (return value is "true") and returned.
If there is no ACK response, the carry flag is cleared (return value is "false") and returned.
When processing has ended, SCL is at low level (9-clock wait mode).

Note 4

When a transmission starts (9-clock wait mode from the previous communication), a wait for wait cancellation is set, along with SCL rise wait processing to confirm whether SCL has actually gone to high level. (This processing is probably not necessary but is input to all bits as a precaution. If it is deemed unnecessary because the parts where waits are not inserted have already been checked, then it should be deleted and replaced with two NOP instructions.)


[Program flow]
The program flow is illustrated as follows.

i2c_1.gif

[Sample program]

;bit    _put_i2c(u8 data);
    public  __put_i2c
__put_i2c:
put_i2c:
    PUSH    BC              ; Saved for use with loop counter
    MOV B,#8
    MOV A,X                 ; Input data (argument) is 8 bits
;
;
OUTLOOP:
    _scl_lo                 ; 6:Data output setup when SCL goes low
    NOP                     ; 2:For fall time adjustment
    ROL A,1                 ; 2:Shift out from MSB to CY
    BNC $OUTLO              ; 6:Branch when data is 0
    _sda_hi                 ; 6:Output 1
    BR  $CLKHI              ; 6:SCL is set high
OUTLO:  _sda_lo             ; 6:0 is output
    NOP                     ; 2:Secure setup time
CLKHI:
      _scl_hi               ; 6:
    BF  PSW_SCL,$$          ;10:SCL's rising edge is detected (as a precaution)
    DBNZ    B,$OUTLOOP      ; 6:

    POP BC                  ; 6:
    _scl_lo                 ; 6:Set SCL low at eighth clock
    NOP                     ; 2:
    _sda_hi                 ; 6:SDA is switched to input
    NOP                     ; 2:
    NOP                     ; 2:
    NOP                     ; 2:
    _scl_hi                 ; 6:Set SCL high at ninth clock
;
;   Wait for wait cancellation
;
    SET1    CY              ; 2:

    BF  PSW_SCL,$$          ; Wait for detection of SCL's rise

    BF  PSW_SDA,$ACKOK      ;10:Obtain/ACK signal 
    NOT1    CY              ; 2:
ACKOK:

    _scl_lo                 ; 6:Set SCL low (9th clock)
    NOP
    RET

(8) 8-bit data is output to the I2C bus. The bus is released when an error occurs (__put_i2c2)
Data is output by the routine shown in (7) above, and if an error occurs a stop condition is issued and returned. The I/O conditions are the same as in (7).

;static bit  _put_i2c2(u8 data)
    public  __put_i2c2
__put_i2c2:
    CALL    !put_i2c            ; Output 1 byte
    BNC $PUTERROR               ; Go to termination processing if error occurs
    RET                         ; End if normal
PUTERROR:
    CALL    !I2C_STPR           ; Issue stop condition
    CLR1    CY                  ; Set as false (as a precaution)
    RET

(9) 8-bit data is input from I2C bus (__get_i2c)
8-bit data is received from the slave device selected for the I2C bus, and the received data is saved to the C register and returned. Data from the slave is read starting from the MSB when SCL is at high level. If the acke bit = 1 after reception completion, an ACK response is sent to notify the slave device for following data. If the acke bit = 0, a NACK response is sent to notify the slave device that the last data has been received.
When processing has ended, SCL is at low level (9-clock wait mode).

[Program flow]
The program flow is illustrated as follows.

i2c_2.gif

[Sample program]

;u8 _get_i2c(void);
    public  __get_i2c
__get_i2c:
    MOV A,#11111110b        ; Shift and end when CY = 0
INLOOP:
    NOP                     ; 2:
    SET1    CY              ; 2:Set 1 as initial value
    _scl_hi                 ; 6:SCL goes high
    BF  PSW_SCL,$$          ; SCL rise is detected

    BT  PSW_SDA,$DATAIS1    ;10:
    NOT1    CY              ; 2:Invert carry when data is 0
DATAIS1:
    ROLC    A,1             ; 2:Shift in read result to A register 
    _scl_lo                 ; 6:SCL goes low
    BC  $INLOOP             ; 6:8-bit loop

;******************************************************
;*  ACK response control                              *
;******************************************************
    BF  _acke,$ACKEND           ;10:Check for ACK response
    _sda_lo                     ; 6:Return ACK
ACKEND:
    _scl_hi                     ; 6:Rise of 9th clock
;
;     This is the response when the slave device has an 8-clock wait
;     while waiting for the SCL signal to go to high level. 
;     Normally not required?
;
    BF  PSW_SCL,$$              ; Detection of SCL's rise (as a precaution)
    NOP                         ; 2:
    _scl_lo                     ; 6:Falling edge of ninth clock

    MOV C,A                     ; 4:return (u8)
    _sda_hi                     ; 6:SDA is set to input mode when completed
    RET
Note 5

Here, an A register is used for loop counting. The capture (shift-in from right) of receive data (bits) to the A register is combined with this loop counting operation. When 11111110b is set to the A register as the initial value, it changes with each shift-in to 1111110xb → 111110xxb → and 11110xxxb. When the seventh time is completed, the setting is 0xxxxxxxb, and the shift result so far causes the carry flag to be set. When the 8th shift is performed, the last bit of the initial value 0 is sent to be carried. Accordingly, when the carry value is 0, it indicates that the 8th time is completed.


(10) Clear error flag (__clear_i2c_error)
This processing simply clears internal variables. The error flag status is the return value.

;static u8 _clear_i2c_error(void)
    public  __clear_i2c_error
__clear_i2c_error:
clear_i2c_error:
    MOV i2c_error,#I2C_NO_ERROR ;
    MOV C,#I2C_NO_ERROR
    RET

(11) Set error flag (__set_i2c_error)
This sets the error status to an internal variable. Errors are defined as follows.

I2C_NO_ERROR        = 0x00:No error
I2C_BUS_NOT_FREE    = 0x10:Bus has not been released
I2C_ADDR_NACK       = 0x11:Slave device has not responded to address
I2C_RADDR_NACK      = 0x12:Slave device has not responded to read address
I2C_REG_ADDR_NACK   = 0x13:Unexpected NACK was returned from slave device (register)
I2C_DATA_NACK       = 0x14:Unexpected NACK was returned from slave device (data)

;static u8 _set_i2c_error(u8 error)
    public  __set_i2c_error
__set_i2c_error:
set_i2c_error:
    XCH A,X                     ; Capture error data
    MOV i2c_error,A             ; Set error flag
    MOV C,A                     ; Set return value
    XCH A,X
    RET

(12) Read error flag (__get_i2c_error)
This reads the contents of error flags. The errors are defined in (11) above.

;static u8 _get_i2c_error(void)
    public  __get_i2c_error
__get_i2c_error:
get_i2c_error:
    PUSH    AX
    MOV A,i2c_error             ; Read error flag
    MOV C,A                     ; Set return value
    POP AX
    RET

(13) Set ACK response enable flag (__ACK_EN)
This sets enable status for ACK responses issued when the master device receives data. This status should usually be set to "enable" and set to "disable" when receiving the last data.

;void _ACK_EN(void)
    public  __ACK_EN
__ACK_EN:
ACK_EN:
    SET1    acke                ; Set ACK response flag 
    RET

(14) Set ACK response disable flag (__ACK_DS)
This is used to set a NACK response when the master device receives the last data.

;void _ACK_DS(void)
    public  __ACK_DS
__ACK_DS:
ACK_DS:
    CLR1    acke                ; Clear ACK response flag 
    RET

(15) Master transmit processing (__write_i2c_block)
The I2C bus is used to write to a 256-byte EEPROM. To accommodate use of this subroutine from C language (normal mode), the required parameters are passed using the same format as in C language. The passed parameters have the following format.

First argument:X register   :Address of I2C bus's slave device
Second argument:First stack layer   :Internal address to be accessed by slave device
Third argument:Second stack layer   :Address of buffer where data is stored
Fourth argument:Third stack layer   :Byte count of write data


These parameters are pushed onto the stack in two-byte units, from the fourth argument to the second argument in that order, and then this subroutine is called.
This subroutine writes the number of bytes of data specified by the fourth argument from the buffer memory specified by the third argument to the internal addresses that start at the address specified by the second argument in the EEPROM at the slave device address specified by the first argument. Once this write operation is completed, the bus is released and the error flag is cleared and returned. Since the return value that is stored in the C register is the same as the error flag value, errors can be confirmed by checking the returned value. (The errors are described above in "(11) Set error flag")

[Program flow]
The program flow is illustrated as follows.

i2c_3.gif

The assembly language code's __put_i2c2 function is used for transmit processing in byte units, and if an error occurs the bus is released and an error corresponding to the status where an error occurred is returned. If there are no errors, the bus is released and no errors are returned. (EEPROM detects a stop condition and writes the transmitted data to an internal cell.)
An example of a C language program that performs the same processing as this sample program is shown in section (1) under "Program Use Examples" below. Although the sequence of processing differs slightly, the processing itself is the same. The same function call methods and return values are used.

[Sample program]

;**************************************************************
;*                                                            *
;*  Block data transfer to I2C bus                            *
;*  Targets are devices with sub-addresses                    *
;*  of 256 bytes or less                                      *
;*                  ADR(W), SUB_ADR, DATA...                  *
;**************************************************************
;*  [i] : u8 i2c_adr    ... Address of I2C slave device       *
;*  [i] : u8 reg_adr    ... Address within slave device       *
;*  [i] : u8 *data      ... Address of write data             *
;*  [i] : u8 size       ... Byte count (0x00 = 256)           *
;*  [r] : u8            ... error code                        *
;*                                                            *
;*  The actual arguments are pushed onto the stack            *
;*  in 16-bit units, so the required arguments are gotten by  *
;*  moving the SP value to HL register and specifying the     *
;*  relative address based on HL.                             *
;*                                                            *
;**************************************************************
;u8 _write_i2c_block(u8 i2c_adr, u8 reg_adr, u8 *data, u8 size)

    public  __write_i2c_block
__write_i2c_block:
    PUSH    HL                  ; Save register
    PUSH    AX
;
;  This subroutine is called via a function call from C (normal mode). 
;  The stack data at this stage is as follows.
;
;   sp   : X register (first argument)     : Slave address
;   sp+1 : A register                      : Not used (only for register saving)
;   sp+2 : L register                      : Not used (only for register saving)
;   sp+3 : H register                      : Not used (only for register saving)
;   sp+4 : Lower PC                        : Lower return address
;   sp+5 : Higher PC                       : Higher return address
;   sp+6 : Second argument                 : Address in slave device 
;   sp+7 :                                 : Not used (only for register saving)
;   sp+8 : Lower values of third argument  : Write data save address 
;   sp+9 : Higher values of third argument
;   sp+10: Fourth argument                 : Write data count

    MOVW    AX,SP               ; Prepare to pop the second and subsequent arguments 
    MOVW    HL,AX
    MOV X,#I2C_BUS_NOT_FREE
    CALL    !set_i2c_error      ; Use dummy to set error
    CALL    !free_i2c_bus       ; Can bus be released?
    BNC $EXITSUB                ; Returned when bus cannot be released

    CALL    !I2C_STR            ; Issue start condition

    MOV X,#I2C_ADDR_NACK
    CALL    !set_i2c_error      ; Use dummy to set error
    MOV A,[HL]                  ; Pop slave address
    AND A,#11111110b            ; Set direction flag to transmit
    MOV X,A
    CALL    !put_i2c2           ; Transmit slave address in transmit direction
    BNC $EXITSUB                ; Return with error if no response from slave device 

    MOV X,#I2C_REG_ADDR_NACK    ;
    CALL    !set_i2c_error      ; Set dummy error
    MOV A,[HL+6]                ; Pop second argument from stack
    MOV X,A
    CALL    !put_i2c2           ; Transmit slave device's internal address
    BNC $EXITSUB                ; Return with error if no response from slave device

    MOV A,[HL+10]               ; Pop fourth argument from stack
    MOV B,A                     ; Set data count to B register
    MOV A,[HL +8]               ; Pop third argument from stack
    MOV X,A                     ; Set third argument (write data save address) 
    MOV A,[HL+9]                ; to 
    MOVW    HL,AX               ; HL register
    MOV X,#I2C_DATA_NACK        ; Set "no ACK response to data" to flag 
    CALL    !set_i2c_error      ; as dummy setting
WBLOOP:
    MOV A,[HL]                  ; Read write data from buffer
    MOV X,A
    CALL    !put_i2c2           ; Write data
    BNC $EXITSUB                ; Returned if error occurs

    INCW    HL                  ; Refresh data address
    DBNZ    B,$WBLOOP           ; Repeat in data count units

    CALL    !I2C_STPR           ; Release bus
    CALL    !clear_i2c_error    ; Clear error flag 
EXITSUB:
    MOV B,#0                    ; Clear higher return value (unnecessary?)
    POP AX
    POP HL
    RET

(16) Master device receive processing (__read_i2c_block)
Using the I2C bus, data is read from a 256-byte EEPROM.
The number of bytes specified by the fourth argument is read to the buffer specified by the third argument from the addresses starting at the internal address specified by the second argument in the slave device specified by the first argument.
The assembly language code's __put_i2c2 function is used for transmit processing for the slave address or slave internal address, and if an error occurs the bus is released and an error corresponding to the status where an error occurred is returned. If there are no errors during this processing, the bus is released and no errors are returned.

[Program flow]
The program flow is illustrated as follows.

i2c_4.gif

i2c_5.gif

[Sample program]

;****************************************************************
;*                                                              *
;*  Reception of block data from I2C device                     *
;*  Targets are devices with sub-addresses of 256 bytes or less *
;*                  ADR(W), SUB_ADR, DATA...                    *
;****************************************************************
;*  [i] : u8 i2c_adr    ... Address of I2C slave device         *
;*  [i] : u8 reg_adr    ... Address within slave device         *
;*  [i] : u8 *data      ... Address of write data               *
;*  [i] : u8 size       ... Byte count (0x00 = 256)             *
;*  [r] : u8            ... error code                          *
;*                                                              *
;*  The actual arguments are pushed onto the stack in 16-bit    *
;*  units, so the required arguments are gotten by moving the   *
;*  SP value to HL register and specifying the relative         *
;*  address based on HL. About the relative address, refer to   *
;*  [Sample program] of "(15) Master transmit processing".      *    
;****************************************************************
;u8 _read_i2c_block(u8 i2c_adr, u8 reg_adr, u8 *data, u8 size)

    public  __read_i2c_block
__read_i2c_block:
    PUSH    HL
    PUSH    AX
    MOVW    AX,SP               ; Prepare to pop the second and subsequent
                                ; arguments 
    MOVW    HL,AX               ; Set pointer to HL register
    MOV X,#I2C_BUS_NOT_FREE
    CALL    !set_i2c_error      ; Use dummy to set error
    CALL    !free_i2c_bus       ; Can bus be released?
    BNC $EXITSUBR               ; Returned when bus cannot be released
    CALL    !I2C_STR            ; Issue start condition

    MOV X,#I2C_ADDR_NACK
    CALL    !set_i2c_error      ; Use dummy to set error
    MOV A,[HL]                  ; Pop slave address
    AND A,#11111110b            ; Set direction flag to transmit
    MOV X,A
    CALL    !put_i2c2           ; Transmit slave address in transmit direction
    BNC $EXITSUBR               ; Return with error if no response from slave device

    MOV X,#I2C_REG_ADDR_NACK
    CALL    !set_i2c_error      ; Use dummy to set error
    MOV A,[HL+6]                ; Pop second argument from stack
    MOV X,A
    CALL    !put_i2c2           ; Transmit slave device's internal address
    BNC $EXITSUBR               ; Return with error if no response from slave device

    CALL    !I2C_STR            ; Issue restart condition

    MOV X,#I2C_RADDR_NACK
    CALL    !set_i2c_error      ; Use dummy to set error
    MOV A,[HL]                  ; Pop slave address
    OR  A,#00000001b            ; Set direction flag to transmit
    MOV X,A
    CALL    !put_i2c2           ; Transmit slave address in receive direction
    BNC $EXITSUBR               ; Return with error if no response from slave device 

    SET1    acke                ; Set ACK response
    MOV A,[HL+10]               ; Pop fourth argument from stack
    MOV B,A                     ; Set data count to B register
    MOV A,[HL+8]                ; Pop third argument from stack
    MOV X,A                     ; Third argument (write data save address)
    MOV A,[HL+9]                ; is set
    MOVW    HL,AX               ; to HL register

BRLOOP:
    MOV A,#1                    ; Check whether this is the last data
    CMP A,B
    BNZ $BRNEXT                 ; If it is the last data (1 byte remaining),
    CLR1    acke                ; set a NACK response.
BRNEXT:
    CALL    !get_i2c            ; Read data
    MOV A,C                     ;
    MOV [hl],a                  ; Write read data to buffer

    INCW    HL                  ; Refresh pointer 
    DBNZ    B,$BRLOOP           ; Repeat in data count units

    CALL    !I2C_STPR           ; Release bus
    CALL    !clear_i2c_error    ; Clear error flag 
EXITSUBR:  
    MOV B,#00H                  ; Any extra?
    POP AX
    POP HL
    RET

[Program use example]
As an example of using the assembly language-based programming described above, the following is an example of function calls set in a C language program.
The functions shown in (1) and (2) below perform the same processing as the assembly language programs shown above in (15) and (16). In these examples, basic control routines that have been coded in assembly language are used to control a 256-byte EEPROM via the I2C bus. In addition, (3) is an example of using assembly language programs such as in (15) and (16), and programs using a function such as in (1) and (2) below.
When using the examples described below, a declaration, such as one that declares "unsigned char" as "u8", must be entered in order to reference the assembly language programs from an external source.

#pragma SFR
#pragma  EI
#pragma  DI
#pragma  NOP
typedef unsigned char   u8, uint8,  /* same as "byte"   */
            *u8Ptr, *uint8Ptr;  /* same as "bytePtr"    */
/*  Error flag manipulation-related processing functions  */

extern      u8  _set_i2c_error(u8 error);
extern      u8  _get_i2c_error(void);
extern      u8  _clear_i2c_error(void);

/*  Error flag status definitions          */
enum{
    I2C_NO_ERROR        = 0x00, // No errors (0)
    I2C_BUS_NOT_FREE    = 0x10, // Bus has not been released
    I2C_ADDR_NACK       = 0x11, // No response to address from slave device 
    I2C_RADDR_NACK      = 0x12, // No response to READ address from slave device 
    I2C_REG_ADDR_NACK   = 0x13, // Unexpected NACK was returned (register)
    I2C_DATA_NACK       = 0x14  // Unexpected NACK was returned (data)
};

/*  ACK response control-related processing functions       */

extern      void    _ACK_EN(void);
extern      void    _ACK_DS(void);

extern      void    _setup_i2c_port(void);
extern      bit _i2c_busy(void);
extern      bit _free_i2c_bus(void);

/*  I2C bus initialization-related processing functions    */

extern      void    _I2C_STR(void);
extern      void    _I2C_STPR(void);

/*  I2C bus byte transfer basic functions    */

extern      bit _put_i2c(u8 data);
extern      bit _put_i2c2(u8 data);
extern      u8  _get_i2c(void);

/*  I2C bus block transfer processing functions */

extern      u8 _write_i2c_block(u8 i2c_adr, u8 reg_adr, u8 *data, u8 size);
extern      u8 _read_i2c_block(u8 i2c_adr, u8 reg_adr, u8 *data, u8 size);
Caution 3

These programs cause the output from an 8 MHz internal oscillator to be operated as a CPU clock. Accordingly, when the 78K0S/KY1+'s POC is cancelled, operation at 8 MHz is no longer possible, until the power supply voltage has reached at least 4V. Therefore, the following processing must be described in the hdwinit function to perform an initialization.
    LVIS = 0x00;        /* select VLI=4V */
    LVIM = 0b10000000;  /* Start VLI detection*/
    PCC = 0x00;         /* CPU clock is fx/4 */
    _clear_i2c_error(); /* I2C flag clear */
    _setup_i2c_port();  /* Initialize I2C port*/
/*
    wait for 0.17ms(=0.2ms-(32+30)*52/1000)
*/
    for (work1 = 0; work1 < 10; work1++){
        NOP();
    }
    WDTM = 0b01110000;  /* Stop WDT */
    LSRSTOP = 1;        /* Stop Low Speed OSC */
    while ( LVIF ){
        NOP();
    }   /* wait VDD > 4V */

    PPCC = 0;       /* CPU clock is fx=fR */
    IF0 = 0x00;     /* Clear interrupt */

 

Note 6

Here, option bytes must be specified for use in stopping WDT or a low-speed internal oscillator and for using the I2C bus with the X1 and X2 pins as ports. To do this, an assembly language definition file such as the following must be created, which then must be linked.
    @@OPTB  CSEG    AT  0080H
    DB  10011100b
    DB  11111111b
    END


(1) Master device's transmission processing (u8 write_i2c_block (u8 12c_adr, u8 reg_adr, u8 *data, u8 size))
This function writes to a 256-byte EEPROM via the I2C bus.
It writes the number of bytes specified by the fourth argument of the buffer contents specified by the third argument to addresses starting at the internal address specified by the second argument in the slave device specified by the first argument.
The assembly language code's __put_i2c2 function is used for transmit processing in byte units, and if an error occurs the bus is released and an error corresponding to the status where an error occurred is returned.
If there are no errors during this processing, the bus is released and no errors are returned. The processing steps are listed below.

  • First, whether the I2C bus has been released is checked, and the bus is released if necessary.
  • If the bus cannot be released, an error is returned.
  • A start condition is issued and bus access starts.
  • An EEPROM address is specified and is then sent in the transmit direction.
  • Next, the address to be written within the EEPROM is transmitted.
  • Subsequently, the specified number of data bytes is transmitted.
  • When transmission is completed, a stop condition is issued. (EEPROM detects the stop condition and writes the transmitted data to an internal cell.)
u8 write_i2c_block(u8 i2c_adr, u8 reg_adr, u8 *data, u8 size)
{
    i2c_adr &= 0xfe;
    if (!_free_i2c_bus())         // Release bus
        return  _set_i2c_error(I2C_BUS_NOT_FREE);  // If bus is not released, 
                                                   // set an error flag and return.
    _I2C_STR();                   // Issue start condition 

    if (!_put_i2c2(i2c_adr))      // Transmit I2C address 
        return  _set_i2c_error(I2C_ADDR_NACK); // If there is no response from
                                               // the target device, 
                                               // set an error flag and return.
    if (!_put_i2c2(reg_adr))      // Transmit register address
        return  _set_i2c_error(I2C_REG_ADDR_NACK); // If a NACK occurs for 
                                                   // the sub-address, 
                                                   // set an error flag and return.

    do{
        if (!_put_i2c2(*data++))  // Transmit data
            return  _set_i2c_error(I2C_DATA_NACK); // NACK occurred when 
                                                   // transmitting data
    }while(--size);
    _I2C_STPR();                  // Release bus after a normal end
    return  _clear_i2c_error();   // Clear error flag and return 
}

(2) Master device's reception processing (u8 read_i2c_block (u8 i2c_adr, u8 reg_adr, u8 *data, u8 size)
Using the I2C bus, data is read from a 256-byte EEPROM.
The number of bytes specified by the fourth argument is read to the buffer specified by the third argument from the addresses starting at the internal address specified by the second argument in the slave device specified by the first argument.
The assembly language code's __put_i2c2 function is used for transmit processing for the slave address or slave internal address, and if an error occurs the bus is released and an error corresponding to the status where an error occurred is returned. If no errors occur during this processing, the bus is released and no errors are returned. The communication steps are as follows.

  • First, whether the I2C bus has been released is checked, and bus is released if necessary.
  • If the bus cannot be released, an error is returned.
  • A start condition is issued and bus access starts.
  • An EEPROM address is specified and is then sent in the transmit direction.
  • Next, the address to be read within the EEPROM is transmitted.
  • A restart is executed.
  • An EEPROM address is specified and is then sent in the receive direction.
  • Data is read from the specified address.
  • When reading, if the current data is not the last data, acke is set to send an ACK response and the assembler's __get_i2c function is called.
  • For the last data, acke is cleared and a NACK response is set, then the __get_i2c function is called.
u8 read_i2c_block(u8 i2c_adr, u8 reg_adr, u8 *data, u8 size)
{
    u8  d;
    i2c_adr &= 0xfe; 
    if (!_free_i2c_bus())
        return  _set_i2c_error(I2C_BUS_NOT_FREE);

    _I2C_STR();                     // Issue start condition 

    if (!_put_i2c2(i2c_adr))        // Transmit I2C address 
        return  _set_i2c_error(I2C_ADDR_NACK); // If there is no response from 
                                               // the target device, 
                                               // set an error flag and return.
    if (!_put_i2c2(reg_adr))        // Transmit register address
        return  _set_i2c_error(I2C_REG_ADDR_NACK); // If a NACK occurs for 
                                                   // the sub-address, 
                                                   // set an error flag and return.
    _I2C_STR();                     // Restarts

    if (!_put_i2c2(i2c_adr | 0x01)) // Transmit I2C address (READ) 
        return  _set_i2c_error(I2C_RADDR_NACK);  // If there is no response from
                                                 // the target device, 
                                                 // set an error flag and return.

    _ACK_EN();              // ACK is enabled
    do{
        if (size == 1)      // Last byte?
            _ACK_DS();      // If it is the last byte received 
                            // from the slave device, a NACK is set.
        d = _get_i2c();     // Read data 
        *data++ = d;        // Set read data to buffer
    }while(--size);

    _I2C_STPR();            // Release bus

    return  _clear_i2c_error(); // Clear error flag and return
}

(3) EEPROM access example
In this use example, 32 bytes are set up for reception and 16 bytes are set up for transmission, and assembly language programs (15) and (16) are used, along with "(1) Master device's transmission processing" and "(2) Master device's reception processing" from above.

u8      eep_read_buf[32];   // read buffer
u8      eep_write_buf[16];  // write buffer
u8      result;             // for result flag

#define     ADR_EEP     0xa0    // slave address
//              1010xxxyB
//              ||||||||
//              |||||||+--  R/W#
//              |||||++---  P1/P0 (fixed 00)
//              ||||+-----  A2 (fixed 0)
//              ++++------  I2C Addr (fix)
  • An assembly code function is used to write 16 bytes of data from the buffer to addresses 00 to 0F in EEPROM.
    result = _write_i2c_block(ADR_EEP, 0x00, eep_write_buf, 16);
  • An assembly code function is used to write the data at addresses 00 to 1F in EEPROM to a buffer.
    result = _read_i2c_block(ADR_EEP, 0x00, eep_read_buf, 32);
  • A C language function is used to write 16 bytes of data from the buffer to addresses 00 to 0F in EEPROM.
    result = write_i2c_block(ADR_EEP, 0x00, eep_write_buf, 16);
  • A C language function is used to write the data at addresses 00 to 1F in EEPROM to a buffer.
    result = read_i2c_block(ADR_EEP, 0x00, eep_read_buf, 32);

Suitable Products