I received today my cyclone IV basic dev board and I immediately tested the "my first FPGA" tutorial available on the Altera website.
You can find it here and it can be a very good way to test your new board and/or your software setup.
I followed the step by step process and I must say I understood most of it (still a few grey areas about timing constraints... need to review them a bit and check some literature maybe).
I had to cross reference the schematics of my board to identify the pins, took me a while -ehm, like 2 or 3 minutes actually- to identify the correct pin for the oscillator signal (it was not displayed on the main chip in the schematics).
Lol, I just noticed that on the board itself you can read "Clk=Pin25" and it is even printed in a quite visible font :)
As a general suggestion, if you buy a generic cheap board like I did, make sure you can find and download the schematics, you will definitely need them for the pin mapping!!
Anyhow, it all sounded pretty new to me, so I was quite sure there were about zero chances to see the binary counter running at the first shot.
Turns out I was wrong (oh, I really love to be wrong this way!! :) ).
100% completed, green is nice, but what actually was shocking was the view of the binary magic on the leds installed on the board (yeah, we geeks can spend hours watching a binary led clock).
I know, this post was not that informative, just wanted to share that I am happy that my FPGA "hello world" worked.
That's the beauty of technology after all (for those who love it), it can surprise you every single day and you can count on the fact that tomorrow you may start playing with something new you don't know anything about.
Ain't it amazing?
An LED bus
blinks its four bits,
the MSB goes off, it starts again.
All in all happiness
is something little (that blinks)
Originally Trilussa (a poet from Rome) wrote :
A bee settled
on a rose petal.
It sipped, and off it flew.
All in all happiness
is something little
Search Microcontrollers
Thursday, March 27, 2014
Sunday, March 23, 2014
My first FPGA is on its way
I couldn't resist.
I am already busy with many other things (but my laser is now sat up in a lab some 100s Km from me now, to be used to run some tests), but it's quite some time I am curious about FPGAs.
Every now and them I found myself thinking : "I should get myself an FPGA DEV board and start playing with it", then I would normally try to stick my fingers in my ears and say out loud : "lalalalalalala!".
It actually worked for a while, till a few days ago when I bought a Cyclone IV dev board.
Argh, I knew it would have happened sooner or later, I wonder if there is some kind of therapy for us geeks, to keep our urges of technology under control.
Funny enough this is one of the times where I do not have an application in mind that might justify the acquisition of the mentioned technology... it is simply curiosity at its finest.
I started reading about FPGA, downloaded the free software from the Altera website while my board is traveling from mighty China to the Swiss mountains where I play with cheese, lasers and bits.
So, if you know already a bit FPGAs, then you will not find a lot of valuable information here : I am just discovering (still you are welcome to post your comments, they might help me out in my discovery process).
On the other hand, if you are like myself, maybe a bit skilled with MCUs etc, but never played with FPGAs, then this article might actually spare you a bit of googling around and help you decide if you want to give them a try or not.
Field Programmable Gate Arrays are "a different animal" if you are coming from microprocessors or MCUs.
These devices don't come with any specific functionality.
Sounds weird, right?
I mean, when you buy an MCU, you select it for the number of bits it has, the clock frequency, internal peripherals such as serial ports, ADCs, timers etc,
None of that is available in an FPGA.
So, what do you get?
You should expect a high number of pins that can be configured as inputs or outputs, cool, but honestly not really enough.
Then you can connect those pins internally with sequences of logic gates (remember the old 74xx TTL stuff? Those, but smaller and with no need of wires or pcb traces to connect them!).
I know it does not seem much, but it turns out to be a big deal in the end.
Now, imagine you have several thousands of those Logic Elements (LE), which to be fair are a bit more flexible that the old 74xx gates.
You know that a microprocessor is just a bunch of those properly connected, right?
Technically these LEs are arranged in an Array and an internal RAM stores how they should be configured and how they should be interconnected.
Did I say RAM?
Yeah, I did, and that does mean that every time you turn off an FPGA it goes back to be a bunch of non configured and non connected LEs just the same way it was shipped out of the production line.
This means you need an external EEPROM or similar device to store the configuration that allows the functionality you planned for your project and, when the FPGA chip is powered up, this configuration data is sent with a serial protocol.
In the end, programming an FPGA simply means storing the configuration data into the non volatile memory that will feed the device at power on.
With that clarified, how do we create a complex logic (i.e. a microprocessor) using just basic gates?
I am sure there are out there skilled professional that could design a CoreI7 with a bunch of 74xx chips, and actually that would be one way (not the easiest) to go.
In fact you can design the schematics and place all your lovely AND, NAND, XOR etc in it, place the wires, connect stuff to pins and probably invent the next gen CPU with that.
I surely hope this is not the only or easiest way to go... apparently my hopes are not placed in vain.
It is possible to "describe" the hardware using a Hardware Description Language such as Verilog or VHDL.
If you are reading this post, chances are you are familiar on how source code normally looks like, so you could see the example below (VHDL snippet) and still feel you are in your comfort zone.
Ok, cool, we have code blocks with begin / end (Pascal anyone?), we have IFs.. meh, looks like source code.
What's special there?
I guess many things, but the one that shocked me at first is that instructions are not necessarily executed in a sequence.
Let me give you an example :
Imagine you are using an MCU, reacting to an Interrupt, you received a command over the UART and now you want to turn on an LED on pin P1.1, check the value of pin P2.1 and if this is equal to 1 then turn on a fan connected to pin P1.2.
So you would get something like this :
..
gpio_P1_1 = 1;
if (gpio_P2_1==1) gpio_P1_2 = 1;
Technically this gets compiled and a series of instructions are executed sequentially, if we use the debugger we can clearly see thew first line being executed and the LED turning on, then the second one and eventually the fan turning on.
What happened in our MCU is that the same hardware (registers, whatever) meaning the same transistors in the chip where used to execute the two lines and when they were busy taking care of line 1, line 2 was waiting.
In an FPGA you do not (necessarily) create a sequence of instructions, so the same task would be accomplished by describing what should happen when the command is received.
The two lines would be executed simultaneously because they would actually be implemented by separate transistors in the chip.
Those transistors would have one single job : to deal with that specific instruction, anytime needed and regardless of any kind of clock.
A block of gates, interconnected to absolve some dedicated function is then considered a sort of black box with inputs and outputs that allow it to interact in a bigger design,
Basic and complex blocks are available in libraries, which also contain a free microprocessor (called NIOS II) that can be implemented in your design, you can even run a Linux Kernel on it (yup, with a bunch of 74xx style toys, not bad eh?).
To embed some specific and existing designs you may need to pay licenses (Intellectual Property) to the creator, at this time I am not aware of that ecosystem, so it might very well be that you can find free stuff for most of the needs or not.
Normally FPGA devices include also some memory blocks (RAM) and PLL circuitry to create clock signals you may need for the logic you implement.
But then if an FPGA is in general an empty canvas, why are there so many models?
There are different manufactures (not many actually) each one of them offering different families of devices.
Every family has some peculiarity : power consumption, input output configuration (some have high speed transducers), maximum speed achievable, eventual internal HW CPU (typically a cortex A9 or similar) embedded etc.
Then , within a family there are plenty of models which have two main variables :
Number of LEs and Speed Grade.
Why number of LEs is important?
My dev board is equipped with a small Cyclone IV (Cyclone IV is the family), I am saying small because it has only about 6200 LEs, while the big devices might have 350.000 of them (115.000 max currently in the Cyclone IV family).
The more complex your design is, the more logic it needs which translates in a higher number of LEs required.
I think I found somewhere that, depending which configuration you need, a NIOS II processor would require from about 600 LEs.
[more info here]
Now, if you consider that my small device has 6200 of them, you can put things into scale.
The second parameter is the speed grade, I imagine that would greatly affect the maximum clock frequency you could use in your internal logic (i.e. in the NIOS II processor).
There are then some other details such as number of PLL circuits, number of internally available memory blocks etc.
The beauty of this is that, say you start your design with a small device and then it gets more complex over time, you just need to switch to a device that has more LEs (might impact your pcb tho, as number of pins might also change).
In conclusion, I cannot say I learnt a lot so far, but the few things I saw are making me more and more curious and eager to start experimenting.
I found the Altera website quite good to find resources, there is a training section which has plenty of good (and free) online training resources.
I cannot endorse one brand or another, looks like for beginners the two most obvious options are Altera and Xilinx, and I decided I'd give Altera a go first as there are plenty of cheap and simple DEV boards on the market, lot's of literature etc.
I think that even porting from a brand to another might be quite easy, since by itself an FPGA does not provide any specific functionality, you just need enough room for your design, silicon that reacts fast enough and eventually amenities such as RAM and PLLs.
This article is already quite long, so I am cutting it here and I am not describing how the LEs work (which is quite fascinating), how they use the LUT etc.
While I might expand that in a future post, if you are curious, you can find a lot of info here
Update : I just received the board, you can see it here (top board) compared with the Stellaris Launchpad (bottom one).
The USB Blaster manages the JTAG interface, tiny device actually and you need to install the driver found in the Quartus software distribution (also in the free one) as explained here
Should you be interested, you can find some boards here :
I am already busy with many other things (but my laser is now sat up in a lab some 100s Km from me now, to be used to run some tests), but it's quite some time I am curious about FPGAs.
Every now and them I found myself thinking : "I should get myself an FPGA DEV board and start playing with it", then I would normally try to stick my fingers in my ears and say out loud : "lalalalalalala!".
It actually worked for a while, till a few days ago when I bought a Cyclone IV dev board.
Argh, I knew it would have happened sooner or later, I wonder if there is some kind of therapy for us geeks, to keep our urges of technology under control.
Funny enough this is one of the times where I do not have an application in mind that might justify the acquisition of the mentioned technology... it is simply curiosity at its finest.
I started reading about FPGA, downloaded the free software from the Altera website while my board is traveling from mighty China to the Swiss mountains where I play with cheese, lasers and bits.
So, if you know already a bit FPGAs, then you will not find a lot of valuable information here : I am just discovering (still you are welcome to post your comments, they might help me out in my discovery process).
On the other hand, if you are like myself, maybe a bit skilled with MCUs etc, but never played with FPGAs, then this article might actually spare you a bit of googling around and help you decide if you want to give them a try or not.
Field Programmable Gate Arrays are "a different animal" if you are coming from microprocessors or MCUs.
These devices don't come with any specific functionality.
Sounds weird, right?
I mean, when you buy an MCU, you select it for the number of bits it has, the clock frequency, internal peripherals such as serial ports, ADCs, timers etc,
None of that is available in an FPGA.
So, what do you get?
You should expect a high number of pins that can be configured as inputs or outputs, cool, but honestly not really enough.
Then you can connect those pins internally with sequences of logic gates (remember the old 74xx TTL stuff? Those, but smaller and with no need of wires or pcb traces to connect them!).
I know it does not seem much, but it turns out to be a big deal in the end.
Now, imagine you have several thousands of those Logic Elements (LE), which to be fair are a bit more flexible that the old 74xx gates.
You know that a microprocessor is just a bunch of those properly connected, right?
Technically these LEs are arranged in an Array and an internal RAM stores how they should be configured and how they should be interconnected.
Did I say RAM?
Yeah, I did, and that does mean that every time you turn off an FPGA it goes back to be a bunch of non configured and non connected LEs just the same way it was shipped out of the production line.
This means you need an external EEPROM or similar device to store the configuration that allows the functionality you planned for your project and, when the FPGA chip is powered up, this configuration data is sent with a serial protocol.
In the end, programming an FPGA simply means storing the configuration data into the non volatile memory that will feed the device at power on.
With that clarified, how do we create a complex logic (i.e. a microprocessor) using just basic gates?
I am sure there are out there skilled professional that could design a CoreI7 with a bunch of 74xx chips, and actually that would be one way (not the easiest) to go.
In fact you can design the schematics and place all your lovely AND, NAND, XOR etc in it, place the wires, connect stuff to pins and probably invent the next gen CPU with that.
I surely hope this is not the only or easiest way to go... apparently my hopes are not placed in vain.
It is possible to "describe" the hardware using a Hardware Description Language such as Verilog or VHDL.
If you are reading this post, chances are you are familiar on how source code normally looks like, so you could see the example below (VHDL snippet) and still feel you are in your comfort zone.
Ok, cool, we have code blocks with begin / end (Pascal anyone?), we have IFs.. meh, looks like source code.
What's special there?
I guess many things, but the one that shocked me at first is that instructions are not necessarily executed in a sequence.
Let me give you an example :
Imagine you are using an MCU, reacting to an Interrupt, you received a command over the UART and now you want to turn on an LED on pin P1.1, check the value of pin P2.1 and if this is equal to 1 then turn on a fan connected to pin P1.2.
So you would get something like this :
..
gpio_P1_1 = 1;
if (gpio_P2_1==1) gpio_P1_2 = 1;
Technically this gets compiled and a series of instructions are executed sequentially, if we use the debugger we can clearly see thew first line being executed and the LED turning on, then the second one and eventually the fan turning on.
What happened in our MCU is that the same hardware (registers, whatever) meaning the same transistors in the chip where used to execute the two lines and when they were busy taking care of line 1, line 2 was waiting.
In an FPGA you do not (necessarily) create a sequence of instructions, so the same task would be accomplished by describing what should happen when the command is received.
The two lines would be executed simultaneously because they would actually be implemented by separate transistors in the chip.
Those transistors would have one single job : to deal with that specific instruction, anytime needed and regardless of any kind of clock.
A block of gates, interconnected to absolve some dedicated function is then considered a sort of black box with inputs and outputs that allow it to interact in a bigger design,
Basic and complex blocks are available in libraries, which also contain a free microprocessor (called NIOS II) that can be implemented in your design, you can even run a Linux Kernel on it (yup, with a bunch of 74xx style toys, not bad eh?).
To embed some specific and existing designs you may need to pay licenses (Intellectual Property) to the creator, at this time I am not aware of that ecosystem, so it might very well be that you can find free stuff for most of the needs or not.
Normally FPGA devices include also some memory blocks (RAM) and PLL circuitry to create clock signals you may need for the logic you implement.
But then if an FPGA is in general an empty canvas, why are there so many models?
There are different manufactures (not many actually) each one of them offering different families of devices.
Every family has some peculiarity : power consumption, input output configuration (some have high speed transducers), maximum speed achievable, eventual internal HW CPU (typically a cortex A9 or similar) embedded etc.
Then , within a family there are plenty of models which have two main variables :
Number of LEs and Speed Grade.
Why number of LEs is important?
My dev board is equipped with a small Cyclone IV (Cyclone IV is the family), I am saying small because it has only about 6200 LEs, while the big devices might have 350.000 of them (115.000 max currently in the Cyclone IV family).
The more complex your design is, the more logic it needs which translates in a higher number of LEs required.
I think I found somewhere that, depending which configuration you need, a NIOS II processor would require from about 600 LEs.
[more info here]
Now, if you consider that my small device has 6200 of them, you can put things into scale.
The second parameter is the speed grade, I imagine that would greatly affect the maximum clock frequency you could use in your internal logic (i.e. in the NIOS II processor).
There are then some other details such as number of PLL circuits, number of internally available memory blocks etc.
The beauty of this is that, say you start your design with a small device and then it gets more complex over time, you just need to switch to a device that has more LEs (might impact your pcb tho, as number of pins might also change).
In conclusion, I cannot say I learnt a lot so far, but the few things I saw are making me more and more curious and eager to start experimenting.
I found the Altera website quite good to find resources, there is a training section which has plenty of good (and free) online training resources.
I cannot endorse one brand or another, looks like for beginners the two most obvious options are Altera and Xilinx, and I decided I'd give Altera a go first as there are plenty of cheap and simple DEV boards on the market, lot's of literature etc.
I think that even porting from a brand to another might be quite easy, since by itself an FPGA does not provide any specific functionality, you just need enough room for your design, silicon that reacts fast enough and eventually amenities such as RAM and PLLs.
This article is already quite long, so I am cutting it here and I am not describing how the LEs work (which is quite fascinating), how they use the LUT etc.
While I might expand that in a future post, if you are curious, you can find a lot of info here
Update : I just received the board, you can see it here (top board) compared with the Stellaris Launchpad (bottom one).
The USB Blaster manages the JTAG interface, tiny device actually and you need to install the driver found in the Quartus software distribution (also in the free one) as explained here
Should you be interested, you can find some boards here :
Friday, March 14, 2014
2.4W 808nm Infrared Laser
I bought on ebay a laser diode because I need to run some experiments on plastic materials.
Being quite new to the game, I did some research and tests, so I am documenting here what I understood as it might be useful for others.
Laser diodes are normally quite easy to use and, for low power and relatively high frequencies, they offer a good alternative to CO2 laser tubes.
For the application I have in mind I will need to use a 60W CO2 tube, but since I am not sure the concept is sound, before investing in that kind of material I wanted to proof the concept with cheaper and easier to use lasers.
There are two things that actually affect my test :
Obviously the power, for a cheap laser (about 300$ shipped, with driver, power supply and good protection glasses) there is no way you can compete witha 60W tube.
And then the frequency.
Non pigmented plastic is quite transparent to visible light, while it absorbs pretty well in the deep infrared range.
The diode I got is at 808nm which is a wavelength sitting in the "near infrared", it is absorbed by plastic materials only if they are colored, possibly black.
The deep infrared CO2 lasers work at about 10.000nm, which means they have a wavelength which is more than 10x the 808nm ones.
That makes a lot of difference.
Still, if I use black targets -which is kind of ok for my test- I should be able to achieve something.
Operating the laser itself is quite easy, just plug in the power supply connected to the driver (this one works at 12V) and it basically fires out right away.
WARNING : even few milliWatts can easily damage your eyes permanently, wearing proper protection goggles is mandatory.
No, it is not enough to make sure you are not firing the beam directly to your eyes, as the beam can bounce against any target and being scattered to your eyes, it just takes a small portion of the beam sometimes to be a big issue.
Also, make sure the goggles you use are specific for the frequency you are using : 404nm protection goggles would not help much if your laser is at 808nm.
Particularly, if you are using near infrared diodes, like mine, an additional danger comes from the fact that the beam is not easy to spot, since the frequency is almost invisible for the human eye (cameras can detect it way better).
That said, laser diodes can come with some different features :
Optics : depending on the power and frequency the lenses can be plastic or glass. For high power (more than 0.5 -1W) you normally have glass lenses, like mine.
Normally you should have some kind of regulation on the lenses, to adjust the focus, which is really important to properly use your laser.
Driver : Technically you apply the correct voltage to the diode and it emits the beam, pretty simple.
However a " driver" circuit is normally used to deliver a constant current to the diode (you can build yourself one of these circuits, there are plenty of schematics on the web normally based on the LM317 - just google the topic-) else your diode might have a short life span.
Power Control : The laser diode is an LED, like any LED it is possible to dim it using a PWM signal. For this reason many lasers come with two additional wires connected to the driver, they are labeled "TTL" and are supposed to be the PWM signal input.
I believe there is a pull-up resistor at the end of those wires (at least in MY driver) because if you do not provide any signal, then the laser is on 100%, if you shortcut them (grounding the signal, I take) the laser goes at 0% (off).
The ttl signal in my case is 5V, 3.3V compatible, so it can be driven by any MCU directly, the suggested frequency says >20KHz.
I completed my PWM setup (my stellaris pwm library here), but did not test it on the laser yet.
Cooling : Laser diodes tend to get hot and heat generates a lot of issues.
For starters it might actually damage your diode, but another effect is that the emitted frequency starts drifting off, presumably reducing the emitted power as well.
Typically you really want to run your laser at about ambient temperature, not exceeding too much 30C and if so not for long time.
Cooling becomes a real issue when you are planning to use the laser for long periods.
My laser is supposed to be able to operate at 100% for 30 minutes and then rest to cool down for 5 minutes, it just has a small fan.
I am staying on the safe side and operate it continuously for not more than 5 mins at the moment, but I ordered a few TECs (peltiers) which I plan to use to cool down the assembly.
Now, that said, the first thing I had to do, which proved to be a bit more complex than I expected, is to set the proper focus.
As you cannot see the beam when you are wearing protection goggles (and you DO NOT want to remove them), focusing the beam by looking at the target is pretty much impossible, maybe you can do something using a camera.
The other thing is that if you can chose the distance it is easier to set the lens focus and then move the target towards or away the laser until you get the desired focus.
But how do you detect the "desired focus" ?
I placed my laser on a table, taped down a reference with a mark every 5 cm, then moved some target objects on the reference.
The closer you are to the focus, the fastest the target object starts burning, I used a piece of wood for this rough measurement.
Once you get roughly the range in which you believe your focal distance is (between 40 and 55 cm for me), you can assume the correct value is somewhat in the middle.
I then took a black object and moving it I found that the best results were at 47cm, to prove that I let the beam punch a hole in my target, the smallest hole will be the one where the focus distance is optimal.
You can see the difference between the holes on the left and on the right, clearly the left one was way closer to the optimal distance.
At this point I knew roughly the range I would need, so I started building a stand that would allow me to run my tests in a more comfortable and repeatable way.
I came out with this :
It still allows me to move the diode up&down, is uses a glass work area where I can easily place my target objects and holds in place driver, power supply (and in the future the stellaris launchpad for the PWM)... granted, nothing fancy, but quite functional in the end.
One issue you may want to consider is how to attach the diode part as you may want to avoid something that prevents a good thermal dispersion (i.e. avoid to attach it to a wooden support that blocks the heat dispersion on one side).
Now I need to finish the interface program for the stellaris, it is written in Java and communicates via UART with the stellaris, the host computer will be an Olimex A20 running Debian Linux, that will give me a graphical interface to control the power, temperature drive eventual additional fans and peltier cells.
I am planning to record a video to demonstrate the rig, hopefully in few days.
Update : here's the video
Being quite new to the game, I did some research and tests, so I am documenting here what I understood as it might be useful for others.
Laser diodes are normally quite easy to use and, for low power and relatively high frequencies, they offer a good alternative to CO2 laser tubes.
For the application I have in mind I will need to use a 60W CO2 tube, but since I am not sure the concept is sound, before investing in that kind of material I wanted to proof the concept with cheaper and easier to use lasers.
There are two things that actually affect my test :
Obviously the power, for a cheap laser (about 300$ shipped, with driver, power supply and good protection glasses) there is no way you can compete witha 60W tube.
And then the frequency.
Non pigmented plastic is quite transparent to visible light, while it absorbs pretty well in the deep infrared range.
The diode I got is at 808nm which is a wavelength sitting in the "near infrared", it is absorbed by plastic materials only if they are colored, possibly black.
The deep infrared CO2 lasers work at about 10.000nm, which means they have a wavelength which is more than 10x the 808nm ones.
That makes a lot of difference.
Still, if I use black targets -which is kind of ok for my test- I should be able to achieve something.
Operating the laser itself is quite easy, just plug in the power supply connected to the driver (this one works at 12V) and it basically fires out right away.
WARNING : even few milliWatts can easily damage your eyes permanently, wearing proper protection goggles is mandatory.
No, it is not enough to make sure you are not firing the beam directly to your eyes, as the beam can bounce against any target and being scattered to your eyes, it just takes a small portion of the beam sometimes to be a big issue.
Also, make sure the goggles you use are specific for the frequency you are using : 404nm protection goggles would not help much if your laser is at 808nm.
Particularly, if you are using near infrared diodes, like mine, an additional danger comes from the fact that the beam is not easy to spot, since the frequency is almost invisible for the human eye (cameras can detect it way better).
That said, laser diodes can come with some different features :
Optics : depending on the power and frequency the lenses can be plastic or glass. For high power (more than 0.5 -1W) you normally have glass lenses, like mine.
Normally you should have some kind of regulation on the lenses, to adjust the focus, which is really important to properly use your laser.
Driver : Technically you apply the correct voltage to the diode and it emits the beam, pretty simple.
However a " driver" circuit is normally used to deliver a constant current to the diode (you can build yourself one of these circuits, there are plenty of schematics on the web normally based on the LM317 - just google the topic-) else your diode might have a short life span.
Power Control : The laser diode is an LED, like any LED it is possible to dim it using a PWM signal. For this reason many lasers come with two additional wires connected to the driver, they are labeled "TTL" and are supposed to be the PWM signal input.
I believe there is a pull-up resistor at the end of those wires (at least in MY driver) because if you do not provide any signal, then the laser is on 100%, if you shortcut them (grounding the signal, I take) the laser goes at 0% (off).
The ttl signal in my case is 5V, 3.3V compatible, so it can be driven by any MCU directly, the suggested frequency says >20KHz.
I completed my PWM setup (my stellaris pwm library here), but did not test it on the laser yet.
Cooling : Laser diodes tend to get hot and heat generates a lot of issues.
For starters it might actually damage your diode, but another effect is that the emitted frequency starts drifting off, presumably reducing the emitted power as well.
Typically you really want to run your laser at about ambient temperature, not exceeding too much 30C and if so not for long time.
Cooling becomes a real issue when you are planning to use the laser for long periods.
My laser is supposed to be able to operate at 100% for 30 minutes and then rest to cool down for 5 minutes, it just has a small fan.
I am staying on the safe side and operate it continuously for not more than 5 mins at the moment, but I ordered a few TECs (peltiers) which I plan to use to cool down the assembly.
Now, that said, the first thing I had to do, which proved to be a bit more complex than I expected, is to set the proper focus.
As you cannot see the beam when you are wearing protection goggles (and you DO NOT want to remove them), focusing the beam by looking at the target is pretty much impossible, maybe you can do something using a camera.
The other thing is that if you can chose the distance it is easier to set the lens focus and then move the target towards or away the laser until you get the desired focus.
But how do you detect the "desired focus" ?
I placed my laser on a table, taped down a reference with a mark every 5 cm, then moved some target objects on the reference.
The closer you are to the focus, the fastest the target object starts burning, I used a piece of wood for this rough measurement.
Once you get roughly the range in which you believe your focal distance is (between 40 and 55 cm for me), you can assume the correct value is somewhat in the middle.
I then took a black object and moving it I found that the best results were at 47cm, to prove that I let the beam punch a hole in my target, the smallest hole will be the one where the focus distance is optimal.
You can see the difference between the holes on the left and on the right, clearly the left one was way closer to the optimal distance.
At this point I knew roughly the range I would need, so I started building a stand that would allow me to run my tests in a more comfortable and repeatable way.
I came out with this :
It still allows me to move the diode up&down, is uses a glass work area where I can easily place my target objects and holds in place driver, power supply (and in the future the stellaris launchpad for the PWM)... granted, nothing fancy, but quite functional in the end.
One issue you may want to consider is how to attach the diode part as you may want to avoid something that prevents a good thermal dispersion (i.e. avoid to attach it to a wooden support that blocks the heat dispersion on one side).
Now I need to finish the interface program for the stellaris, it is written in Java and communicates via UART with the stellaris, the host computer will be an Olimex A20 running Debian Linux, that will give me a graphical interface to control the power, temperature drive eventual additional fans and peltier cells.
I am planning to record a video to demonstrate the rig, hopefully in few days.
Update : here's the video
Sunday, March 2, 2014
Stellaris Launchpad - PWM Library
I played a bit with PWM, which I actually need to drive a laser diode (2.4W 808nm) at the moment (will dig into this probably more in future posts).
Once you get the basics PWM is not difficult with the LM4F, however I found way easier for my application to create a minimalist library to deal with PWM signals, each time I look at it I keep repeating to myself that it could be better... but the way it is now already fits my needs (and maybe yours, but feel free to improve it and let me know :) ).
One thing I wanted was to have a simple interface that would allow me to play with multiple PWM generators at the same time, 8 seemed a fair amount, so I pre-allocated an array of 8, but it is fairly easy to expand the array.
Meh, some range checking would help maybe, but the Stellaris does not run on MS Windows, so it does pretty much what you tell it to do, just be careful not to add more than 8 pwm or at least remember to expand the array and you should be fine.
That said, each pwm generator is actually coming from a timer, we can use 16 bit or 32 bit timers and I figured that a 16 bit timer would do (did not try yet, but I think I found how to use it in 32 bit mode).
Still I did not implement pre-scaling which could be beneficial if you have to run with very low frequencies (<2Hz).
Here's the library (don' t expect anything fancy, it's just easy to use and actually works)
#include <inc/hw_memmap.h>
#include <inc/hw_types.h>
#include <driverlib/gpio.h>
#include <driverlib/sysctl.h>
#include <driverlib/uart.h>
#include <inc/hw_timer.h>
#include <driverlib/timer.h>
#include <driverlib/pin_map.h>
int pwm_count = 0;
int pwmtimers[8][4];
/**
* each timer can send the signal out to 2 different gpios
* 0 means to set it to the lowest of the two 1 to the highest
*
* TIMER PIN(gpio=0) PIN(1)
* T0A PB6 PF0
* T0B PB7 PF1
* T1A PB4 PF2
* T1B PB5 PF3
* T2A PB0 PF4
* T2B PB1 PB1
* T3A PB2 PB2
* T3B PB3 PB3
*
*
*
*/
int addPWM(int pwm_timer,int subtimer, int gpio) {
pwmtimers[pwm_count][0] = pwm_timer;
pwmtimers[pwm_count][1] = subtimer;
if (gpio == 0) {
if ((int)subtimer == (int)TIMER_A) {
switch (pwm_timer) {
case TIMER0_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_6;
pwmtimers[pwm_count][3] = GPIO_PB6_T0CCP0;
break;
case TIMER1_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_4;
pwmtimers[pwm_count][3] = GPIO_PB4_T1CCP0;
break;
case TIMER2_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_0;
pwmtimers[pwm_count][3] = GPIO_PB0_T2CCP0;
break;
case TIMER3_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_2;
pwmtimers[pwm_count][3] = GPIO_PB2_T3CCP0;
break;
}
} else {
switch (pwm_timer) {
case TIMER0_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_7;
pwmtimers[pwm_count][3] = GPIO_PB7_T0CCP1;
break;
case TIMER1_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_5;
pwmtimers[pwm_count][3] = GPIO_PB5_T1CCP1;
break;
case TIMER2_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_1;
pwmtimers[pwm_count][3] = GPIO_PB1_T2CCP1;
break;
case TIMER3_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_3;
pwmtimers[pwm_count][3] = GPIO_PB3_T3CCP1;
break;
}
}
} else if (subtimer == TIMER_A) {
switch (pwm_timer) {
case TIMER0_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_0 | 0x100; // port F
pwmtimers[pwm_count][3] = GPIO_PF0_T0CCP0;
break;
case TIMER1_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_2 | 0x100;
pwmtimers[pwm_count][3] = GPIO_PF2_T1CCP0;
break;
case TIMER2_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_4 | 0x100;
pwmtimers[pwm_count][3] = GPIO_PF4_T2CCP0;
break;
case TIMER3_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_2;
pwmtimers[pwm_count][3] = GPIO_PB2_T3CCP0;
break;
}
} else { // timer B
switch (pwm_timer) {
case TIMER0_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_1 | 0x100;
pwmtimers[pwm_count][3] = GPIO_PF1_T0CCP1;
break;
case TIMER1_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_3 | 0x100;
pwmtimers[pwm_count][3] = GPIO_PF3_T1CCP1;
break;
case TIMER2_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_1;
pwmtimers[pwm_count][3] = GPIO_PB1_T2CCP1;
break;
case TIMER3_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_3;
pwmtimers[pwm_count][3] = GPIO_PB3_T3CCP1;
break;
}
}
return pwm_count++;
}
/**
*
*/
void pwmStop(int pidx) {
TimerDisable(pwmtimers[pidx][0], pwmtimers[pidx][1]);
//enables timerx_y
}
/**
*
*/
void pwmStart(int pidx) {
TimerEnable(pwmtimers[pidx][0], pwmtimers[pidx][1]);
//enables timerx_y
}
/**
* Frequency in HZ and Duty cycle in %
* example : pwm_setFreqDC(0,25000,0.3) sets 25KHz with 30% duty cycle
*/
void pwmSetFreqDC(int idx, int freq,double dutyCycle) {
int period = (SysCtlClockGet() / freq);
int pidx = idx;
TimerLoadSet(pwmtimers[pidx][0],
pwmtimers[pidx][1], period - 1); // sets pwm period
TimerMatchSet(pwmtimers[pidx][0],
pwmtimers[pidx][1], period *dutyCycle); // sets duty cycle
}
/**
*
*/
void pwmConfigure(int idx) {
int timerp = 0;
// ENABLE gpio port
int gpioPort;
volatile int pidx = idx;
if ((pwmtimers[pidx][2] & 0x100) > 0) { // pidx changes value here ????
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
gpioPort = GPIO_PORTF_BASE;
} else {
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
gpioPort = GPIO_PORTB_BASE;
}
// configure gpio pin
GPIOPinTypeGPIOOutput(gpioPort, pwmtimers[pidx][2] & 0xff);
GPIOPadConfigSet(gpioPort, // identifies the GPIO PORT
pwmtimers[pidx][2] & 0xff, // identifies the pin within the GPIO Port
GPIO_STRENGTH_8MA_SC, // sets the strength to 8mA with Slew Rate control
GPIO_PIN_TYPE_STD); // sets the pad control type to push-pull
// clear output pin
GPIOPinWrite(gpioPort, pwmtimers[pidx][2] & 0xff, 0);
// setup timer
switch (pwmtimers[pidx][0]) {
case TIMER0_BASE:
timerp = SYSCTL_PERIPH_TIMER0;
break;
case TIMER1_BASE:
timerp = SYSCTL_PERIPH_TIMER1;
break;
case TIMER2_BASE:
timerp = SYSCTL_PERIPH_TIMER2;
break;
case TIMER3_BASE:
timerp = SYSCTL_PERIPH_TIMER3;
break;
}
SysCtlPeripheralEnable(timerp); // enables timer x
GPIOPinConfigure(pwmtimers[pidx][3]); // port x pin y set to TIMERz_k output
GPIOPinTypeTimer(gpioPort, pwmtimers[pidx][2] & 0xff); // port x pin y set to timer type output, does not bind
// configure Timer
if (pwmtimers[pidx][1] == TIMER_A)
TimerConfigure(pwmtimers[pidx][0],
(TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_PWM));
else
TimerConfigure(pwmtimers[pidx][0],
(TIMER_CFG_SPLIT_PAIR | TIMER_CFG_B_PWM));
// 16 bit timer TIMERx_y set to PWM mode
TimerControlLevel(pwmtimers[pidx][0], pwmtimers[pidx][1], 1);
// invert the signal for TIMERx_y
}
Once you get the basics PWM is not difficult with the LM4F, however I found way easier for my application to create a minimalist library to deal with PWM signals, each time I look at it I keep repeating to myself that it could be better... but the way it is now already fits my needs (and maybe yours, but feel free to improve it and let me know :) ).
One thing I wanted was to have a simple interface that would allow me to play with multiple PWM generators at the same time, 8 seemed a fair amount, so I pre-allocated an array of 8, but it is fairly easy to expand the array.
Meh, some range checking would help maybe, but the Stellaris does not run on MS Windows, so it does pretty much what you tell it to do, just be careful not to add more than 8 pwm or at least remember to expand the array and you should be fine.
That said, each pwm generator is actually coming from a timer, we can use 16 bit or 32 bit timers and I figured that a 16 bit timer would do (did not try yet, but I think I found how to use it in 32 bit mode).
Still I did not implement pre-scaling which could be beneficial if you have to run with very low frequencies (<2Hz).
Here's the library (don' t expect anything fancy, it's just easy to use and actually works)
#include <inc/hw_memmap.h>
#include <inc/hw_types.h>
#include <driverlib/gpio.h>
#include <driverlib/sysctl.h>
#include <driverlib/uart.h>
#include <inc/hw_timer.h>
#include <driverlib/timer.h>
#include <driverlib/pin_map.h>
int pwm_count = 0;
int pwmtimers[8][4];
/**
* each timer can send the signal out to 2 different gpios
* 0 means to set it to the lowest of the two 1 to the highest
*
* TIMER PIN(gpio=0) PIN(1)
* T0A PB6 PF0
* T0B PB7 PF1
* T1A PB4 PF2
* T1B PB5 PF3
* T2A PB0 PF4
* T2B PB1 PB1
* T3A PB2 PB2
* T3B PB3 PB3
*
*
*
*/
int addPWM(int pwm_timer,int subtimer, int gpio) {
pwmtimers[pwm_count][0] = pwm_timer;
pwmtimers[pwm_count][1] = subtimer;
if (gpio == 0) {
if ((int)subtimer == (int)TIMER_A) {
switch (pwm_timer) {
case TIMER0_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_6;
pwmtimers[pwm_count][3] = GPIO_PB6_T0CCP0;
break;
case TIMER1_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_4;
pwmtimers[pwm_count][3] = GPIO_PB4_T1CCP0;
break;
case TIMER2_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_0;
pwmtimers[pwm_count][3] = GPIO_PB0_T2CCP0;
break;
case TIMER3_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_2;
pwmtimers[pwm_count][3] = GPIO_PB2_T3CCP0;
break;
}
} else {
switch (pwm_timer) {
case TIMER0_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_7;
pwmtimers[pwm_count][3] = GPIO_PB7_T0CCP1;
break;
case TIMER1_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_5;
pwmtimers[pwm_count][3] = GPIO_PB5_T1CCP1;
break;
case TIMER2_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_1;
pwmtimers[pwm_count][3] = GPIO_PB1_T2CCP1;
break;
case TIMER3_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_3;
pwmtimers[pwm_count][3] = GPIO_PB3_T3CCP1;
break;
}
}
} else if (subtimer == TIMER_A) {
switch (pwm_timer) {
case TIMER0_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_0 | 0x100; // port F
pwmtimers[pwm_count][3] = GPIO_PF0_T0CCP0;
break;
case TIMER1_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_2 | 0x100;
pwmtimers[pwm_count][3] = GPIO_PF2_T1CCP0;
break;
case TIMER2_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_4 | 0x100;
pwmtimers[pwm_count][3] = GPIO_PF4_T2CCP0;
break;
case TIMER3_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_2;
pwmtimers[pwm_count][3] = GPIO_PB2_T3CCP0;
break;
}
} else { // timer B
switch (pwm_timer) {
case TIMER0_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_1 | 0x100;
pwmtimers[pwm_count][3] = GPIO_PF1_T0CCP1;
break;
case TIMER1_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_3 | 0x100;
pwmtimers[pwm_count][3] = GPIO_PF3_T1CCP1;
break;
case TIMER2_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_1;
pwmtimers[pwm_count][3] = GPIO_PB1_T2CCP1;
break;
case TIMER3_BASE:
pwmtimers[pwm_count][2] = GPIO_PIN_3;
pwmtimers[pwm_count][3] = GPIO_PB3_T3CCP1;
break;
}
}
return pwm_count++;
}
/**
*
*/
void pwmStop(int pidx) {
TimerDisable(pwmtimers[pidx][0], pwmtimers[pidx][1]);
//enables timerx_y
}
/**
*
*/
void pwmStart(int pidx) {
TimerEnable(pwmtimers[pidx][0], pwmtimers[pidx][1]);
//enables timerx_y
}
/**
* Frequency in HZ and Duty cycle in %
* example : pwm_setFreqDC(0,25000,0.3) sets 25KHz with 30% duty cycle
*/
void pwmSetFreqDC(int idx, int freq,double dutyCycle) {
int period = (SysCtlClockGet() / freq);
int pidx = idx;
TimerLoadSet(pwmtimers[pidx][0],
pwmtimers[pidx][1], period - 1); // sets pwm period
TimerMatchSet(pwmtimers[pidx][0],
pwmtimers[pidx][1], period *dutyCycle); // sets duty cycle
}
/**
*
*/
void pwmConfigure(int idx) {
int timerp = 0;
// ENABLE gpio port
int gpioPort;
volatile int pidx = idx;
if ((pwmtimers[pidx][2] & 0x100) > 0) { // pidx changes value here ????
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
gpioPort = GPIO_PORTF_BASE;
} else {
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
gpioPort = GPIO_PORTB_BASE;
}
// configure gpio pin
GPIOPinTypeGPIOOutput(gpioPort, pwmtimers[pidx][2] & 0xff);
GPIOPadConfigSet(gpioPort, // identifies the GPIO PORT
pwmtimers[pidx][2] & 0xff, // identifies the pin within the GPIO Port
GPIO_STRENGTH_8MA_SC, // sets the strength to 8mA with Slew Rate control
GPIO_PIN_TYPE_STD); // sets the pad control type to push-pull
// clear output pin
GPIOPinWrite(gpioPort, pwmtimers[pidx][2] & 0xff, 0);
// setup timer
switch (pwmtimers[pidx][0]) {
case TIMER0_BASE:
timerp = SYSCTL_PERIPH_TIMER0;
break;
case TIMER1_BASE:
timerp = SYSCTL_PERIPH_TIMER1;
break;
case TIMER2_BASE:
timerp = SYSCTL_PERIPH_TIMER2;
break;
case TIMER3_BASE:
timerp = SYSCTL_PERIPH_TIMER3;
break;
}
SysCtlPeripheralEnable(timerp); // enables timer x
GPIOPinConfigure(pwmtimers[pidx][3]); // port x pin y set to TIMERz_k output
GPIOPinTypeTimer(gpioPort, pwmtimers[pidx][2] & 0xff); // port x pin y set to timer type output, does not bind
// configure Timer
if (pwmtimers[pidx][1] == TIMER_A)
TimerConfigure(pwmtimers[pidx][0],
(TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_PWM));
else
TimerConfigure(pwmtimers[pidx][0],
(TIMER_CFG_SPLIT_PAIR | TIMER_CFG_B_PWM));
// 16 bit timer TIMERx_y set to PWM mode
TimerControlLevel(pwmtimers[pidx][0], pwmtimers[pidx][1], 1);
// invert the signal for TIMERx_y
}
---------------------
The addPWM(timer, subtimer, gpio) function allows you to select which timer you want to use.
To see which timers are available, you should use the lm4f datahseet or the summary table I placed at the beginning of the code, it should be quite easy to read.
For each timer/subtimer there are up to two output options in regard to the gpio pin to be used.
I sorted them alphabetically, so if for a timerxy you have options being PB2 and PF4 then PB2 would be 0 and PF4 1 in the gpio parameter of the function.
If the timer has one single configurable output, then both 0 and 1 would produce the same result.
This function just fills in the pwmtimers array, does not do anything with the hardware.
Say you call addPWM 3 times, the first one would be index 0 in the array, the second one index 1 and so on.
There is no functionality to remove an element from that array (did not see the point).
Once all the basic parameters are set (by addPWM) it is time to configure the needed hardware.
Relevant timer peripheral must be enabled, associated gpio port should be enabled too, the selected pin should be configured as output, set in timer mode and eventually pad-configured with high current push-pull with slew rate control.
The timer should be configured in split mode / pwm, also the pwm signal should be inverted (see previous article for a detailed explanation of these steps).
It is not difficult, but honestly i' d rather not code this process each time I need to use a pwm, so the pwmConfigure(idx) function takes care of ALL of it by fetching the needed values from the pwmtimers array.
Not really elegant maybe, but quite effective.
The idea here is that once you specified what a pwm signal should be, the next operation should just refer to the array index, like configure idx 0, set frequency and duty cycle for idx 0 , start idx 0 etc.
So, next in line comes the pwmSetFreqDC( idx, freq, dutyCycle) which allows us to set the frequency (you need to pass the number of hertz, like 25000 if you want 25KHz, no range checking is done, it' s up to you to pass a possible value) and the duty cycle as a fraction (i.e. 0.3 if you want 30%).
The you can start your signal with pwmStart(idx)
Let's see an example that drives the R,G and B components of the onboard LED.
---------------------
/*
* Stellaris LAUNCHPAD LM4F120H5QR
* ---> PART_LM4F120H5QR <---
*/
#include <inc/hw_memmap.h>
#include <inc/hw_types.h>
#include <driverlib/gpio.h>
#include <driverlib/sysctl.h>
#include <driverlib/uart.h>
#include <inc/hw_timer.h>
#include <driverlib/timer.h>
#include <driverlib/pin_map.h>
#include <utils/uartstdio.c>
void setClock()
{
SysCtlClockSet( SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL
| SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN);
}
int main(void) {
setClock();
// Add PWM timers to the library array
addPWM(TIMER0_BASE,TIMER_B,1); //0
addPWM(TIMER1_BASE,TIMER_A,1); //1
addPWM(TIMER1_BASE,TIMER_B,1); //2
// Configure Them
pwmConfigure(0);
pwmConfigure(1);
pwmConfigure(2);
// Set Frequency and duty cycle
pwmSetFreqDC(0,25000,0.9);
pwmSetFreqDC(1,25000,0.7);
pwmSetFreqDC(2,25000,0.3);
// Start the signal
pwmStart(0);
pwmStart(1);
pwmStart(2);
while (1) {
// do nothing, forever... looks like my dream life :)
}
}
--------------
When I was debugging it, I really stumbled against a stupid detail which I am going to tell you now, so you may avoid to think you are stupid and you should not deal with these things (because I did, at some point).
As you can see I am using all 3 components of the led, to debug my library I tried to use one , then two.
I tried to configure but not start them... but each time all 3 components were active.
I thought that the timers were starting as soon as they were configured, possibly with the frequency and DC I used before.
Why, if I did not start the green signal, the green component is there?
Turns out the answer is way easier than you' d think.
No, there is no green pwm signal, but the green light is on and the reason (my DSO confirmed it, I would have discovered this before if I was not too lazy to pull it out and probe the pins) is that the output is a steady HIGH.
Why?
I configured the pwm signals to be inverted, so when the timers are off, you should expect a steady high output, if this bothers you, then change it to not inverted in the TimerControlLevel call ( set to 0 the last parameter) and calculate the complementary value for the dutycycle in pwmSetFreqDC simply using (1-dutyCycle).
I tried and it worked (my DSO confirmed it, I love that instrument!).
I hope this simple library can be useful for you, if you are using another model of the lm4f, you might need to add some code to the addPWM function in order to consider the timers available in your device, the rest should probably be ok.
If you have improvements, I'd be glad to see them.
My next step will be to add UART or I2C control to drive the signals, again nothing fancy, but this is what I need :)
Happy coding!
Subscribe to:
Posts (Atom)