HelvePic32


There is a German version at boxtec.ch

This board was designed together with my son Dominik.

The HelvePic32 can now be ordered as a kit from boxtec.ch. It is planned to ship the kit to EU customers from Germany.


The design criteria for this board are:

  • PTH, no SMD - I know, bulky and not sexy but acn be soldered by beginners such as kids
  • All available pins are borken out to the side, all pins are in 0.1" raster
  • both sides have the same pni layout so attachments (wings) to one side can be connected to the other side as well (with a change of the pins of course)
  • pins rows are distant from the PCB edge so angled pin connectors can be used
  • USB mini connector: not as bulky as the a-type and not so delicate as the micro type
  • PicKit pins on seperate pinrow to program the chip directly using PicKit3
  • I2C on seperate pinrow
  • pull-up resistors (4.7k) for I2C are present and selectable by jumpers
  • USB pins are on a seperate pinrow
  • both UARTs are on pins near the USB pinrow
  • SPI on ICSP 6-pin header
  • 8 MHz external oscillator leading to 40MHz CPU clock
  • use of standard DP32 bootloader

The final PCB has arrived and passed all test OK - lets have a look at it:

Uploading Code

On an Arduino, you just click on upload and the USB/FTDI chip puts the Arduino into bootloader mode for the upload. With the HelvePic32 you have to press the program button first and then the reset button to put the board into bootloader mode. Under Windows you will hear that the OS has detected the COM-Port.

The Pin-Map

The HelvePic32 uses the same bootloader as the ChipKit DP32 and for the use of the board it is best to use the DP32 as board selection for the HelvePic32. I plan to define the HelvePic32 as an explicit board, however, as all works fine with the DP32 board selection, I have this on the low priority list.

The DP32 uses a pinmapping that is not convenient for the HelvePic32. I therefore define a pin array to map the DP32 pin numbers to the pins of the HelvePic32, taking into account the two sides of the HelvePic32:

const uint8_t LEFT=0;
const uint8_t RIGHT=1;
// pins of verison 1.0 using DP32 board definition
uint8_t nP[2][8] = {{0,17, 9,10,11,12,13,14},{18,17, 1, 2, 3, 6, 7, 8}};

The definition of the both side is option but makes the handling of the code more easier. The user LED is connected to the second pin on the right side and can be used via

nP[RIGHT][2]

Looking at the pinmap, you may recognize that ther is the pin 17 that appears on both sides. This is due to the fact that I have 8 pins on both sides (in total 16) but only 15 pins to use for GPIO. I therefore decided to put the PROGRAM pin on both sides at the same place so that any wing connected to one of the sides can take the program pin low and the reset to put the board into bootloader mode. Aside of the GPIO pins on each side, there are some other pins that are of interest on the board:

  • next to the USB sockiet, all USB lines are available via a pin row. This makes RB10 and RB11 available if you want to use them as they are connected to D+/D-
  • next to the USB pins there are the RX7TX pins for UART1 and UART2. UART1 is 5V tolerant
  • on the lower left side I have arranged the SPI pins in the way they are arranged in the Arduino world
  • next to the crystal, the two pins connected to the crystal are available (RA2 and RA3)
  • on the lower right side, there are two 4.7 kOhm resistors to be used as pull-up resistors for I2C. They are not connected by default but can be using jumpers
  • on the bottom of the board there are the pins needed to connect the board to the PicKit3 adapter and the standard pins for the I2C bus.

Blink

In the good tradition of the Arduino world, the first step is to let the user led blink. The HelvePic32 offers two possibilities:

1) Traditional digitalWrite()

const uint8_t LEFT=0;
const uint8_t RIGHT=1;
uint8_t nP[2][8] = {{0,17, 9,10,11,12,13,14},{18,17, 1, 2, 3, 6, 7, 8}};

void setup()
{
	pinMode(nP[RIGHT][2],OUTPUT);
}

void loop()
{
	digitalWrite(nP[RIGHT][2], HIGH);
	delay(250);
	digitalWrite(nP[RIGHT][2], LOW);
	delay(250);
}

2) Register Manipulation

I am usinhg a trick that I saw in the UTFT library code. Instead of determening the Port Register and the bitmap mask by myself I can ask the system to return them to me. This works for the arduino as well as all ChipKit boards. I only have to take into accoun the right value definition as the Arduino is a 8-bit system and the HelvePic32 is a 32-bit system.

