Links to all my PIC tips, tricks and 'mini-project' notes
Whilst the mid-range PIC's can tackle many complex and otherwise almost impossible applications with ease, the challenge is to minimise cost by using the cheapest baseline PIC 'whenever possible'. Baseline PIC's can be had for less than 50p each = I purchased many 16F5x chips for between 40 and 50p each (mainly from CPC as 'remaindered' stock in their 'Bargain bin' section).
The even cheaper to use 12F675 (it has an internal OSC) can be found for as little as 20p (in Qty 10pcs, eBay), as can many other PIC's for less than £1 each. These PIC's are so cheap that you will soon start using them 'for everything' (especially as the PIC can often be used in place of a higher cost 'single function' digital chip - such as divider, ADC, PWM generator etc.) !
Buying the PIC in a 'TSOP' package is (sometimes) cheaper than the DIL/DIP package version = and whilst this costs you 10-20p extra for a mini-PCB TSOP-DIP 'converter', if you use a 'bigger' PCB than the PIC TSOP really needs you can mount other devices (resistors, caps, even osc. crystals) on the same board - and make use of the extra 'pin holes' to wire this up to the rest of your circuit
Below is a mix of programming tips and tricks, common circuit tricks and all the 'mini-projects' I've used the PIC for
I hope these details proves as useful to you as it does to me !
Below, click on the '+' to expand 'in place' (includes diagrams/images) or click the title URL (to view/download the text only version).
(+) 0004 Multi byte ADD - (24bit)
(+) 0005 new PIC 33 instruction set - (macros)
(+) 0006 Binary multiply methods
(+) 0007 8x8 - (multiply)
(+) 0008 8x16 - (multiply)
(+) 0011 Bi color LED driving
(+) 0012 One pin dual LED and button detect
(+) 0013 Input only multi button detect
(+) 001a One pin controls motor Fwd off Reverse
(+) 001c One pin controls 3 relays
(+) 0020 I2C bit banging
(-) 0021 I2C code
; Version 1.0, 2017-08-17 12:50 ; ; I2C Transmit and Recieve, using base-line instruction set (PIC-33), Steve's Macro instruction set ; This version uses 2 pins only , so uses TRIS to isolate the drive from I2C bus and thus not very fast ; ; [I use a faster approach, no TRIS but needs 4 pins (2 out, 2 in) ... ; ... plus 2x Shockley diodes to isolate the 'Hi' drive (from the output only pins) from the I2C bus ] ; ; Quick overview of I2C operation: ; 1) To start a transfer, Master ensures CLK is HI, then pulls Data Lo, then CLK Lo ; 2) To send data, Master sets up Data, then sends CLK Hi pulse (Master has to check CLK goes Hi as slave can hold it Lo to force wait) ; 3) To fetch data, Master sets CLK Hi (checks CLK Hi), samples data, sets CLK LO ; 4) After 8 bits, Master checks the Slave Ack bit with a Clk Hi (+wait for hI). Ack = data Lo = OK, Nack = Hi = fail. ; 5) After Ack, both Master and Slave can hold CLK Lo until it's ready for next byte ; 6) To release the bus, Master removes all drive (allows both CLK and Data to go Hi) ; 7) To indicate busy, Slave waits until Master has recieved Ack (Clk Lo) then holds CLK Lo until it's ready ; ; See I2C manual:- http://www.nxp.com/docs/en/user-guide/UM10204.pdf ; ; ; NB. This code assumes this PIC is the (only) Master (i.e. I2C in single Master configuration) ; ; ; I2CPORT = the i/o PORT (address) containing the 3 bits used for I2C ; bSCL = Clk (i/o pin) bit number ; bSDA = Data (i/o pin) bit number ; rI2Ctris contains a copy of the tri-state settings for I2CPORT ; (this I2C subrouine will only modify the bSCL and bSDA bits) ; rData is the data byte to be sent (or recieved) ; rTemp is used as the bit counter ; ; CALL I2Cstart to grab the I2C bus and transmit a data byte from rData (this selects the Slave for TxD or RxD) ; CALL I2CTxD to transmit a date byte (start must be called first) ; CALL I2CRxD to recieve a byte (start must be called first) ; CALL I2Cstop to release the bus (must be called after TxD or RxD) ; ; On Return, Acc can be used for 'Skip if no Error' (ADD Acc,PCL) as follows :- ; If Error (no Ack recieved), Acc == 0 and ADD Acc,PCL will execute the next instruction (typically a JMP to error handler) ; If no Error, Ack recieved, Acc = 1 and ADD Acc,PCL will skip the next instruction ; NOTE. To work, the CALL has to be from LOW half of 512 address bank (ADD PCL clears bit9), and can't be on FE or FF (page boundary) ; ; WARNING - all subroutines will 'lock up' if the sCLK line is being held Lo (i.e. whilst Slave forces bit/bus wait) ; ; I2Cstart ;CALL to start the I2C and send a (slave select) byte. ; NOTE. we always start with a 'select slave', so at least one byte TxD always comes before RxD ; ; Don't assume this routine has been used befor, so start by making sure the clk and data drive bits are disabled (tri-state) bSET rI2Ctris,bSCL ;Set bits in tri-state 'copy' reg. (Hi = set tristate = disable output, set as input) bSET rI2Ctris,bSDA COPY rI2Ctris,Acc ;get the tri-state copy settings .. TRIS I2CPORT ; and make sure I2C lines are tri-state (will be pulled hi by the I2C 200R bus pull-ups)) ; Now make sure the clk and data bits will be driven Lo when TRIS ebables them ; WARNING bSET/bCLR one bit at a time will READ ALL THE PINS, then set ONLY that one, then write them to the o/p latch ; any pin that is an input will get it's current state written to the o/p latch .. ; Since bith SCL and SDA are tri-state, a bCLR on one will always set the other Hi ! ; so setting 2nd pin Lo can't be done using bCLR direct bCLR I2CPORT,bSCL,Acc ;get the pin state to Acc (with clk set lo) bCLR Acc,bSDA ;force Data 0 COPY Acc,I2CPORT ;copy settings to the port o/p latch (both clk and data 0) ; ; OK, now grab the bus .. first make sure CLK is Hi SKIPset I2CPORT,bSCL JMP $-1 ; Yep - set data Lo COPY rI2Ctris,Acc bCLR Acc,bSDA ;rtris with data Lo to Acc TRIS I2CPORT ; .. and to the I2C bus ; OK, now to select the Bus, with data Lo, set CLK lO .. then wait a bit bCLR Acc,bSCL ;set for o/p clk Lo TRIS I2CPORT ;clk Lo (and data Lo) to the I2C bus ; OK thats the start, now send data .. ; I2CTxD ; Entry point for transmit (another) byte (I2Cstart must have been called first to select the slave ..) LOAD 0x08 ;Set the bit count (DECFSZ will dec and skip if zero to end the loop) COPY Acc, rTemp ; (re)load the Acc with tris settings COPY rI2Ctris,Acc bCLR Acc,bSCL ;default = both clk and data Hi, need clk Lo ; ; Bit transmit will loop back here with clk Lo ready to set next data bit I2CdataTx ; OK, now we can set a (next) data bit .. set data, Clk Hi loop for next ROTR rData ;first (next) bit = b0 to Cy bCLR Acc,bSDA ;assume it's zero, set tris to o/p 0 SKIP nCY ;skip if bit is 0 bSET Acc,bSDA ;need hi, tris to input (i.e. set SDA bit Hi) TRIS I2CPORT ;data to the I2C bus ; release the CLK bSET Acc,bSCL ;prepare to release the clk (hi) TRIS I2CPORT ;release the Clk (data unchanged) SKIPset I2CPORT,bSCL ;check CLK Hi .. JMP $-1 ; .. Slave can hold clk Lo to force a wait ; Slace has data, pull clk Lo before changing to next bit bCLR Acc,bSCL ;enable output (Clk Lo) TRIS I2CPORT ;clk lo to the I2C bus ; any more to do ? DECFSZ rTemp ;dec count, skip if zero JMP I2CdataTx ; ; all done, release the data line, get the Ack bSET Acc,bSDA ;tri-state data bit Hi TRIS I2CPORT ;release data line ; now clk in the slave Ack bit (Hi = failed, Lo = ok) bSET Acc,bSCL ;clk hi ; this is where RxD arrives to get the slave Ack I2CslaveAck TRIS I2CPORT ; .. to the I2C bus bCLR Acc,bSCL ;prepare exit with clk Lo ; get slave Ack SKIPset I2CPORT,bSCL ; remember = slave can hold CLK Lo to force a wait JMP $-1 ; OK ack status is valid, sample data pin SKIPset I2CPORT,bSDA ;input data Lo is OK, hi is error JMP $+3 TRIS I2CPORT ; Clk lo to I2C bus RETURN 0x01 ;slave sent 0, all OK (return 1 for ADD Acc,PCL skip error jump if OK) TRIS I2CPORT ; Clk Lo to I2C bus RETURN 0 ;return with Acc= 0 = error (return 0 for ADD Acc,PCL, execute next inst = jump to error) ; ; ; ; I2CRxD ;reviece a byte (I2Cstart must have been called first to select the slave ..) ; on entry, I2C bus clk is Lo (rI2Ctris has both bits Hi) COPY rI2Ctris,Acc ;get the tri-state copy settings LOAD rDate 0x08 ;set the bit count ; the sequence is much the same as data transmit, expect we wait for clk Hi then sample bSDA I2CRxDwait TRIS I2CPORT ;release the clk SKIPclr I2CPORT,bSCL ;wait for CLk hi JMP $-1 bCLR Cy ;assume data bit is 0 SKIPclr I2CPORT,bSDA ;skip if is 0 bSET Cy ROTL rData ;get the bit bCLR Acc,bSCL ;say we got it (clk lo) TRIS I2CPORT bSET Acc,bSCL ;prepare to release the clk DECFSZ rTemp ;dec count, skip if zero JMP I2CRxDwait ; all done, exit via get slave Ack JMP I2CslaveAck ; ; ; I2Cstop ;release the I2C bus ; on entry, I2C bus clk is Lo (rI2Ctris has both bits Hi) ; to release, data Lo, then Clk Hi, then Data Hi COPY rI2Ctris,Acc ;get tris copy for this port bCLR Acc,bSDA ;set for tris data lo bCLR Acc,bSCL ;now clk Lo TRIS I2CPORT ;set both clk data lo bSET Acc,bSCL ; clk to Hi TRIS I2CPORT ;release the clk SKIPset I2CPORT,bSCL ;wait for slave to relese clk JMP $-1 COPY rI2Ctris,Acc ;both data and clk hi TRIS I2CPORT ;release the bus RETURN 0x01 ;return with all OK ; ; ends
This note last modified: 17th Aug 2017 16:21.