Thursday, July 23, 2009

Jaluino: an Arduino-like board, power by jal


Jaluino, with user guide, schematics, PCB, shields, etc... can now be reached on the following sites:

Main site: http://jaluino.org
Repository: http://jaluino.googlecode.com
Group: http://groups.google.com/group/jaluino


It's been a while I'd like to build and provide the same kind of board as Arduino's, but PIC-based. And powered by Jalv2 and jallib, of course. The reasons are pretty clear: as we share our jal code (software), why don't we also share our boards & PCB (hardware) ? Jaluino to the rescue !

There can be many benefits having such a board, such a common hardware. For instance, everybody would be able to get a board, and experiment with it (educational benefits). Keeping hardware problems away helps focusing on the real experiment's content. Jaluino provides a base, a context on which anyone can build extra boards. These can then be shared with everyone, easily, since Jaluino is the common base. This is what Arduino's shields are about, and what would be nice to reproduce.


Which PIC should be used for Jaluino ?

The size and the features are what matter. A USB capable PIC is somewhat mandatory, it opens oportunities for nice experimentation. 18F PICs seem to be the candidates. What about these three PICs:

  • 18F14K50 : 20-pin, cheap, lots of features
  • 18F2550 : 28-pin
  • 18F4550 : big fat 40-pin, the max for a PDIP package.
Following Arduino's ideas, several Jaluino boards could be provided:

  • Jaluino Mini : 18F14K50 based, breadboard layout (no shield connectors, just plug your Jaluino board on a breadboard). Closed to Pinguino, but with a smaller PIC
  • Jaluino Medium : 18F2550 based. Board contains connectors on which we can plug shields. A breadboard version can also be built (same as Pinguino)
  • Jaluino Maxi : 18F4550 based. Board is "shieldable" too, a breadboard version may not be a good idea, as the PIC is quite big a would require a rather large breadboard.