So I define two arrays, one for the pointers to the Port Register and one for the bitmask. I also define two pre-processor macros to set (SBI) and clear (cbi) a bit:

#define cbi(reg, bitmask) *reg &= ~bitmask
#define sbi(reg, bitmask) *reg |= bitmask
const uint8_t LEFT=0;
const uint8_t RIGHT=1;
uint8_t nP[2][8] = {{0,17, 9,10,11,12,13,14},{18,17, 1, 2, 3, 6, 7, 8}};

#if defined(__AVR__)
volatile uint8_t *pP[2][8];
uint8_t bP[2][8];
#elif defined(__PIC32MX__) volatile uint32_t *pP[2][8];
uint32_t bP[2][8];
#endif void _IO_Init() { for (uint8_t i=0; i<2; i++){ for (uint8_t j=0; j<8; j++){ pP[i][j] = portOutputRegister(digitalPinToPort(nP[i][j])); bP[i][j] = digitalPinToBitMask(nP[i][j]); } } } void setup() { _IO_Init(); for (uint8_t i=0; i<8; i++){ pinMode(nP[RIGHT][i],OUTPUT); } } void loop() { sbi(pP[RIGHT][2], bP[RIGHT][2]); delay(500); cbi(pP[RIGHT][2], bP[RIGHT][2]); delay(100); }

(to differentiate the sketch from the previous one, different delay times were used)

What is the difference?

On the oscilloscope, I looked at the pulses if I have a digitalWrite(pin,HIGH) directly followed by a digitalWrite(pin,LOW) compared to a sbi() followed by a cbl() call:

The picture shows that the shortest pulse using digitalWrite() is 1.2 μs wheras the sbi/cbi only needs 140 ns, making these kind of actions 8-10 times faster.

Serial Interface

The next test covers the serial interface: There are three serial interfaces:

  1. Serial.begin(9600) -  the normal serial interface via the USB connection. After a reset, it takes about a second to be ready
  2. Serial0.begin(9600) - UART1 on the board
  3. Serial1.begin(9600) - UART2 on the board

The code is pretty simple:

void setup() {
	Serial.begin(9600);
	Serial0.begin(9600);
	Serial1.begin(9600);
}

void loop() {
  Serial.println("Hello World 1");
  Serial0.println("Hello World 2 UART1");
  Serial1.println("Hello World 3 UART2");
  delay(1000);
}

With this code, the connected PC will see three serial ports being available and in the connected terminal windows you will see the different output. The baud rate used here of 9600 can be replaced independently by higher baud rates. To connect to the pins on the board I used FOCA breakouts. Please note that you have to connect RX of the FOCA with TX of the UART and TX of the FOCA with RX of the UART.

The nice thing with this setup is that it is easily possible to connect to a serial device like a bluetooth adapter without interfering with the USB connection as it is the case with the Arduino.

Servo

Now is a good time to get something to move with our board by connecting a servo. I directly go to the supplied SoftPWMServo library as it offer to run a servo ono any GPIO pin of the chip. In the example, I connect the servo to the pin 4 on the right side of the board:

The code is quite simple and straight forward:

const uint8_t LEFT=0;
const uint8_t RIGHT=1;
uint8_t nP[2][8] = {{0,17, 9,10,11,12,13,14},{18,17, 1, 2, 3, 6, 7, 8}};

#include <SoftPWMServo.h> 

int pos = 0;         // variable to store the servo position, in microseconds
const int pin = nP[RIGHT][3];  // Choose _any_ pin number on your board, i.e. pin 3, right side
 
void setup() 
{ 
} 
 
void loop() 
{ 
  for(pos = 1000; pos < 2000; pos += 10)  // goes from 1ms to 2ms
  {                                       // in steps of 10us
    SoftPWMServoServoWrite(pin, pos);     // tell servo to go to position in variable 'pos' 
    delay(25);                            // waits 25ms for the servo to reach the position 
  } 
}

For a specific servo, it is useful to test to extend the range for the position to 500 - 2500 ms. I was surprised by the smoth movement of the servo compared to the normal servo lib I used in the Arduino setup.

16x2 LCD Display

To have a nice output device, it is quite common to connect a simple Text-LCD with two lines and 16 characters per line. As the ChipKit project aims for high compatibility with the Arduino world, the example is quite the same as for the Arduino setup:

The code only requires the definition of the correct pins:

