External Links

Creative Science Centre


Rookie 2

** AS from 10 August, (BvSerail 17) these files have been broken into smaller files and can be included into a file, this page will be updated soon ***

  • rookie 1: This is for the PIC32MX150 or PIC32MX340 running firmware 2.0n
  • rookie 2: This is for the PIC32MX170 ro PIC32MX370 running firmware 2.3n it is backwards compatible with rookie 1 and so should be used for the tutorials which were originally designed for rookie 1
  • rookie 3: This should be used for new projects it is much smaller and leaner than rookie 2 but it will not run software designed for rookie 1

This applies to ByPic Version 2.3 onwards where constant arrays have been introduced. Mainly it effects the situation where ? was used, this now becomes *name().

<< back to Rookie 1

Quick link: GPIO, PPS, Tables, ADC, Timers, I2C, SPI, Interrupts, UART, MX3, Touch Screen, BV513, BV514, BV523, File

The ByPic language is extensible and this rookie code extends the language (gives it more functions) to make common tasks simpler. With this code there is no need to consult the data sheet or registers but it does make some assumptions. The code will also serve as a reference for more advanced functions.

Another advantage is that these functions are the same (within reason) across all boards and processors. For a list of the functions and what they do see the Library menu.

This text is a description of the rookie functions, the rookie firmware is in the download section



OUT     Sets pin to be an output
IN        Sets pin to be an input
LOW    Makes pin low '0'
HIGH    Makes pin high '1'
TOGGLE  Toggles pin
WOFF Weak pull up off
WPU Enable weak pull up
WPD Enable weak pull down (MX1 only)

NOTE: MX1 devices have PORTA and PORTB, MX3 devices have PORTB through PORTG

Set a pin on port to be an input or output, the upper case keywords IN or OUT are required
io_pinMode(port, pin, direction, weakPull)

Example: io_pinMode(PORTB, 11 ,OUT,WOFF) // sets RB11 to be an output

The weak pull up is ignored for output but it still needs to be there. The MX1 has weak pull up and weak pull down on all of its port pins but the MX3 devices only have weak pull ups and then only on certain pins.

RB0 - RB9 & RB15, RC13 & RC14, RD4 - RDRD7, RF4 & RF5

Reads pin on port
x = io_read(port, pin)

Example: x = io_read(PORTB, 11)

Sets pin on port
io_write(port, pin, ID)

Example: io_write(PORTB, 11, HIGH) // sets RB11 to '1'
               io_write(PORTN, 11,TOGGLE) // RB11 will now be '0'


Gets the whole port as a 32 bit number

x = io_getPort(port)

Example: x = io_gePort(PORTB) // gets all pins on PORTB

Sets the whole port

io_setPort(port, value)

Example io_setPort(PORTB, 3) // sets pins 0,1 to high, the rest low

This will set only those pins on the port that have the high bit set on the value


In the following example only bits 0 and 1 are set, to 1 the other bits on the port are left alone

Example io_setPortH(PORTB, 3)

This will clear only those pins on the port that have the high bit set on the value


In the following example only bits 0 and 1 are cleared, to 0 the other bits on the port are left alone

Example io_setPortL(PORTB, 3)

Peripheral Pin Select (PPS) Applies to MX1 Only

The MX1 device allows certain peripherals (UART etc.) to be mapped to different pins and the following two functions simplify the mapping. There is a distinction between input and output and only certain pins are allowed. The items in grey are not available on the 28 pin devices (BV500).

  Peripheral Pin
Inputs INT4,T2CK,IC4,SS1, PRA0  PRB3  PRB4  RB15  RB7  RRC7  RRC0  RRC5 
Outputs U1TXs,U2RTSs,SS1s,OC1s,C2OUTs
Outputs SDO1s,SDO2s,OC2s
Outputs SDO1s,SDO2s,OC4s,OC5s,REFCLKO
Outputs U1RTSs,U2TXs,SS2s,OC3s,C1OUTs

Special NOCONNECTs (see pps_out)

NOTES: The above are constants and should be prefixed with '*' when used in the pps functions, notice also that the output peripheral constants have a 's' postfix.

This function associates an input peripheral to a pin

pps_in(*peripheral(), *pin())

This function associates an output pin to a peripheral

pps_out(*pin(), *peripheral())

There is a special peripheral called NOCONNECTs. This is to disconnect an output peripheral as one output peripheral can be directed to more than one port. This is particularly useful for redirecting UART2.

How to Use

Example 1: Set up external interrupt 2, which is an input to be on RA4

pps_in(*INT2(), *PRA4()) // that's it, you will still need to set up the interrupt

Example 2: On all the boards UART1 is left for the user to use as UART is used for the main communication with the IC. So on the MX1 device we can set up on a choice of pins:

pps_out(*PRA0(), *U1TXs())
pps_in(*U1RX(), *PRA2())

The above will have U1TX coming out of pin RA0 and the input to the UART will be on pin RA2


Are no longer needed as constants with curly brackets are treated as arrays


BV500 Channel numbers. Note that channels 6,7 and 8 do not exists on the 28 pin device so an error will be given if trying to initialise one of those.

PORT    PIC    Channel
RA0      AN0      0
RA1      AN1      1
RB0      AN2      2
RB1      AN3      3
RB2      AN4      4
RB3      AN5      5
RC0      AN6      6   ** SMD Device 44 pin only
RC1      AN7      7   **
RC2      AN8      8   **
RB12    AN12     12
RB13    AN11     11
RB14    AN10     10
RB15    AN9      9

B513,BV514,BV523 Channel numbers (MX340)

ADC    PORT     Channel

AN0     RB0       0
AN1     RB1       1
AN2     RB2       2
AN3     RB3       3
AN4     RB4       4
AN5     RB5       5
AN6     RB6       6
AN7     RB7       7
AN8     RB8       8
AN9     RB9       9
AN10     RB10    10
AN11     RB11    11
AN12     RB12    12
AN13     RB13    13
AN14     RB14    14
AN15     RB15     15

Initialise the ADC system for the particular channel, the channel number is given in the table as a range from 0 to 9

Example adc_init(6) // this will set RB12 to be an adc input  

Gets the results of the adc channel 0 to 9
x = adc_get(channel)

Additional BP (Arduino shaped board) functions

These are available if BPn (where n is either 1 or 2) has been loaded on top of the normal rookie code.


For the BP2 board, there are up to 15 analogue inputs, see the information for the BP2 board.

Initialise the ADC system for the particular channel, the channel number is given as a constant

Example adc_init(?A3) // this will set pin A3 to be an adc input  

Gets the results of the adc channel
x = adc_get(?PIN)


This gives access to two 32 bit timers that can be set from anywhere between 1uS and 214 seconds. It utilises timers 2 and 3 in combination and 4 & 5 in combination. Simple to set up and can be useful.


TIMER23 - This is the timer 2 and 3 combination
TIMER45 - This is the timer 4 and 5 combination
Added for version 2.4 November 2013 see below


Sets timer to a time base of value in uS. The time base is the time it takes for the timer to roll over to 0 again. ONLY use Timer combinations (i.e. TIMER23) for these functions.
tmr_set(*timer(), value)

Example: tmr_set(*TIMER23(), 10000000) // sets the timer23 to have a time base of 10 seconds

Gets value of timer register

Example: x = tmr_get(*TIMER23())

Gets value of timer overflow flag. If the timer has overflowed then this flag will be set, it remains set until cleared by tmr_clr()

Clears timer and flag. The flag can be polled and cleared when set.

Starts and stops the timer, if the value is 0 then the timer stops. Note that the timer automatically starts when tmr_set() is used.
tmr_s(*timer(), value)

Added for version 2.4 November 2013. To set Individual timers.

Initialises a single 16 bit timer suing a prescale and base value.

The timer is a choice from the above single timer constants TIMER2 through TIMER5. Prescale will prescale the peripheral clock (40MHz) by a fixed amount:

Prescale Divide Value
0 0
1 2
2 4
3 8
4 16
5 32
6 64
7 256

The rollover value sets PRn. This means that when the timer reaches that value, it will set the interrupt flag and then reset to start counting up again.

Example tmr_init(TIMER2,7,60000)

In the above example, timer 2 is initalised so that the clock will be divided by 256 giving a frequency of 156.25KHz, so for a rollover value of 60,000 it should take 0.38 seconds to reach that value. NOTE the maximum that rollover can be is 16 bits or 65,535

Resets TMRn, this is the counter so for accurate timing, this can be set to zero with this function.

Example tmr_reset(TIMER5)

Turns the selected timer on or off, when v=1 the timer is on. The timer is automatically switched on when tmr_init() is used.

Example turn timer 3 off tmr_on(TIMER3,0)


This is more or less a copy of what is already in the library with some minor differences.

BV500 Hardware (Don't forget the pull up resistors)

// SCL1 is RB8
// SDA1 is RB9

BV513,BV514,BV523 Hardware (MX3) (channel 1 implemented)

// SCL1 is RG2
// SDA1 is RG3

I2c must be opened before use, this opens it at the requested frequency, normally 100000 or 400000

This will send a start condition followed by a byte that is the address of the slave I2C device you want to communicate with. The address is the 8 bit address and for writing should be an even number but for reading should be an off number.

Puts a byte on to the I2C bus, the device must have been addressed first using i2c_start() with an even address.

This closes the I2C bus for that data exchange

This is the restart command, stop and start can be used instead.
i2c_rstart(address) // Not available on MX340

This will get a byte from the I2C bus, this device actually does the clocking and so the slave needs some additional information. Last is set to 0 if there are more bytes to get, set to 1 if this is the last byte required from the slave. Prior to using theis the start function must be used with an odd address.
x = i2c_getc(last)

A utility that will return the first address (between start and end) of any device that may happen to be on the bus.

Example: print i2c_find(0x30,0xf0) // searches for addresses between 0x30 and 0xf0


Serial Peripheral Interface.

BV500 Hardware

// Data out SDO1 is RB13
// Data in  SDI1 is RB8
// Clock SCK1 is RB14

BV513,BV514,BV523 Hardware (MX3) Channel 2 implemented

On this hardware SPI channel 2 is already initialised and so spi_init() will simply set the speed, the default initialised speed is 10 MHz.

// Data out SDO2 is RG8 (MOSI)
// Data in  SDI2 is RG7 (MISO)
// Clock SCK2 is RG6

// Initialises the SPI bus, speed is in HZ, for 10MHz the speed value will be 10000000

Transfers data, with the SPI interface data is transferred in and out at the same time, by convention id just receiving is required the output byte is normally set to 0xff

Example: x = spi_data(0xff) // gets a byte from the SPI bus 
                     spi_data(0x33) // send 0x33 to the SPI bus


There are a few interrupts set up in rookie and more can be added if required, they are selected by the constant as shown below.


The following constants for PORTA and PORTB are fully available on the MX1, however only certain pins of particular ports are available for the MX3

  • IrPORTA - Interrupt on change port A only available on the MX1
  • IrPORTB - Interrupt on change port B only available on the MX1
  • IrCANGE - Change notice applies to MX3 only
  • IrTIMER23 - This is the interrupt for the timer 2,3 combination
  • IrTIMER45 - This is the interrupt for the timer 4,5 combination
  • IrEXT0 - External interrupt 0, this is the RB7 pin for the MX1 and the RF6 pin for the MX3 (64 pin version)

Constants Added for version 2.4

  • IrTIMER2 - 16 bit timer
  • IrTIMER3 - 16 bit timer
  • IrTIMER4 - 16 bit timer
  • IrTIMER5 - 16 bit timer

The following will clear all of the interrupts and remove then from the interrupt table.


An interrupt will call a user function and so setting up an interrupt involves selecting why an interrupt occurs (timer, external etc.) and then pointing this to a user function that will be called when the interrupt happens.

ir_set(*interrupt(), function_name$)

Example: ir_set(*IrEXT0(), "tunnLedOn")

In the above example a function has been created by the user to turn an LED on, the name of the function is 'turnLedOn()' and so this is provided WITHOUT the brackets to ir_set(). When an external interrupt occurs, the turnLedOn() function will be called.

Globally disables all interrupts, this is useful for temporarily suspending on going interrupts whilst a critical function is carried out. Note, the main communication uses interrupts and so using this will also suspend any incoming information on RX


Globally enables interrupts, this is only needed after the use of ir_di() as the interrupts are normally enabled.


There is another type of interrupt 'set' that applies to interrupt on change. The interrupt in this case is different for MX1 and MX3 devices.

Currently the above global interrupt functions only applies to MX1 devices

MX1 Devices

Sets a port / pin to have an interrupt on change.

ir_setPin(*interrupt(), function_name$, pin)

Example: ir_setPin(*IrPORTB(), "ir_function", 4)// this will call ir_function() when there is a change on RB4

MX3 Devices

There are only certain pins that can be used for interrupt on change on the MX3 device and these are listed below. There is also only ONE interrupt flag and so if more than one pin is set for interrupt then it is up to the interrupt service function to determine which pin caused the interrupt. It is important that in the user interrupt routine the port is read (can use io_getPort(port_name)) to clear any mismatch otherwise the interrupt will not be cleared.

The following can have interrupt on change:

Pin 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
Port C14 C13 B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 B15 D4 D5 D6 D7 F4 F5

This will associate one of the above port / pins to be an interrupt

ir_pinSet(port, function_name, pin)

Example: ir_setPin(PORTD, "ir_function", 4)// this will call ir_function() when there is a change on RD4

function ir_three()
    io_getPort(PORTD) // important clear
    print "\nInterrupt 3"
// MX3 test interrupt on change on RD4
function cgo()
    ir_setPin(PORTD, "ir_three", 4)


When working with interrupts, particularly those that work with a timer. the interrupt will be active all of the time or at least after the user program has run. This will effect the download process and so the interrupt should be stopped for that, one way to do this is to reset the device before downloading but it is easy to forget to do so. The simplest way is to put ir_clear() in the send string ot the tload dialog box like this:

(dialog box from the .tl command of BvSerial)


This is a general purpose function to be used for opening com port 1. If this is used then it will be the same for MX1 and MX3 and so no file conversion will be needed.

MX1 Defaults TX=RB4,  RX=RA2 (this matches the TX and RX printed on BP1)

MX3 Defaults TX=RF3, RX=RF2

com1_open(baud,buffer) // specify baud rate and buffer size in bytes (typical com1_open(9600,80))

For MX1 only: This has the ability to direct TX and RX to different pins with some restrictions:

RX choose from ?PRA2, ?PRA4, ?PRB2, ?PRB6, ?PRB13

TX choose from ?PRA0, ?PRB3, ?PRB4, ?PRB7, ?PRB15


Example: Open com1 and set TX on RA0 and RX on RA2
com1_pins(*PRA2(), *PRA0())


The following are additional functions that are not appropriate to the MX1 devices and so are only available to MX3 devices

rts_on() // turns on RTS hardware handshaking for use with fbload()

Touch Screen Devices

This will output the x and y co-ordinate of where the screen is touched, useful for locating items within an image for later activation when touched.


New font : Consolas16x8$


Available only on BV513


Available only on BV514

Displays the AT flash memory useage as a list of blocks.


Dumps the contents of a block of memory


Available only on the BV523

Powers off the BV523 completely. The only way it can be turned back on is via a reset or and alarm for the RTC so it is a good idea to set the alarm first. This will also activate the alarm so no need to use rtc_on(1) first. There are more details here of how this works and the RTC clock chip.


rtc_getAlarm$() // gets the current alarm setting as a string

rtc_setAlarm(dom,month,h,m,s) // set alarm

rtc_Alarm(value) // if value is 1 then alarm is turned on if 0 it is turned off

rtc_clear() // use this after an event otherwise a re-trigger of alarm cannot take place

Sets a repeating alarm where setting the value to 1=second, 2=minute, 3=hour, 4=day, 5=month and 6=year.


rtc_all() // just shows all of the rtc registers.

Touch screen calibration saving and retrieval. Saves and gets settings from an SD Card, the file name is 'cal'

Use cal_save The screen calibration can be saved to a file on the SD card for later use. Before using cal_save(), calibrate the touch screen using disapect(0) and tucalibrate(). The calibration can be checked with tu_locate, it okay then save to a file name. The start of a user program will normally use cal_get.

cal_save("filename")  // asks for calibration and saves data to SD Card

cal_get("filename") // gets previously saved calibration from SD Card


This is available on the BV513 and BV523 as they have SD Cards, this useful function is missing from the standard set of functions

fputs(string$, filenumber)

The filenumber is the one that is obtained when opening a file. The maximum string length has been set to 128.