(we'd definitively need to work on marketing names for "Mini", "Medium", "Maxi"...)


Connectors for shields

Despite several Jaluino boards would be available, shield connectors have to be compatible. Ex: a shield for 18F2550 can be used with a 18F4550 (the reverse isn't true...). As Arduino Diecilima's shields are compatible with Arduino Mega. This can be done by extanding the 18F2550 layout by other connectors.

Comparing 18F2550 and 18F4550 pins, we can see one is perfectly included into the other. In green, pins from 18F2550 which be found in 18F4550, in blue, 18F4550 only pins. Circle-red pins are important features: serial, I²C, USB and OSC. Fortunatly, these are carried by the exact same pins between 18F2550 and 18F4550.

PIC 18F2550


PIC 18F4550

(also note 18F877 could be used and plugged into a Jaluino Maxi as OSC, RX/TX are carried by the same pins as in 18F4550. Only I²C -- and USB of course -- pins aren't compatible).

The general layout for Jaluino Medium and Jaluino Maxi would look like this (not scaled...). Orange connector is dedicated to power supply. Green connectors are exactly on the place between Maxi and Medium Layout, these are the compatible pins. Blue connectors export 18F4550 pins only.


Other connectors...

Several other connectors would be needed:

  • USB, of course...
  • I²C : following Joep's suggestion, a 5-pin header (+9V,+5V, GND, SCL, SDA) can be used a dedicated connector, making easier connection between boards & other parts using this protocol
  • Serial : again, following Joep's suggestion, a 4-pin header (+5V, GND, TX, RX) would allow to plug any serial module (see later)
  • ICSP : to easily program the chip, even if no bootloader is present

Serial : different options

In addition to dedicated USB connector, a serial connector would allow easy comms. from Jaluino to the external world. But what type of connector to provide ? DB9 ? USB via USB-to-serial conversion ?

Here are the different alternatives, depending on budget limits, easiness to make, and parts used to build this module:

  • USB-to-Serial via FDTI chip: widely parts, but FTDI chip only exist on SSOP package, hard to solder (but not impossible). Small. USB can be used to power the board.
  • USB-to-Serial, using a 18F14K50 : easy to make (though 18F14K50 may be quite hard to find...). Size to define. USB can be used to power the board.
  • DB9 connector, using MAX232 chip : classical, quite big. External power supply will be required
  • what-you-want option: just plug a module which honor the connector's interface. For instance, I have a bluetooth-to-serial module, this gives the opportunity to easily integrate it to Jaluino.

Few words about bootloader

ICSP connector make the develop-program-test cycle faster, preventing unplug/plug the chip to program it. But still it requires a programmer... A bootloader is required here.

There are many different ones out there, but Tiny Bootloader is widely used, very (very) small, many different programs exits for many different platforms. Bootloader should be able to be triggered without having to manually reset the device (see here for more, it seems to require some hardware parts).


A first schematic

I started to build a schematic. Please keep in mind this is very alpha, and this is not even supposed to work...




License

Following Arduino, I'd like to release Jaluino schematics & PCB under Creative Commons Attribution-Share Alike 3.0.


Basically, it says:

  • AttributionYou must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work).
  • Share AlikeIf you alter, transform, or build upon this work, you may distribute the resulting work only under the same, similar or a compatible license.
See here for more details.


Any suggestions ? Would you like to join the effort ?


PS: see this thread on jallib group for a discussion. Also search the group more...



Sébastien Lelong

Friday, July 10, 2009

Using new ADC libraries

Since several weeks or months, quite a lot of effort has been put into the development and update of ADC libraries. Why ? Because when Guru Stef Mientki first developed this library, he designed it for quite a few PICs (but most used ones at this time). Since jallib is to support all (or as much as) possible PICs, it was time to have a look on this huge piece of code...


Many different cases...

As usual, Microchip PICs offers a wide choice configuring ADC:
  • Not all PICs have ADC module (...)
  • Analog pins are dispatched differently amongst PICs, still for user's sake, they have to be automatically configured as input. We thus need to know, for each PIC, where analog pins are...
  • Some PICs have their analog pins dependent from each other, and some are independent (more on this later)
  • Clock configuration can be different
  • Some PICs have 8-bits low resolution ADC module, some have 10-bits high resolution ADC module (and some have 12-bits, those aren't currently handled, that's a restriction...)
  • Some PICs can have two voltage references (VRef+ and VRef-), only one voltage reference (Vref+) and some can't handle voltage references at all
  • (and probably other differences I can't remember :)...
Luckily most of these differences are transparent to users (hey, that was the goal of this refactoring/rewriting...).


Dependent and independent analog pins

OK, let's write some code ! But before this, you have to understand one very important point: some PICs have their analog pins dependent from each other, some PICs have their analog pins independent from each other. "What is this suppose to mean ?" I can hear... Let's consider two famous PICs: 16F877 and 16F88. 16F877 datasheet explains how to configure the number of analog pins, and vref, setting PCFG bits:


Want 6 analog pins, no Vref ? Then PCFG bits must be set to 0b1001. What will then be the analog pins ? RA0, RA1, RA2, RA3, RA5 and RE0. "What if I want 7 analog pins, no Vref ?" You can't because you'll get a Vref pin, no choice. "What if I want 2 analog pins being RE1 and RE2 ?" You can't, because there's no such combination. So, for this PIC, analog pins are dependent from each other, driven by a combination. In this case, you'll have to specify:
  • the number of ADC channels you want,
  • and amongst them, the number of Vref channels
Note: this is the behavior of the original Stef's lib adc_hardware.jal

Now, let's consider 16F88. In this case, there's no such table:


Mmmh... OK, there are ANS bits, one for each analog pins. Setting an ANS bit to 1 sets the corresponding pin to analog. This means I can set whatever pin I want to be analog. "I can have 3 analog pins, configured on RA0, RA4 and RB6. Freedom !"

Analog pins are
independent from each other in this case, you can do what you want. As a consequence, since it's not driven by a combination, you won't be able to specify the number of ADC channels here. Instead, you'll use set_analog_pin() procedure, and if needed, the reverse set_digital_pin() procedure. These procedures takes a analog pin number as argument. Say analog pin AN5 is on pin RB6. To turn this pin as analog, you just have to write set_analog_pin(5), because this is about analog pin AN5, and not RB6.

Note: As a consequence, these procedures don't exist when analog pins are dependent as in our first case.

Careful:
it's not because there are PCFG bits that PICs have dependent analog pins. Some have PCFG bits which act exactly the same as ANS bits (like some of recent 18F)

"Stop it, show me some code !"

Basically, all the logic accessing ADC module has been kept. Most of the work was about configuring the ADC module to handle all PICs. You'll find adc_read() and adc_read_low_res() functions, as usual. Changes only occur while configuring the ADC module.

16F877 : dependent analog pins

The following examples briefly explains how to setup ADC module when analog pins are dependent from each other, using PIC 16F877.

The following diagram is here to help knowing where analog pins are and where Vref pins are:



Example 1: 16F877, with only one analog pin, no voltage reference

-- beginning is about configuring the chip
-- this is the same for all examples for about 18F877
include 16f877
-- setup clock running @20MHz
pragma target OSC HS
pragma target clock 20_000_000
-- no watchdog
pragma target WDT disabled
pragma target LVP disabled
enable_digital_io()
include delay

-- ok, now setup serial, we'll use this
-- to get ADC measures
const serial_hw_baudrate = 19_200
include serial_hardware
serial_hw_init()


-- ok, now let's configure ADC
-- we want to measure using low resolution
-- (that's our choice, we could use high resolution as well)
const bit ADC_HIGH_RESOLUTION = false
-- we said we want 1 analog channel...
const byte ADC_NCHANNEL = 1
-- and no voltage reference
const byte ADC_NVREF = 0
-- now we can include the library
-- note it's now named "adc", not "adc_hardware" anymore
include adc
-- and run the initialization step
adc_init()


-- will periodically send those chars
var byte measure
forever loop
-- get ADC result, on channel 0
-- this means we're currently reading on pin RA0/AN0 !
measure = adc_read_low_res(0)
-- send it back through serial
serial_hw_write(measure)

-- and sleep a litte to prevent flooding serial...
delay_1ms(200)
end loop

Example 2: 16F877, with 5 analog pins, 1 voltage reference, that is, Vref+

This is almost the same as before, except we now want 5 (analog pins) + 1 (Vref) = 6 ADC channels (yes, I consider Vref+ pin as an ADC channel).

The beginning is the same, here's just the part about ADC configuration and readings:

const bit ADC_HIGH_RESOLUTION = false
-- our 6 ADC channel
const byte ADC_NCHANNEL = 6
-- and one Vref pin
const byte ADC_NVREF = 1
-- the two parameters could be read as:
-- "I want 6 ADC channels, amongst which 1 will be
-- reserved for Vref, and the 5 remaining ones will be
-- analog pins"
include adc
adc_init()

-- will periodically send those chars
var byte measure
forever loop
-- get ADC result, on channel 0
-- this means we're currently reading on pin RA0/AN0 !
measure = adc_read_low_res(0)
-- send it back through serial
serial_hw_write(measure)

-- same for pin RA1/AN1
measure = adc_read_low_res(1)
serial_hw_write(measure)

-- same for pin RA2/AN2
measure = adc_read_low_res(2)
serial_hw_write(measure)

-- pin RA3/AN3 can't be read, since it's Vref+

-- same for pin RA5/AN4
-- 4 is from from "AN4" !
measure = adc_read_low_res(4)
serial_hw_write(measure)

-- same for pin RE10/AN5
measure = adc_read_low_res(5)
serial_hw_write(measure)

-- and sleep a litte to prevent flooding serial...
delay_1ms(200)
end loop


16F88 : independent analog pins

The following example is about setting up ADC module with PIC 16F88, where analog pins are independent from each other.

The following diagram is here to help knowing where analog pins are and where Vref pins are:


Example 1: 16F88, analog pins on RA0/AN0, RA4/AN4 and RB6/AN5. No voltage reference.

-- beginning is about configuring the chip
include 16f88
-- We'll use internal oscillator. It work @ 8MHz
pragma target CLOCK 8_000_000
pragma target OSC INTOSC_NOCLKOUT
OSCCON_IRCF = 0b_111
pragma target WDT disabled
enable_digital_io()

-- ok, now setup serial, we'll use this
-- to get ADC measures
const serial_hw_baudrate = 19_200
include serial_hardware
serial_hw_init()

-- now configure ADC
const bit ADC_HIGH_RESOLUTION = false
const byte ADC_NVREF = 0
-- we can't specify a number of ADC channel here,
-- or we'll get an error !
include adc
adc_init()
-- now we declare the pin we want as analog !
set_analog_pin(0) -- RA0/AN0
set_analog_pin(4) -- RA4/AN4
set_analog_pin(5) -- RB6/AN5

-- reading is then the same
var byte measure
forever loop

measure = adc_read_low_res(0)
serial_hw_write(measure)

measure = adc_read_low_res(4)
serial_hw_write(measure)

measure = adc_read_low_res(5)
serial_hw_write(measure)

end loop



Whether you would want to turn RB6/AN5 into a digital pin again, you'd just call:

set_digital_pin(5)



OK, enough for now. Next time, we'll see how it works for real, with a "Step by Step" tutorial. We'll probably use a 16F88 for this, using serial.

As of July 19th, 2009, this ADC libraries aren't release yet (but expected to be soon). If you want to have fun with them, you'll have to get them from jallib SVN repository. See instructions here:

http://code.google.com/p/jallib/source/checkout


You can also get files one by one from SVN:

Anyway, browse SVN as needed :)



Sébastien Lelong

Monday, June 29, 2009

PIC 18f14k50 USB Interface Board: Part 1

Introduction
This is the first blog regarding a PIC18f14k50 USB IO board. This first session will focus on the design and building of the board, future sessions will take about software application that are being developed for this board.

At the start of the development of the board, I had the following “bullet spec” in mind:

  • Development “starter” board for Jal(lib) USB developement
  • It must have a USB 2.0 full speed connection
  • Must be able to use it as a USB<->RS-232 interface converter, including 2 HW handshake signal, operate as a true RS-232 interface as well have the ability to use it for 5v signals (RS-232c, so it can be used to interface board towards other test boards that don’t have a MAX-232 level converter).
  • Must be able to use it as a USB<->I2C interface board, either act a master or slave device
  • Be able to add (small) daughter boards, in order use to the board as a HW/SW development platform (e.g. Remote control receiver, remote control transmitter etc)
  • Homebrew friendly (easy to build, single sided PCB, no SMD component)
  • Cheap (~10 Euro)


PIC selection
The first choice is what PIC to use, since I want to use it for JAL development (which restricts me to the PIC18F series), onboard USB Serial Interface Engine (SIE), should be a thru hole device and should run with 5v IO, I got the following list from the Micrcochip website:



Product Fam Price Fl EE RAM I/O

PIC18F13K50 $1.39 8 256 512 15
PIC18F14K50 $1.53 16 256 768 15
PIC18F2450 $2.23 16 0 768 23
PIC18F2455 $3.30 24 256 2048 24
PIC18F2458 $3.58 24 256 2048 24
PIC18F2550 $3.44 32 256 2048 24
PIC18F2553 $4.12 32 256 2048 24
PIC18F4450 $2.39 16 0 768 34
PIC18F4455 $3.51 24 256 2048 35
PIC18F4458 $3.79 24 256 2048 35
PIC18F4550 $3.65 32 256 2048 35
PIC18F4553 $4.33 32 256 2048 35



In the past I’ve used the 18f4550, which is a nice chip, but it is a bit expensive. I also have used the 18f2450 in a couple of projects; however, the main drawback is the lack of onboard eeprom (always handy for device specific settings). The recently introduced PIC 18f14k50 appears to be a nice compromise (almost similar to the 18f2450, but it is a bit cheaper and has onboard eeprom). Even though it is a low pin count device, I think it has enough IO pins. I have considered the 13k50, which is even a dime cheaper, but the flash and memory size is quite limited and the availability is a problem. However, the board can be used for both chips.



Schematic & PCB:

Once the PIC was selected, the design is straightforward:




I won’t go into the details of the “standard” PIC logic, consisting of the crystal (X1) + caps (C3/C4), ISCP connector (J1) and decoupling capacitor (C1).
There are two switches connected to the PIC, SW1 is can be used as a reset switch, SW2 is a generic switch with pull resistor. The DB-9 connector is connected via a MAX-232 level converter with the PIC (towards the internal UART PINs). The non-level shifted converted TX/RS signals are also connected to J3, in addition to the UART signals pin-5 is connected with the PWM/CCP unit of the PIC.

The 5 volt supply of the USB connector is used to power the board; the differential USB signal lines d+/d- are directly coupled from the PIC to the USB connector. Capacitor C5 is required for the internal 3.3v regulator.

The I2C header signals are also directly coupled with the MSSP unit, it can be used as a master or slave depending on the firmware, besides the 3 standard wires, also a 5v is available on the connector. The pull-ups can be enabled by placing the jumper (JP1).
Potmeter P1 is connector to AN5 of the PIC, this can be used for ADC experiments.


OK enough about the schematic. Richard Zengerink was so kind to draw the schematic and do the board layout. The board layout is depicted below, a PDF file of the layour is available in the SVN repository ( 18F14K50+EXTRA_lay_out.pdf
), this has the mirrored, 1:1 scale, so when printed on glossy paper with a laser printer, it can can be ironed into a copper cladded board, then etch it and drill it.








Building the board should not be too difficult; it took me about an hour to do the component assembly and solder it and perform some quick tests (check power connections, and check shortcuts)
A picture of the assembled board is depicted below:



Testing the board;
The first test is to connect a PIC programmer to the ICSP connector. Program the 18f14k50_led_switch_test.hex file on in the PIC and reset the board by doing a power off/on cycle (external reset is disabled in order to test SW1). The source code and Hex file are available in the jallib SVN repository


The application will turn on both LEDs for about 5 seconds, afterwards it starts to blink both LEDs every second. When SW1 is pressed, LED1 will stop blinking, when SW2 is pressed, LED2 will stop blinking.

Once this is working, we can confirm that ICSP, Power, oscillator, SW1, SW2, LED1 and LED2 are properly mounted.

The next step is to download the bootloader. Select the 18f14k50_usb_bootloader.hex in the ICSP programmer, and program the hex file. Remove the ICSP connector (since there shared with the USB lines), connect the USB connector with a USB cable with the Host computer, when plugging in the USB connector, hold down SW2 (program button)

If everything went ok, after a couple of seconds the Host computer will detect the USB bootloader device, it will ask for a driver. Navigate to the directory where the USB driver bootloader driver files are located, ignore the certificate warning by selecting the continue button. MCHPUSB Custom Driver.zip contains required drivers. Unzip the package to a local drive, the drivers are located in the MCHPUSB Driver\Release directory

Once the drivers are installed, you can use the PDFSUSB.exe applicatio, contained in Pdfsusb.zip archive. After you unzip the file and launch the application, select the dropdown arrow of the combo box.



You should see the PICDEM FS USB 0 (BOOT) option, select it (see also screenshot). After selection, it should update the status bar, it will depict the version number (1.20) of the firmware.

When using the bootloader, you need to add the following setting when compiling a JAL file in cooperation with the bootloader:

-loader18 –no-fuse

The JAL compiler will generate special startup and interrupt code if the loader18 option is specified, the no-fuse is used to avoid overwriting wrong fuses which can mess up the bootloader (although the PDFSUSB will also emit a warning, and ask for confirmation when overwriting the fuses)


So far for part1, next blog(s) will discuss how to use the board as a USB<->RS-232 convert, use it as a USB driven I2C master or slave device and show how to make a small daughter board to capture and generate remote control infrared signals.

Hope you like it, feeback is always welcome…

Albert Faber & Richard Zengerink



Saturday, April 11, 2009

Step by step: interfacing a Sharp GP2D02 IR ranger

Sharp IR rangers are widely used out there. There are many different references, depending on the beam pattern, the minimal and maximal distance you want to be able to get, etc... The way you got results also make a difference: either analog (you'll get a voltage proportional to the distance), or digital (you'll directly get a digital value). This nice article will explain these details (and now I know GP2D02 seems to be discontinued...)


Overview of GP2D02 IR ranger

GP2D02 IR ranger is able to measure distances between approx. 10cm and 1m. Results are available as digital values you can access through a dedicated protocol. One pin, Vin, will be used to act on the ranger. Another pin, Vout, will be read to determine the distance. Basically, getting a distance involves the following:

  1. First you wake up the ranger and tell it to perform a distance measure
  2. Then, for each bit, you read Vout in order to reconstitute the whole byte, that is, the distance
  3. finally, you switch off the ranger

The following timing chart taken from the datasheet will explain this better.



Note: the distances obtained from the ranger aren't linear, you'll need some computation to make them so.

Sharp GP2D02 IR ranger looks like this:


  • Red wire is for +5V
  • Black wire ground
  • Green wire is for Vin pin, used to control the sensor
  • Yellow wire is for Vout pin, from which 8-bits results read
(make a mental note of this...)


Interfacing the Sharp GP2D02 IR ranger

Interfacing such a sensor is quite straight forward. The only critical point is Vin ranger pin can't handle high logic level of the PIC's output, this level mustn't exceed 3.3 volts. A zener diode can be used to limit this level.

Note: be careful while connecting this diode. Don't forget it, and don't put it in the wrong side. You may damage your sensor. And I'm not responsible for ! You've been warned... That's said, I already forgot it, put it in the wrong side, and thought I'd killed my GP2D02, but this one always got back to life. Anyway, be cautious !

Here's the whole schematic. The goal here is to collect data from the sensor, and light up a LED, more or less according to the read distance. That's why we'll use a LED driven by PWM.




Here's the ranger with the diode soldered on the green wire (which is Vin pin, using your previously created mental note...):



I've also added thermoplastic rubber tubes, to cleanly join all the wires:



Finally, in order to easily plug/unplug the sensor, I've soldered nice polarized connectors:



Writing the program

jallib >=0.3 contains a library, ir_ranger_gp2d02.jal, used to handle this kind of rangers. The setup is quite straight forward: just declare your Vin and Vout pins, and pass them to the gp2d02_read_pins(). This function returns the distance as a raw value. Directly passing pins allows you to have multiple rangers of this type (many robots have many of them arranged in the front and back sides, to detect and avoid obstacles).

Using PWM libs, we can easily make our LED more or less bright. In the mean time, we'll also transmit the results through a serial link.

var volatile bit gp2d02_vin is pin_a4
var volatile bit gp2d02_vout is pin_a6
var bit gp2d02_vin_direction is pin_a4_direction
var bit gp2d02_vout_direction is pin_a6_direction
include ir_ranger_gp2d02
-- set pin direction (careful: "vin" is the GP2D02 pin's name,
-- it's an input for GP2D02, but an output for PIC !)
gp2d02_vin_direction = output
gp2d02_vout_direction = input

var byte measure
forever loop
-- read distance from ranger num. 0
measure = gp2d02_read_pins(gp2d02_vin,gp2d02_vout)
-- results via serial
serial_hw_write(measure)
-- now blink more or less
pwm1_set_dutycycle(measure)
end loop

Note: I could directly pass pin_A4 and pin_A6, but to avoid confusion, I prefer using aliases.

A sample, 16f88_ir_ranger_gp2d02.jal, is available in jallib SVN repository, and also in jallib released packages, starting from version 0.3. You can access downloads here.


Building the whole on a breadboard

Putting all this stuff on a breadboard should be easy. Just follow the schematics :)




I usually power two tracks on the side, used for the PIC and for the ranger:


Using the same previously created mental note, I connected the yellow Vout pin using a yellow wire, and the green Vin pin using a green wire...



Testing (and the video)


Time to test this nice circuit ! Power the whole, and check no smoke is coming from the PIC or (and) the ranger. Now get an object, like you hand, more or less closed to the ranger and observe the LED, or the serial output... Sweet !








Sébastien Lelong

Saturday, February 14, 2009

Step by Step: having fun with PWM and a piezo buzzer (or a speaker) (part 2)

The last time, we had fun by controlling the brightness of a LED, using PWM. This time, we're going to have even more fun with a piezo buzzer, or a small speaker.

If you remember, with PWM, you can either vary the duty cycle or the frequency. Controlling the brightness of a LED, ie. produce a variable voltage on the average, can be done by having a constant frequency (high enough) and vary the duty cycle. This time, this will be the opposite: we'll have a constant duty cycle, and vary the frequency.


What is a piezo buzzer ?

It's a "component" with a material having piezoelectric ability. Piezoelectricity is the ability for a material to produce voltage when it get distorted. The reverse is also true: when you produce a voltage, the material gets distorted. When you stop producing a voltage, it gets back to its original shape. If you're fast enough with this on/off voltage setting, then the piezo will start to oscillate, and will produce sound. How sweet...


Constant duty cycle ? Why ?

So we now know why we need to vary the frequency. This will make the piezo oscillates more and less, and produces sounds at different levels. If you produce a 440Hz frequency, you'll get a nice A3.

But why having a constant duty cycle ? What is the role of the duty cycle in this case ? Remember: when making a piezo oscillate, it's not the amount of volts which is important, it's how you turn the voltage on/off:
  • when setting the duty cycle to 10%: during a period, piezo will get distorted 10% on the time, and remain inactive 90% on the time. The oscillation proportion is low.
  • when setting the duty cycle to 50%: the piezo is half distorted, half inactive. The oscillation proportion is high, because the piezo oscillates at the its maximal amplitude, it's half and equally distorted and inactive.
  • when setting the duty cycle to 90%: the piezo will get distorted during 90% of a period, then nothing. The oscillation proportion is low again, because the proportion between distortion and inactivity is not equal.
(I guess this is about energy or something like that. One guru could explain the maths here...)

So, to summary, what is the purpose of the duty cycle in our case ? The volume ! You can vary the volume of the sound by modifying the duty cycle. 0% will produce no sounds, 50% will be the max volume. Between 50% and 100% is the same as between 0% and 50%. So, when I say when need a constant duty cycle, it's not that true, it's just that we'll set it at 50%, so the chances we hear something are high :)


Let's produce sounds !

The schematics will use is exactly the same as on the previous post with the LED, except the LED is replaced with a piezo buzzer, like this:




By the way, how to observe the "duty cycle effect" on the volume ? Just program your PIC with the previous experiment one, which control the brightness of a LED, and power on the circuit. I wanted to show a video with sounds, but the frequency is too high, my camera can't record it...

Anyway, that's a little bit boring, we do want sounds...


Writing the software


The software part has a lot of similarities with the previous experiment. The initialization is the same, I let you have a look. Only the forever loop has changed:

var dword counter = 0
forever loop

for 100_000 using counter loop
pwm_set_frequency(counter)
-- Setting @50% gives max volume
-- must be re-computed each time the frequency
-- changes, because it depends on PR2 value
pwm1_set_percent_dutycycle(50)
end loop

end loop

Quite straightforward:
  • we "explore" frequencies between 0 and 100 000 Hz, using a counter
  • we use pwm_set_frequency(counter) to set the frequency, in Hertz. It takes a dword as parameter (ie. you can explore a lot of frequencies...)
  • finally, as we want a 50% duty cycle, and since its value is different for each frequency setting, we need to re-compute it on each loop.
Note: jallib's PWM libraries are coming from a "heavy refactoring" of Guru Stef Mientki's PWM library. While integrating it to jallib, we've modified the library so frequencies can be set and changed during program execution. This wasn't the case before, because the frequency was set as a constant.


So, how does this look like ? Hope you'll like the sweet melody :)





"Where can I download the files ?"

As usual, you'll need the last jallib pack (at least 0.2 version). You'll also find the exact code we used here.




Sébastien Lelong

Saturday, February 7, 2009

Step by Step: having fun with PWM and a LED (part 1)

Part 1 : controlling the brightness of a LED


In this "Step-by-Step" tutorial, we're going to (try to) have some fun with PWM. PWM stands for Pulse Width Modulation, and is quite weird when you first face this (this was at least my first feeling).


So, how does PWM look like ?...

PWM is about switching one pin (or more) high and low, at different frequencies and duty cycles. This is a on/off process. You can either vary:
  • the frequency,
  • or the duty cycle, that is the proportion where the pin will be high


Both have a 50% duty cycle (50% on, 50% off),
but the upper one's frequency is twice the bottom



Three different duty cycle (10%, 50% and 90%),
all at the same frequency


But what is PWM for ? What can we do with it ? Many things, like:
  • producing variable voltage (to control DC motor speed, for instance)
  • playing sounds: duty cycle is constant, frequency is variable
  • playing PCM wave file (PCM is Pulse Code Modulation)
  • ...


One PWM channel + one LED = fun

For now, and for this first part, we're going to see how to control the brightness of a LED. If simply connected to a pin, it will light at its max brightness, because the pin is "just" high (5V).

Now, if we connect this LED on a PWM pin, maybe we'll be able to control the brightness: as previously said, PWM can be used to produce variable voltages. If we provide half the value (2.5V), maybe the LED will be half its brightness (though I guess the relation between voltage and brightness is not linear...). Half the value of 5V. How to do this ? Simply configure the duty cycle to be 50% high, 50% low.

But we also said PWM is just about switching a pin on/off. That is, either the pin will be 0V, or 5V. So how will we be able to produce 2.5V ? Technically speaking, we won't be able to produce a real 2.5V, but if PWM frequency is high enough, then, on the average, and from the LED's context, it's as though the pin outputs 2.5V.


Building the whole

Enough theory, let's get our hands dirty. Connecting a LED to a PWM pin on a 16f88 is quite easy. This PIC has quite a nice feature about PWM, it's possible to select which pin, between RB0 and RB3, will carry the PWM signals. Since I use tinybootloader to upload my programs, and since tiny's fuses are configured to select the RB0 pin, I'll keep using this one (if you wonder why tinybootloader interferes here, read this post).




On a breadboard, this looks like this:


The connector brings +5V on the two bottom lines
(+5V on line A, ground on line B).


LED is connected to RB0


Writing the software

For this example, I took one of the 16f88's sample included in jallib distribution (16f88_pwm_led.jal), and just adapt it so it runs at 8MHz, using internal clock. It also select RB0 as the PWM pin.

So, step by step... First, as we said, we must select which pin will carry the PWM signals...

pragma target CCP1MUX RB0 -- ccp1 pin on B0

and configure it as output

var volatile bit pin_ccp1_direction is pin_b0_direction
pin_ccp1_direction = output
-- (simply "pin_b0_direction = output" would do the trick too)

Then we include the PWM library.

include pwm_hardware

Few words here... This library is able to handle up to 5 PWM channels (PIC using CCP1, CCP2, CCP3, CCP4 and CCP5 registers). Using conditional compilation, it automatically selects the appropriate underlying PWM libraries, for the selected target PIC.

Since 16f88 has only one PWM channel, it just includes "pwm_ccp1" library. If we'd used a 16f877, which has two PWM channels, it would include "pwm_ccp1" and "pwm_ccp2" libraries. What is important is it's transparent to users (you).

OK, let's continue. We now need to configure the resolution. What's the resolution ? Given a frequency, the number of values you can have for the duty cycle can vary (you could have, say, 100 different values at one frequency, and 255 at another frequency). Have a look at the datasheet for more.

What we want here is to have the max number of values we can for the duty cycle, so we can select the exact brightness we want. We also want to have the max frequency we can have (ie. no pre-scaler).

pwm_max_resolution(1)

If you read the jalapi documentation for this, you'll see that the frequency will be 7.81kHz (we run at 8MHz).

PWM channels can be turned on/off independently, now we want to activate our channel:

pwm1_on()

Before we dive into the forever loop, I forgot to mention PWM can be used in low or high resolution. On low resolution, duty cycles values range from 0 to 255 (8 bits). On high resolution, values range from 0 to 1024 (10 bits). In this example, we'll use low resolution PWM. For high resolution, you can have a look at the other sample, 16f88_pwm_led_highres.jal. As you'll see, there are very few differences.

Now let's dive into the loop...



forever loop
var byte i
i = 0
-- loop up and down, to produce different duty cycle
while i < 250 loop
pwm1_set_dutycycle(i)
_usec_delay(10000)
i = i + 1
end loop
while i > 0 loop
pwm1_set_dutycycle(i)
_usec_delay(10000)
i = i - 1
end loop
-- turning off, the LED lights at max.
_usec_delay(500000)
pwm1_off()
_usec_delay(500000)
pwm1_on()

end loop


Quite easy right ? There are two main waves: one will light up the LED progressively (0 to 250), another will turn it off progressively (250 to 0). On each value, we set the duty cycle with pwm1_set_dutycycle(i) and wait a little so we, humans, can see the result.

About the result, how does this look like ?





"I wanna try, where are the files ?"

To run this sample, you'll need the last jallib pack (at least 0.2 version). You'll also find the exact code we used here.



Sébastien Lelong

Sunday, February 1, 2009

License

As we continue to populate this blog, maybe it's time to put a license on its content. We, at jallib, want it to be as open as possible, as free as possible. We decided to use a Creative Common license.



So, the blog's content is licensed under the Creative Common "Attribution Share Alike" license. For more information, see the Creative Common's summary and the full legal code.

Of course, this license only applies on the blog's content itself (text, photos, diagrams, schematics, videos, etc...), except when another license is explicitly used. Particularly, there are examples and code coming from jallib project, which are released under ZLIB or BSD licenses.

In doubts, just ask on jallib's group.


Have fun !

The jallib team