/*
 Code based on: LiquidCrystal Library - Hello World
 
 Library originally added 18 Apr 2008 by David A. Mellis
 library modified 5 Jul 2009 by Limor Fried (http://www.ladyada.net)
 example added 9 Jul 2009 by Tom Igoe
 modified 22 Nov 2010 by Tom Igoe
 modified for HelvePic32 30 Dec 2014 by Mathias Wilhelm
 This example code is in the public domain.
 */
const uint8_t LEFT=0;
const uint8_t RIGHT=1;
uint8_t nP[2][8] = {{0,17, 9,10,11,12,13,14},{18,17, 1, 2, 3, 6, 7, 8}};

#define RS nP[RIGHT][0]
#define EN nP[RIGHT][1]
#define D4 nP[RIGHT][2]
#define D5 nP[RIGHT][3]
#define D6 nP[RIGHT][4]
#define D7 nP[RIGHT][5]

#include <LiquidCrystal.h>
LiquidCrystal lcd(RS, EN, D4, D5, D6, D7);

void setup() {
  lcd.begin(16, 2);
  lcd.print("hello, world!");
}

void loop() {
  lcd.setCursor(0, 1);
  lcd.print(millis()/1000);
}

The #defines used are only there to ease the readybility of the code.

Please note: The poti used to set the contrast via the pin V0 of the LCD has to be driven to the edge with 3.3Volt. I used a 10kOhm poti. Using a 5kOhm poti did not give me a contrast at all, the LCD stayed clear and it took me some hours to find out that the contrats voltage was not high enough. In this example, the LCD backlight is not used.

Analog Input

The PIC32MX250F128B has 9 analog input lines with 10 bit resolution. They are numbersed as A0 - A8 and can be used with this name in the code. For compatibility with the other examples, I will stick to the pin names in my array. If I connect a 10 kOhm poti to GND and 3.3V and the middle pin to pin 2 on the left side, I can use this code to read the analog input (please note that the maximum voltage is 3.3V not 5V!)

/*
 Code based on Analog Input
 Created by David Cuartielles
 Modified 4 Sep 2010 by Tom Igoe
 modified 31 Dec 2014 by Mathias Wilhelm
 This example code is in the public domain.
 http://arduino.cc/en/Tutorial/AnalogInput
 */

const uint8_t LEFT=0;
const uint8_t RIGHT=1;
uint8_t nP[2][8] = {{0,17, 9,10,11,12,13,14},{18,17, 1, 2, 3, 6, 7, 8}}; 

int sensorPin = nP[LEFT][2];    // select the input pin for the potentiometer
int ledPin = nP[RIGHT][2];      // select the pin for the LED
int sensorValue = 0;  		// variable to store the value coming from the sensor

void setup() {
  pinMode(ledPin, OUTPUT);  
}

void loop() {
  sensorValue = analogRead(sensorPin);    
  digitalWrite(ledPin, HIGH);  
  delay(sensorValue);          
  digitalWrite(ledPin, LOW);   
  delay(sensorValue);                  
}

It seems to be a good idea to connect the servo to the right side as above to combine the code:

/*
 Control a Servo via a poti
 Created  31 Dec 2014 by Mathias Wilhelm
 */

const uint8_t LEFT=0;
const uint8_t RIGHT=1;
uint8_t nP[2][8] = {{0,17, 9,10,11,12,13,14},{18,17, 1, 2, 3, 6, 7, 8}};

#include <SoftPWMServo.h> 

int pos = 0;     		// variable to store the servo position, in microseconds
const int pin = nP[RIGHT][4];	// Choose _any_ pin number on your board
int sensorPin = nP[LEFT][2];    // select the input pin for the potentiometer
int ledPin = nP[RIGHT][2];      // select the pin for the LED
int sensorValue = 0;  		// variable to store the value coming from the sensor

void setup() {
  pinMode(ledPin, OUTPUT);  
}

void loop() {
  sensorValue = analogRead(sensorPin); 
  pos = map(sensorValue,0, 1023, 1000, 2000);
  SoftPWMServoServoWrite(pin, pos);
  delay(25);
}

Step Motor

Instead of a servo, a step motor can also be connected to the HelvePic32. In this example I use the cheap and well known 28BYJ-48 Step Motor in the 5V setup and the common ULN2003 driver chip. The 28BYJ-48 has a gearbox in from that takes 2048 steps for the shaft to make a full rotoation.

With all the versions available for this motor, the colors of the cables may vary. I therefore added a simple cable schema.

The code is exactly the same as for the Arduino setup:

/* 
	Based on Stepper exemple V1.01 11/30/2013 by terry@yourduino.com 
	*/
