UNIO
This module provides support for serial EEPROMs and the Microchip UNI/O one-wire bus.
UNIO Module
//----------------------------------------------------------------------------- // name : unio.bas // author : Jerry Messina // date : 12/1/2014 // version : 1.0.0 // notes : serial EEPROM support for the Microchip UNI/O one-wire protocol //----------------------------------------------------------------------------- module unio // // adapted from the Microchip app note AN1191 // "Using C18 and a Timer to Interface PIC18 MCUs with UNIO Bus-Compatible Serial EEPROMs" // // resources required: // - one IO pin with ext 20K pull-up to ensure bus idle during power-up (#option SCIO_PIN) // - one timer and one CCP compare module (hard coded to use TMR1/CCP1) // - optional IO for debugging (#option UNIO_TRIGGER/#option TRIGIO_PIN) // // note: the UNI/O bus is very timing sensitive, and for reliable operation you // will need to use these routines with interrupts disabled // //----------------------------------------------------------------------------- // IO pin definition options //----------------------------------------------------------------------------- #option SCIO_PIN = PORTB.2 #option SCIO_PIN_TRIS = GetTRIS(SCIO_PIN) // debug trigger pin enable // if enabled, this pin is asserted high during the input pin sample point // this can be used to measure/verify timing #option UNIO_TRIGGER = false #option TRIGIO_PIN = PORTB.1 #if (UNIO_TRIGGER) #warning "UNIO trigger IO output enabled" #endif // IO pin definitions (from #options) dim SCIO as SCIO_PIN.SCIO_PIN@, // serial clock, input/output pin SCIOTRIS as SCIO_PIN_TRIS.SCIO_PIN@ // SCIO direction bit // debug trigger pin (enabled by #option UNIO_TRIGGER = true) dim TRIGIO as TRIGIO_PIN.TRIGIO_PIN@ //----------------------------------------------------------------------------- // bus timings //----------------------------------------------------------------------------- // if using the IDE Code Explorer to view this module directly, system _clock // may not be visible here so the constants will display with different values // than are actually used when compiling. // change the '#define _SYSCLOCK' manually to view them by setting it to the // 'clock' value in use, but be sure to change it back to '_clock' when done #define _SYSCLOCK = _clock // insert 'clock=' setting here const SYSCLOCK = _SYSCLOCK // make sure SYSCLOCK is set correctly to compile #if (_SYSCLOCK <> _clock) #error "set '_SYSCLOCK = _clock' to build" #endif // bus freq option #option UNIO_BUSFREQ = 25000 // set UNIO bus frequency (10KHz-100KHz) #if (UNIO_BUSFREQ < 10000) or (UNIO_BUSFREQ > 100000) #warning "UNIO_BUSFREQ should be 10KHz-100KHz" #endif #define _FCY = _SYSCLOCK*1000000/4 // pic18 instruction cycle (in Hz) // note: the max usable UNIO bus freq is dependant on the system clock // some examples: // clock= 8Mhz: 26000 // clock=16MHz: 52000 // clock=32MHz: 100000 (usable to 104KHz) // clock=64MHz: 100000 (usable to 200KHz) // basic Timer period calculations // calc number of instruction cycles in 1/2 period #variable _HALF_PERIOD = (_FCY/(2*UNIO_BUSFREQ)) // make sure that _HALF_PERIOD is even so that _QUARTER_PERIOD is accurate #if ((_HALF_PERIOD and 1) = 1) #variable _HALF_PERIOD = _HALF_PERIOD + 1 #endif #define _QUARTER_PERIOD = (_HALF_PERIOD/2) // check to see if we need to use full 16-bit timer operation (uses more code) #if (_HALF_PERIOD > 255) #warning "UNIO enabling 16-bit timer code" #option UNIO_USE_16BIT_TIMER = true type timer_val_t = word #else #option UNIO_USE_16BIT_TIMER = false type timer_val_t = byte #endif // create the timer constants from the '#defines' const HALF_PERIOD as word = _HALF_PERIOD const QUARTER_PERIOD as word = _QUARTER_PERIOD const THDR_COUNT as word = 5/(1.0/_SYSCLOCK*4) // 5us min // unio bus frequencies range from 10KHz (100us) to 100KHz (10us) per bit // check if there's enough time to execute the code. currently there's about // 18 instructions or so in the InputByte/InputBit loop. if _QUARTER_PERIOD // approaches this, then things start to fail since there's just too few // instruction cycles vs the 1/4 bit timing req'd for the InputBit routine #if (_QUARTER_PERIOD < 20) then #error "timer period smaller than code execution cycles. reduce UNIO_BUSFREQ" #endif //----------------------------------------------------------------------------- // UNIO constant definitions //----------------------------------------------------------------------------- const STARTHDR = $55, // START HEADER sequence MAK = 1, // master acknowledge (MAK) NOMAK = 0, // master not acknowledge (NoMAK) SAK = 1, // slave acknowledge (SAK) NOSAK = 0 // slave not acknowledge (NoSAK) // UNIO flags structure flags_t b as byte sendMAK as b.bits(0) // master send 1=MAK, 0=NoMAK checkSAK as b.bits(1) // check slave ack SAK1 as b.bits(3) // SAK sample #1 (at T=1/4 bit) SAK2 as b.bits(4) // SAK sample #2 (at T=3/4 bit) end structure dim dataOut as byte, // 8-bit data output buffer dataIn as byte, // 8-bit data input buffer flags as flags_t // transfer status/control flags //----------------------------------------------------------------------------- // eeprom definitions //----------------------------------------------------------------------------- // 11XXXX0 device code 0000 // 11XXXX1 device code 0001 public const DEV_CODE = %0000 public const DEVICE_ADDR = $A0 + DEV_CODE, // define DEVICE_ADDR (eeprom family code=0xA0) PAGESIZE = 16 // 16-byte page size for 11XXXXX // 11AA02E48/11AA02E64 devices are 256x8 (2K bit) eeproms with a // pre-programmed globally unique 48-bit or 64-bit Node Address (EUI48/EUI64) public const EUI48_ADDR = $FA, // EUI-48 addr location (11AA02E48) EUI48_SIZE = 6, // EUI-48 size, in bytes EUI64_ADDR = $F8, // EUI-64 addr location (11AA02E64) EUI64_SIZE = 8 // EUI-64 size, in bytes // eeprom command definitions const WREN = %10010110, // WREN command WRDI = %10010001, // WRDI command WRITE = %01101100, // WRITE command READ = %00000011, // READ command WRSR = %01101110, // WRSR command RDSR = %00000101, // RDSR command ERAL = %01101101, // ERAL command SETAL = %01100111 // SETAL command // eeprom status reg structure statusreg_t b as byte WIP as b.bits(0) WEL as b.bits(1) BP0 as b.bits(2) BP1 as b.bits(3) end structure //----------------------------------------------------------------------------- // timer hardware resources //----------------------------------------------------------------------------- // to support the full range of bus freq and sys clock settings, we use a 16-bit // timer in special event trigger mode and a CCP compare module to give a // programmable period register // // the pic18 app note only uses the low byte of the timer and CCP, always setting // the high byte = 0 ... // TMR1H = 0 // TMR1L += (HALF_PERIOD/2+1) // this makes for faster updates to the timer, so it produces more accurate results // since the timer continues to count, the longer the timer read-add-write sequence, // the more the error. however, this limits the system clock and SCIO BUSFREQ settings. // UNIO is timing sensitive so the slower the uC, the slower the max usable BUSFREQ const CCP1IF = 2, // register bit numbers TMR1ON = 0 dim TMRCON as T1CON, TIMER_ON as T1CON.bits(TMR1ON), // bit 0: TMR1ON CCPCON as CCP1CON, PERIOD_IF as PIR1.bits(CCP1IF) // bit 2: CCP1IF // timer/CCP setup (NOTE: this varies with device, so be sure to check the bits) // prescaler 1:1, internal clock (Fosc/4), 16-bit mode enabled (RD16) const TCON_CONFIG as byte = %00000010 // compare mode: trigger special event, reset timer, CCPx match (CCPxIF bit is set) const CCPCON_CONFIG as byte = %00001011 // write 16-bit timer with the value 'wval' macro WRITE_TIMER(wval) #if (UNIO_USE_16BIT_TIMER) TMR1H = (wval >> 8) // high byte not actually updated yet #else TMR1H = 0 #endif TMR1L = byte(wval) // loads low and high bytes end macro // reads 16-bit timer, stores it in 'result' macro READ_TIMER(result) #if (UNIO_USE_16BIT_TIMER) result.byte0 = TMR1L // read low byte (latches the high byte) result.byte1 = TMR1H #else result = TMR1L // read low byte (latches the high byte) #endif end macro // add a value to the current timer setting dim PROD as PRODL.asword macro ADD_TIMER(wval) #if (UNIO_USE_16BIT_TIMER) PRODL = TMR1L // read TMR low byte (latches the high byte) PRODH = TMR1H PROD = PROD + word(wval) TMR1H = PRODH // high byte not actually updated yet TMR1L = PRODL // loads TMR low and high bytes #else TMR1H = 0 // high byte not actually updated yet TMR1L = TMR1L + byte(wval) // loads TMR low and high bytes #endif end macro // load ccp period register macro LOAD_PERIOD(w) #if (UNIO_USE_16BIT_TIMER) CCPR1H = byte(w >> 8) #else CCPR1H = 0 #endif CCPR1L = byte(w and $FF) end macro // wait for ccp period inline sub WAIT_FOR_PERIOD_IF() while (PERIOD_IF = 0) // wait until CCP1IF flag is set end while PERIOD_IF = 0 // clear CCP1IF flag end sub // debug trigger pin macro TRIGGER(x) #if (UNIO_TRIGGER) TRIGIO = x #endif end macro //-------------------------------------------------------------------- // private sub Tss() // Hold SCIO high for Tss start header setup time period (10 us) //-------------------------------------------------------------------- private sub Tss() delayus(10) // delay for Tss time period end sub //-------------------------------------------------------------------- // private sub Standby() // Waits until end of current bit period has been reached, ensures // SCIO is high for bus idle, and disables the Timer //-------------------------------------------------------------------- private sub Standby() WAIT_FOR_PERIOD_IF() TIMER_ON = 0 // stop Timer1 SCIO = 1 // ensure SCIO is high for bus idle SCIOTRIS = 0 // ensure SCIO is outputting end sub //-------------------------------------------------------------------- // private sub InputBit() // Inputs and Manchester-decodes a bit of data from SCIO and stores it // in the LSb of dataIn. For detecting SAK/NoSAK, the SCIO pin is sampled // twice... once at T=1/4 (SAK1) and again at T=3/4 (SAK2). This is done // so that we can detect a Machester '1' vs an idle line. For a regular // dataIn bit, we just sample SCIO once at T=3/4 // // note: the original app note code changes the sampling points by reading // and adding values to the timer while its running. writing to the timer // this way means you have to account for the code execution time, // otherwise you get extra cycles and time offsets. instead, here we // leave the timer alone and change the period register to detect the // different sampling points. this is a bit more forgiving, but you still // have to make sure that things can execute fast enough to meet the // code deadlines //-------------------------------------------------------------------- private sub InputBit() dim t as timer_val_t // set CCP to 1/4th period so we're offset by 1/4th bit period LOAD_PERIOD(QUARTER_PERIOD) // set default SAK bits... they're only valid after an Acknowledge flags.SAK1 = 1 flags.SAK2 = 1 // wait until flag is set, indicating we are 1/4th into bit period WAIT_FOR_PERIOD_IF() // sample SCIO pin (we do this twice for Manchester decoding) if (SCIO = 0) then flags.SAK1 = 0 // if 0, clear 1st SAK bit endif // wait until flag is set again so we are 1/4 + 1/4 = 1/2 into bit period WAIT_FOR_PERIOD_IF() // now that we're at the halfway-point, put period reg back to 1/2 period LOAD_PERIOD(HALF_PERIOD) // wait for timer to indicate we are 3/4th into period so we can read SCIO repeat READ_TIMER(t) until (t >= QUARTER_PERIOD) // read and update data dataIn.bits(0) = 1 // sssume data bit will be a 1 TRIGGER(1) // assert debug trigger pin (if enabled) if (SCIO = 0) then // sample SCIO pin dataIn.bits(0) = 0 // if 0, clear data bit flags.SAK2 = 0 // and 2nd SAK bit (for Manchester decode) endif TRIGGER(0) // release debug trigger end sub //-------------------------------------------------------------------- // private function InputByte() as byte // Inputs and Manchester-decodes from SCIO a byte of data, stores it // in dataIn, and returns the byte //-------------------------------------------------------------------- private function InputByte() as byte dim count as byte // loop through byte count = 8 repeat WAIT_FOR_PERIOD_IF() // wait until CCP1IF flag is set dataIn = dataIn << 1 // shift dataIn left to prepare for next bit InputBit() // get input bit count = count - 1 until (count = 0) result = dataIn end function //-------------------------------------------------------------------- // private sub OutputHalfBit() // Waits until Timer 1 rolls over, then outputs the next half of the // current bit period. The value used is specified by the MSb of dataOut //-------------------------------------------------------------------- private sub OutputHalfBit() WAIT_FOR_PERIOD_IF() // wait until CCP1IF flag is set if (dataOut.bits(7) = 1) then // check if dataOut MSb is 1 SCIO = 0 // if 1, set SCIO low else SCIO = 1 // if 0, set SCIO high endif dataOut = not(dataOut) // invert dataOut for next half bit end sub //-------------------------------------------------------------------- // private sub AckSequence() // Performs Acknowledge sequence, including MAK/NoMAK as specified by // flags.sendMAK, and SAK //-------------------------------------------------------------------- private sub AckSequence() // prepare for sending MAK bit dataOut.bits(7) = 1 // assume MAK will be sent if (flags.sendMAK = 0) then // check if MAK is to be sent dataOut.bits(7) = 0 // if not sending MAK, clear data bit endif // send MAK/NoMAK bit SCIOTRIS = 0 // ensure SCIO is outputting OutputHalfBit() // output first half of bit OutputHalfBit() // output second half of bit // release SCIO at end of bit period WAIT_FOR_PERIOD_IF() // wait until CCP1IF flag is set SCIOTRIS = 1 // set SCIO to be an input // detect Acknowledge status (whether it's used or not) // this is different from the app note which only checks when // checkSAK = 1, but then it has to adjust timing otherwise. // it's simpler to read it and just ignore SAK if you don't care InputBit() end sub //-------------------------------------------------------------------- // private sub OutputByte(b as byte) // Manchester-encodes & outputs the byte value 'b' to SCIO //-------------------------------------------------------------------- private sub OutputByte(b as byte) dim count as byte // set output data byte (needed by OutputHalfBit routine) dataOut = b // loop through byte sending msb to lsb count = 8 SCIOTRIS = 0 // ensure SCIO is outputting repeat OutputHalfBit() // output first half of bit OutputHalfBit() // output second half of bit dataOut = dataOut << 1 // shift data left 1 bit count = count - 1 until (count = 0) AckSequence() // perform Acknowledge Sequence end sub //-------------------------------------------------------------------- // private sub StartHeader() // Hold SCIO low for Thdr time period (5us min) and output Start Header // ('01010101') //-------------------------------------------------------------------- private sub StartHeader() SCIO = 0 // set SCIO low SCIOTRIS = 0 // ensure SCIO is driving PERIOD_IF = 0 // clear CCP1IF flag // load TMR so that the CCP generates a THDR_COUNT time period WRITE_TIMER(HALF_PERIOD-THDR_COUNT) // load TMR1 to count THDR flags.sendMAK = 1 // send MAK bit flags.checkSAK = 0 // do not check for SAK bit TIMER_ON = 1 // enable Timer1 OutputByte(STARTHDR) // output Start Header value flags.checkSAK = 1 // check for SAK bit end sub //-------------------------------------------------------------------- // private sub StandbyPulse() // Before communicating with a new device, a standby pulse must be // performed. This pulse signals to all slave devices on the bus to // enter Standby mode, awaiting a new command to begin. The standby // pulse can also be used to prematurely terminate a command. // The standby pulse consists of holding SCIO high for a minimum of // Tstby (600us). After this has been performed, the slave devices // will be ready to receive a command //-------------------------------------------------------------------- public sub StandbyPulse() SCIO = 1 // ensure SCIO is high SCIOTRIS = 0 // ensure SCIO is driving delayus(600) // delay for Tstby time end sub //-------------------------------------------------------------------- // private sub SendCmd(b as byte) // common initial sequence for sending commands //-------------------------------------------------------------------- private sub SendCmd(b as byte) Tss() // observe Tss time StartHeader() // output Start Header OutputByte(DEVICE_ADDR) // send DEVICE_ADDR OutputByte(b) // send command byte end sub //-------------------------------------------------------------------- // public function DeviceDetect(addr as byte = DEVICE_ADDR) as boolean // perform an address poll of device at 'addr' // returns true if device acks, false otherwise //-------------------------------------------------------------------- public function DeviceDetect(addr as byte = DEVICE_ADDR) as boolean StandbyPulse() // generate a standby pulse Tss() // observe Tss time StartHeader() // output Start Header flags.sendMAK = NOMAK // addr byte sent w/NoMAK OutputByte(addr) // send DEVICE_ADDR result = false // assume slave not present if ((flags.SAK1 = 0) and (flags.SAK2 = 1)) then result = true // device ACKed endif flags.sendMAK = MAK // put flags back to normal // a standby pulse here is a good idea in case of no slave ack StandbyPulse() end function // //-------------------------------------------------------------------- // EEPROM specific routines //-------------------------------------------------------------------- // //-------------------------------------------------------------------- // public sub WIP_Poll() // This function performs WIP polling to determine the end of the current // write cycle. It does this by continuously executing a Read Status // Register operation until the WIP bit (bit 0 of the Status Register) // is read low //-------------------------------------------------------------------- public sub WIP_Poll() dim stat as statusreg_t SendCmd(RDSR) // send RDSR command stat = InputByte() // input status byte while (stat.WIP = 1) AckSequence() // perform Acknowledge Sequence stat = InputByte() // input status byte end while flags.sendMAK = 0 // send NoMAK AckSequence() // perform Acknowledge Sequence Standby() // gracefully enter Standby end sub //-------------------------------------------------------------------- // public sub WriteDisable() // This function performs a Write Disable instruction to clear the // WEL bit in the Status Register //-------------------------------------------------------------------- public sub WriteDisable() Tss() // observe Tss time StartHeader() // output Start Header OutputByte(DEVICE_ADDR) // send DEVICE_ADDR flags.sendMAK = 0 // send NoMAK OutputByte(WRDI) // send WRDI command Standby() // gracefully enter Standby end sub //-------------------------------------------------------------------- // public sub WriteEnable() // This function performs a Write Enable instruction to set the WEL // bit in the Status Register //-------------------------------------------------------------------- public sub WriteEnable() Tss() // observe Tss time StartHeader() // output Start Header OutputByte(DEVICE_ADDR) // send DEVICE_ADDR flags.sendMAK = 0 // send NoMAK OutputByte(WREN) // send WREN command Standby() // gracefully enter Standby end sub //-------------------------------------------------------------------- // public function ReadStatusReg() as statusreg_t // This function reads and returns the value of the Status Register //-------------------------------------------------------------------- public function ReadStatusReg() as statusreg_t SendCmd(RDSR) // send RDSR command result = InputByte() // read input byte flags.sendMAK = 0 // send NoMAK AckSequence() // perform Acknowledge Sequence Standby() // gracefully enter Standby end function //-------------------------------------------------------------------- // public sub WriteStatusReg(status as byte) // This function writes the byte value specified by 'status' to the // Status Register on the serial EEPROM //-------------------------------------------------------------------- public sub WriteStatusReg(status as byte) WriteEnable() // set Write Enable Latch SendCmd(WRSR) // send WRSR command flags.sendMAK = 0 // send NoMAK OutputByte(status) // output byte Standby() // gracefully enter Standby WIP_Poll() // perform WIP Polling end sub //-------------------------------------------------------------------- // public sub SeqRead(addr as word, byref buffer() as byte, count as byte) // This function reads 'count' bytes from the serial EEPROM starting at // eeprom location 'addr', and stores the bytes into 'buffer()' // Parameters: addr - EEPROM memory location at which to start // buffer()- dest buffer address // count - number of bytes to read //-------------------------------------------------------------------- public sub SeqRead(addr as word, byref buffer() as byte, count as byte) SendCmd(READ) // send READ command OutputByte(addr.bytes(1)) // send address MSB OutputByte(addr.bytes(0)) // send address LSB FSR2 = addressof(buffer) // set addr to buffer(0) POSTINC2 = InputByte() // input first byte // loop through remaining data to read (first byte already read) count = count - 1 while (count <> 0) AckSequence() // perform Acknowledge Sequence POSTINC2 = InputByte() // input next byte buffer(ix) count = count - 1 end while flags.sendMAK = 0 // send NoMAK AckSequence() // perform Acknowledge Sequence Standby() // gracefully enter Standby end sub //-------------------------------------------------------------------- // public sub PageWrite(addr as word, byref buffer() as byte, count as byte) // This function writes 'count' number of bytes from the 'buffer()' // array to the serial EEPROM, starting at the location 'addr' // Parameters: addr - EEPROM memory location at which to start // buffer()- src buffer address // count - number of bytes to write (up to PAGESIZE max) // **NOTE** // This function does NOT test for page boundaries, so it's up to the // caller to ensure that page boundaries are not crossed. Otherwise, // the data will wrap back to the beginning of the page //-------------------------------------------------------------------- public sub PageWrite(addr as word, byref buffer() as byte, count as byte=PAGESIZE) WriteEnable() // set Write Enable Latch SendCmd(WRITE) // send WRITE command OutputByte(addr.bytes(1)) // send address MSB OutputByte(addr.bytes(0)) // send address LSB // loop through data to write (except last byte) FSR2 = addressof(buffer) // point to buffer(0) count = count - 1 while (count <> 0) OutputByte(POSTINC2) // output next byte buffer(ix) count = count - 1 end while flags.sendMAK = 0 // send NoMAK OutputByte(POSTINC2) // load final byte Standby() // gracefully enter Standby WIP_Poll() // perform WIP Polling end sub //-------------------------------------------------------------------- // public sub ByteWrite(addr as word, b as byte) // This function writes the byte value 'b' to the serial EEPROM // location specified by 'addr' //-------------------------------------------------------------------- public sub ByteWrite(addr as word, b as byte) WriteEnable() // set Write Enable Latch SendCmd(WRITE) // send WRITE command OutputByte(addr.bytes(1)) // send address MSB OutputByte(addr.bytes(0)) // send address LSB flags.sendMAK = 0 // send NoMAK OutputByte(b) // send byte b Standby() // gracefully enter Standby WIP_Poll() // perform WIP Polling end sub //-------------------------------------------------------------------- // public function ByteRead(addr as word) as byte // This function reads a byte from the serial EEPROM location specified // by 'addr', and returns the byte value read //-------------------------------------------------------------------- public function ByteRead(addr as word) as byte SendCmd(READ) // send READ command OutputByte(addr.bytes(1)) // send address MSB OutputByte(addr.bytes(0)) // send address LSB result = InputByte() // input byte flags.sendMAK = 0 // send NoMAK AckSequence() // perform Acknowledge Sequence Standby() // gracefully enter Standby end function //-------------------------------------------------------------------- // public inline sub ReadEUI48(byref buffer() as byte) // public inline sub ReadEUI64(byref buffer() as byte) // These routines read the pre-programmed EUI48/EUI64 bytes from the // EEPROM into the dest 'buffer' array //-------------------------------------------------------------------- // 11AA02E48 device public inline sub ReadEUI48(byref buffer() as byte) SeqRead(EUI48_ADDR, buffer, 6) end sub // 11AA02E64 device public inline sub ReadEUI64(byref buffer() as byte) SeqRead(EUI64_ADDR, buffer, 8) end sub //-------------------------------------------------------------------- // public sub Init() // init SCIO hardware resources //-------------------------------------------------------------------- public sub Init() // initialize I/O // note: the caller must make sure the SCIO pin is in digital mode // after a POR/BOR event occurs, a low to high transition on SCIO must be // generated before proceeding with any communication SCIO = 0 // set SCIO to drive low SCIOTRIS = 0 // configure SCIO to output delayus(1) // delay to ensure minimum pulse width of 200 ns SCIO = 1 // bring SCIO high to release device from POR // init debug trigger pin (if enabled) #if (UNIO_TRIGGER) low(TRIGIO) #endif // initialize Timer1 TMRCON = TCON_CONFIG // configure Timer1, 1:1 prescalar, 16-bit LOAD_PERIOD(HALF_PERIOD) // load HALF_PERIOD into CCPR1L WRITE_TIMER($0000) // clear TMR1 // initialize CCP1 CCPCON = CCPCON_CONFIG // configure special event trigger (reset timer) StandbyPulse() // generate Standby Pulse end sub //-------------------------------------------------------------------- // public function check_bit_timing(b as byte) as byte // test function useful for measuring sample points/timing // it performs a start, write, and read sequence // TRIGGER pulses must occur within SCIO bit periods //-------------------------------------------------------------------- #if (UNIO_TRIGGER) public function check_bit_timing(b as byte) as byte Tss() // observe Tss time StartHeader() // output Start Header OutputByte(b) // send a byte result = InputByte() // input byte flags.sendMAK = 0 // send NoMAK AckSequence() // perform Acknowledge Sequence Standby() // gracefully enter Standby end function #endif // UNIO_TRIGGER end module
Sample Code
// UNI/O test program device = 18F25K22 clock = 16 include "utils.bas" // set hdw options #option SCIO_PIN = PORTB.2 #option UNIO_BUSFREQ = 10000 include "unio.bas" // eeprom page buffer (PAGESIZE is defined in unio module) public dim pageBuffer(PAGESIZE) as byte sub main() dim i as byte dim b as byte dim addr as word dim fail as boolean // set all digital pins SetAllDigital() // init UNIO hdw unio.init() // see if we can detect a device if (unio.devicedetect()) then fail = false // device acks else fail = true // things aren't working endif // read eeprom status register b = unio.ReadStatusReg() // check write/read a single byte to addr (page2) addr = 2*PAGESIZE + 1 // set address unio.ByteWrite(addr, $55) // write one byte of data b = unio.ByteRead(addr) // read one byte of data if (b <> $55) then // check write vs read fail = true endif // init our data array for i = 0 to bound(pageBuffer) pageBuffer(i) = i next // test page write/read functions addr = 2*PAGESIZE // set address unio.PageWrite(addr, pageBuffer, PAGESIZE) // write one page of data clear(pageBuffer) unio.SeqRead(addr, pageBuffer, PAGESIZE) // read one page of data // check reading EUI data clear(pageBuffer) unio.ReadEUI48(pageBuffer) end sub main() end program