#define LEFT 0
#define RIGHT 1
uint8_t nP[2][8] = {{0,17, 9,10,11,12,13,14},{18,17, 1, 2, 3, 6, 7, 8}}; // pins of version 1.0 using DP32 bootloader

#include <AccelStepper.h>
#define FULLSTEP 4
#define HALFSTEP 8
// motor pins
#define motorPin1  nP[RIGHT][3]     // Blue   - 28BYJ48 pin 1
#define motorPin2  nP[RIGHT][2]     // Pink   - 28BYJ48 pin 2
#define motorPin3  nP[RIGHT][1]     // Yellow - 28BYJ48 pin 3
#define motorPin4  nP[RIGHT][0]     // Orange - 28BYJ48 pin 4
			            // Red    - 28BYJ48 pin 5 (VCC)
// NOTE: The sequence 1-3-2-4 is required for proper sequencing of 28BYJ48
AccelStepper stepper1(HALFSTEP, motorPin1, motorPin3, motorPin2, motorPin4);

void setup()
{
  stepper1.setMaxSpeed(1000.0);
  stepper1.setAcceleration(50.0);
  stepper1.setSpeed(200);
  stepper1.moveTo(2048);  // 1 revolution   
}

void loop()
{
  //Change direction at the limits
  if (stepper1.distanceToGo() == 0) stepper1.moveTo(-stepper1.currentPosition());
  stepper1.run();
}

Warning: A step motor drwa a lot of current. The USB volatge supply may be replaced by a stronger voltage source. The ULN2003 can also be used with 12V if the 12V version of the motor is used.

Servo Details

Looking at the PWM signals generated by the normal Servo library, you can see that the puls width varies between 500 μs and 2300 μs. This means that it is possible to select the servo position in centi-degrees. However, the library limits ba default the puls-width to 2000 μs; the default values need to be altered. The code looks like this:

const uint8_t LEFT=0;
const uint8_t RIGHT=1;
uint8_t nP[2][8] = {{	0,17, 9,10,11,12,13,14},{18,17, 1, 2, 3, 6, 7, 8}};
const int Smax=1800;
const int Smin=500;

#include <SoftPWMServo.h> 

int pos = 0;         
const int pin = nP[RIGHT][7];  // Choose _any_ pin number on your board

void setup() 
{
	SoftPWMServoInit();
	SoftPWMServoSetFrameTime(100000); // 2500 us are 100000 tics for 40 MHz CPU!
	SoftPWMServoSetServoFrames(8);    // 8 frames make 20 ms wavelength
} 

void loop()
{ 
	for (pos=0; pos <= 1800; pos+=2) // use deci-degree in 0.2 degree steps
	{
		SetServoPos_decidegree(pos);
		delay(5);
	}
	for (pos=0; pos <= 1800; pos+=2) // use deci-degree in 0.2 degree steps
	{
		SetServoPos_decidegree(1800-pos);
		delay(5);
	}
	for (pos=0; pos <= 180; pos++) // use degree in 1 degree steps
	{
		SetServoPos_degree(pos);
		delay(25);
	}
	for (pos=0; pos <= 180; pos++) // use degree in 1 degree steps
	{
		SetServoPos_degree(180-pos);
		delay(25);
	}
	SetServoPos_degree(90);
	delay(1000);
	SetServoPos_degree(180);
	delay(1000);
	SetServoPos_degree(0);
	delay(1000);
}

void SetServoPos_degree(int degree)
{
	SoftPWMServoServoWrite(pin, 10*degree+Smin); 
}

void SetServoPos_decidegree(int decidegree)
{
	SoftPWMServoServoWrite(pin, decidegree+Smin); 
}

The code drives the servos once from 0 to 180 degrees in 0.2 degree steps and back, then again but in 1 degree steps. At the end the positions 90°, 180° und 0° are selected for a second each.

I moved the documentation to the top level of my web presence (click here)

Downloads:

Succesful tests so far:

  • Test with I2C 8×8 Minimatrix from adafruit OK without code change to the Adafruit Libraries
  • Test with I2C SSD1306 OLED OK
  • Test with SPI TFT 320×240 Pixel OK, using UTFT with minor changes
  • Test with 4 Adafruit NeoPixel strips OK. PICxel library used as the Adafruit-code is Arduino-specific. Adjusted PICxel lib so that democode of Adafruit runs without change. Timing for 40 MHz CPU resolved.

Eagle Schematics: