Category: "Code"

New toys! Davinci 3d printer!

05/07/14 | by Jfkfhhfj | Categories: Code

Wow, its been a while! 

 New toys!

So I’ve been slaving away at school as well as working at a gaming shop my brother, myself and a hanfdul of our friends have acquired. Check it out! http://www.legendaryrealmsgames.com/ . I've also acquired a handful of new toys including a CNC router,  Manual Lathe and 3d printer. So expect tons of posts on each of my new toys in the future as well as new services that i will be adding to my store: http://mcuplace.com/store/.

So, with the whole 3d printer movement, I’ve always kind of held back. I've always preferred the precision of a cnc mill over the lack of with a 3d printer. Add this with the ability to use any material a carbide endmill can chew through, cncs are just more refined than the repraps and makerbots that were coming out. Every one of my friends who had a 3d printer, never used it because it was either broken or didn’t meet their specs. Why spend $2000 on a 3d printer when the same amount can get you a cnc mill that allows you to make production grade parts? 

Today, things have changed a bit. This year is an exciting one for FDM printers as they have become ridiculously cheap and new materials allows some pretty interesting prototypes. The resolution still kinda sucks, but hey, for prototypes its great. 

Recently on hackaday, a few posts about the XYZ Davinci printer have popped up, and it's everything I could want. A consumer grade printer that uses real manufactured parts rather than printed crap that falls apart. 

The davinci printer, is a $500 printer that prints down to 100 micron, has a fairly decent sized bed and (hopefully) comes out of the box ready to print. Best of all, its sold through amazon, and is eligible for prime shipping.  Surely there must be a catch and there are, two of them.

 

Proprietary Filament.

First catch, is it used proprietary filament container, this is a moot point simply because the design is to make filament loading and use simpler for the lay person. The filament comes in a nice cartridge and there’s a small eeprom chip which tells the printer what material it is, the color, length of spool and material type. This allows seamless switching of print materials as the user has zero setting to adjust. However, these filament contains are pricey at $28 per 600g vs the standard $35 for 1kg. 

But we, are hackers and fooling the printer is as easy as cake, there are many guides out there on fooling the printer and resetting the filament count, allowing you to use any filament. 

http://hackaday.com/2014/04/10/resetting-drm-on-3d-printer-filament/

Honestly, their filament isn’t that much more expensive than aftermarket stuff, and for the time being i'd recommend just using their stuff until you get familiar with the printer. As im sure hacking the filament container voids any warranty. 

 

Smooth sailing?

The second major flaw with the printer is well... it doesn’t quite always work out of the box. I had two major issues with my printer and spend about an hour trying to get it working properly. Fortunately, support does exist, and you can always return through amazon easy as pie. So if you're patient, this can be a non issue. 

The y axis sensor.

When i first turned on the unit i was greeted to the sound of gears grinding. When the unit turns on, it does a quick calibration, where it homes the x and y axis. The x axis homes no problem, the y axis crashed into the machine and an error 0032 flashed on the screen. Looking in the manual its the code for y-axis home sensor/motor failure. I look inside the unit, i find the optoswitch for the y axis and then test the unit by tripping the optoswitch with a piece of paper, sue enough, the unit passes the start up test and i'm greeted by a ready state.

The fix begins with me taking off the side of the unit. If you lift up the front cover, you can pop the side off the unit, the back clips can come off by hand but one clip in the front you need something to pry it off with, a flat head screwdriver works fine, be careful you shouldn’t need to much force. 

After that the side should lift upwards.

The y axis sensor is attached to the side of the case with two screws. It looks like it was planned to make the sensor movable, but either the wrong pattern was stamped or the wrong mechanism was use. My y-axis sensor was fixed in position and properly mounted in the housing.

My solution was to simply tape a piece of paper to the y axis carriage. This gave the carriage just enough protrusion to hit the sensor. 

The Z axis cable.

The next error i came across was when i tried to do my first print. I selected the sample print and i was greeted by a "building"  message on the LCD, after about 5 minutes of nothing happening, an error code for z-axis sensor/motor not working flashed on the screen. I looked at the z-axis stepper and sure enough there was a cable housing with no cable attached. 

I removed the back of the unit and found the cable and proceeded to try and plug it in. One small problem....

The connector for the z axis is mounted UNDERNEETH the steel chassis, and to plug it in, you have to route the cable underneath. The problem with this is, there is no access hatch or way to do this WITHOUT taking the ENTIRE printer apart. So i was pretty annoyed at this. 

The solution was to plug the cable in from above, however this required taking the z-axis apart and lifting the stepper out.

To do this:

-Loosen the z axis coupler, on the STEPPER side with a m2 Allen wrench.

-Lift the bed and screw up, support it with some kind of block  (I had 2 123 block which i used)

-Loosen the two torx screws holding the stepper plate to the machine

-Lift the stepper up, and attach the cable

-Reattach the stepper to the machine, rest the coupler on the shaft and re tighten.

Everything should work after doing this.

The prints

Once you get printing, your rage starts to subside, the quality is pretty decent and for a $500 machine its quite awesome.  Their software is quite limiting, but any .stl file you can slice, and modify basic parameters, there are also ways to bypass the software completely.

http://hackaday.com/2014/04/17/using-non-crappy-software-with-the-da-vinci-printer/

I still think some fine tuning of the machine can be done, but in the coming weeks im sure there will be tons of write ups and hacks. Overall i think this is a great printer and an excellent device to get into 3d printing with. 

Overall im quite happy with the printer and already have some projects i will be printing parts for. 

 

If you have any further questions, feel free to leave a comment, if you are having issues with the same problems i had, send me an email and ill try and help you through your problems!

For all you shibes out there, comment with your doge address and ill send you 50 doge! 

Ps.. Main board shots

 UPDATES

So I am on my second filament cartridge, there are a few small issues i've encountered i'd like to document.

Calibration

I began having alot of issues with the levelness of my bed, while printing it was clearly misaligned and caused alot of bubbling on the first layers. Running through the leveling calibration improved prints alot, especially since i was able to get all 3 points exactly the same height.

Follow the procedures here: Yourtube video: xTKKcVMo7LU

It takes a good 30-45 minutes, but vastly improves print quality.

 

Running out of filament

The cartridges are suppose to tell you when they run out, but for some reason mine was mis calibrated, this meant that i ran out of filament midway through a print. Now this causes an issue when loading new filament because there is still old filament that cant be ejected. To fix this issue, squeeze the black clip at the top rear of the extruder, pull the housing off and pull out any remaining filament in the hotend. Once you do this, loading new filament should work fine. 

 

Squeeky x-axis

I've noticed that the x-axis on my unit squeeks quite alot, i dont know if this is a common issue and i've yet to resolve it.

 

 

Design of a switching motor controller

02/08/13 | by Jfkfhhfj | Categories: General, Code, Projects

Let me start by telling a little about myself. Ever since I arrived at Stony Brook I've been involved in the Solar Boat club. Were basically an organization that designs, builds and races solar powered boats. Now, when I tell people we build solar powered boats, they think RC race boats; no, we build typically around 14ft vessels meant for speed (At least we hope). Also by build, I mean we build pretty much everything from scratch including the hull, drive train and other systems. Anyway, not too long into when i joined the club i toyed with making our own motor controllers. Little did i know this design would take almost 2 years. What i want to now share with the everyone are the lessons that i have learned while working on this design. It began as something that i would expect to be done with quickly, but has turned into something which i still work on.

Motivation

So why build a motor controller? There are companies that already offer plenty of solutions for electric vehicles that will work in a variety of voltage ranges and currents right? Well the truth is, not really. At our competition all the controller are either.. AllTrax, Curtis or 4QD. And all of them either are: Stuck in the 70's in terms of logic and control signals or have a micro but don't actually allow you to usefully use the information they spit out. This being a custom race boat, we want specific information so knowing how the controller is operating at all times is crucial for our boat. That is, if we design the micro system that controls the controller, we can get whatever information we want and use it however we want. We would have digital freedom, which doesn't limit our design(Hello iPhone controlled solar boat?). Secondly, if you actually design a motor controller you know the specifications on the controller are completely artificial. Can a Curtis 1205 actually only source 400 amps? Well... no, the mosfet bank is rated for quite a bit more than that. But due to thermal limitations, they manufacturer sets these limits. Same goes for voltage, most controller have over-voltage ratings to limit the input voltage, despite the fact all the components on the inside are rated for much higher voltages. When you actually design a system from the ground up, you can set those limits and maximize performance of your system. You aren't limited to designing a product specifically for consumer use. You can test the limits of your hardware and learn more about your system in the long run.(for real world design, i suggest against this, but we are racing, so we can take some things to their limits in a blaze of glory :P) These two reasons were the main reasons why i decided to begin this design. Now i would like to go over a little bit about the basics of the design of a switching motor controller

The Basics

A switching motor controller, at its heart is super simple; We have a battery source, motor and mosfet bank like such:

 

When we apply a positive voltage on the gate(with respect to the source) The mosfets are fully conducting and the motor turns on. When we apply 0 or a negative voltage, the motors turn off. Yay! we finished our design and we can all go home, the controller works perfectly! Well... not really, we have 0 RPM and Max RPM, when the hell did you ever use a vehicle where you could only go full speed and 0 speed? The technology that we use is called pulse width modulation. I am not going to bore everyone with what it is, it's fairly simple. We have a square wave of frequency x and we vary the time that is is on, versus off. this is called the Duty Cycle and varies from 0 to 1. We can roughly estimate that Iavg = DC * Imax. Since we know P = t*w = I*v, with a constant supply voltage by changing the average current, we change the average power, and therefor for a constant torque load, we change the RPM.

To do this with any mircocontroller is fairly trivial, all decent micro's have some way of outputting a PWM wave at a specified frequency. For a PIC microcontroller, we use a single timer and we compare TMRx to the CCPRxH register, when they are equal, we have a transition from HI/LOW or LOW/HI (settable in the control register). When the TMRx register equals the PRx register we have another transitional and a new period starts. Operation is pretty simple, we have two registers, one for the Period of the wave PR2 and one for the Duty Cycle of the wave CCPRxH. The same is true for the AVR, its just different register names. So with this in mind our updated schematic is now: With this design, we have exactly what we want, we have a micro-controller that can adjust the average power going through a DC motor from a full stop to maximum RPMs. We can add various sensors to adjust the duty cycle and we can chose our mosfets however big to pass as much current we want! There is one small problem..... This setup would NEVER work.

Models and why they are important

A model is a way of representing a very complicated system with one which is much more manageable and well defined. An extreme example of a model is the law of gravitational forces:

This model allows us to tell what two forces an object will see on each other separated by a distance R. As far as well can tell, this is a very accurate model which works for most cases. However, it doesn't represent reality perfectly. We know that the law of gravity doesn't hold for the quantum scale and it especially doesn't hold true for black holes and, even when we calculate a force using this law, we only know this force to a certain amount of uncertainty. How fitting an equation the law of gravitational forces to nature is also a matter of interpretation. for a MUCH better explanation o this subject I recommend those to Richard Feynman's lecture on the character of physical law: http://research.microsoft.com/apps/tools/tuva/ The one thing that annoys me about many engineers is that many of them take a schematic as a golden rule. It exactly shows how a circuit is to operate and if you follow a schematic you will get precisely the operation you desire. This is simply not true. Lets take a simple system of a 9 volt battery with a resistor attached to it:

Now, we want to find the current so we use , great.  For most applications this works great, we find what we need and now we can move on with our design. However, lets think about this, what if R is 0? Well then , which is undefined, so that means every time you short your common 9v battery, universe should explode, no? How come it doesnt? Ok, R=0 is a little extreme, but you'll also find, even with small resistances where R!=0, the system wont behave as expected. The above schematic, is only a model to a certain degree, it doesn't tell us everything about the system. For most batteries, there is an internal resistance. This internal resistance, is in series with our load resistance and limits the total output current. We can add this resistance to our schematic and improve our model by a degree:

If we keep digging we can find more and more parameters we can add to our model. One important thing is to realize when we work with REAL components vs ideal components. What makes an inductor? an inductor is a coil of wire...a coil.. of WIRE. Every piece of wire in our circuit has an inductance. What makes a capacitor? two of anything with area, separated by a distance with a potential between them, everything has capacitance, and unless you're working with superconductors, everything has a resistance. These are called parasitic components and for the most part, they have such low values, we arent concerned since they don't affect our circuit in a region we are operating it. Lets add some of these components to our schematic and we get (and yes i know i may be "missing" a few):

Whats great about this model, is that well know exactly the frequency of oscillation when an incident photon strikes the body of the resistor creating a mobile electron.We can even go further! If we add quantum mechanics we'll have a decent idea of what any individuality electron might do. Do we need this? When making a model, you have to define what you NEED to know and you design your model accordingly, for 99% of us, our simple schematic will do. When your designs start failing, and you don't know why, chances are, its because of the parasitics. For high power electronics, the parasitics own your design and your job is to constantly fight them and try to tame them.

Where we stand

Our job is to take a look at our design and find where the parasitics are, the most obvious place to start looking is the electric motor. For our motor we use either a Lynch permanent magnet dc motor or a mars e-tek electric motor. If you're curious about motors check out:

its a good video with some well made diagrams and doesn't move at too fast a pace.  At the end of the day, the simplest model we can apply to our motor is an inductor in series with the armature resistance. Four our cabling we use 00 gauge welding wire, if you haven't worked with large gauge welding cable, its the most fantastic cable you can ever hope to work with, its awesome stuff, but i digress. Our wire has both inductance and resistance to it. Adding to original schematic we get:

 

 

Now to get back to the "switching" part of this design. We are using a bank of NMOS transistors to turn on and off the motor. When the bank is on, it has a resistance of RDSon/nmosfets . When it is off, the resistance is a few mega-ohms. When the mosfet is going from off to on, it is a resistance between those two. To minimize the power dissipated in the mosfets, we want to switch them as fast as possible. Once again we must look at the inside of a mosfet and understand what makes it tick. A Metal Oxide Field Effect Transistor works by using an electric field to change the concentration of carriers in a semiconductor bulk. On the gate pin, there is no physical connection to the body of the device, it is separated by a later of oxide. This gate, is analogous to a capacitor, and to apply any voltage, it must be charged. Every mosfet has a gate capacitance, and to turn on the device, we have to charge this capacitor.

 

 

We know to charge a capacitor C to a voltage of V through a resistor of R is:

where is the RC time constant. Here we can find the time it takes to charge the gate capacitor to a voltage V. To charge this capacitor we generally use an IC called a mosfet driver, we do this simple because the pins of a microcontroller can only source 20mA. At that rate it would simply take too long to turn on/off and we would waste alot of energy in our fets switching. Ok, so that problem is solved, whats next? well, lets get back to that schematic of the motor controller with the inductors, once equation you may recall is the following:

this is the equation for voltage across an inductor, is the same as saying how fast the current changes over time, now we have a mosfet that's trying its absolute hardest to turn on and off as fast as possible, di is the same as the magnitude of on current and off current and dt is how fast it takes to go from on to off. well crap.. it doesn't matter how small L is, is HUGE, it dominates that term. and everywhere in that schematic where we have an inductor, we have a very large spike in voltage. Below is an old capture of a probe across the mosfet bank. I would see spikes well past 100V on a 24V system! I essentially made a really nice boost converter! (Add output caps in the right place, and this IS how you make a boost converter)

Crap, what do we do

Because i didn't set out to design a boost converter, those spikes are unpleasant, plus they'll destroy about every component in the controller if i don't control them. Now, if we have a large positive voltage across the mosfet bank, and the input to the motor is a smaller positive voltage, we have a negative voltage across the motor (with respect to current flow). If anyone has ever worked with inductive loads before, you know the answer to this problem, we need a flyback diode. A very large bank of flyback diodes. A flyback diode does miracles to take care of the motor but we also need something to take care of the inductors in the wires, the short answer is we use a bank of capacitors on the input of the controller. The bank of capacitors act as a low impedance short to "ground" for any spikes in the wires, keeping the voltage across the controller very stable. It isn't perfect, since we have to deal with alot of energy, but it makes things safe enough that we wont unnecessarily kill anything.

 

 

This schematic represents closely what I did on the actual controller, replace the diodes, capacitors and mosfets with many in parallel and you got the basic picture of the real thing.

Now, flyback diodes are only one possible solution, another is making a synchronous half bridge controller, This is something that i am also working on, but i wanted to perfect the passive system before i moved onto an active one.

The Design

So lets dive into what the motor controller has evolved into, this is the 3(or fourth) major revision of my design and so far it is the best operating version I have.

Below is a picture of the PCB mostly populated and the mosfet bank:

Here is a picture of it connected and ready to be used!:

Picture of the watercooling:

It has the following features:
-pic18f26K22 runs the show, all pins and power are broken out to a set of headers which allow the use of "shields"

-TX Shield has filtered and buffered inputs, 3.3v supply for xbee

-Inputs for various current, temperature, tachometer, battery voltage and other sensors

-Dual independent logic and drive power supplies, coupled optically

-4 layer PCB

-Large bank of schottky high current diodes for flyback

-Bank of 12 high capacitance electrolytic capacitors, Hexagonal closed packed D.

-Breakout for 10 high power mosfets each with own gate resistor connected to two mosfet drivers in parallel

-Watercooled Mosfet Drain bus bar

I used the following components:

-PIC18F26K22 Microcontroller : http://ww1.microchip.com/downloads/en/DeviceDoc/41412F.pdf

-FOD220 optocoupler: http://ww1.microchip.com/downloads/en/DeviceDoc/41412F.pdf

- UCC27424 Low side mosfet driver: http://www.ti.com/lit/ds/symlink/ucc27424.pdf

-LM2796 Simple switcher: http://www.ti.com/lit/ds/symlink/lm2596.pdf

-SBR60a200ct rectifier: http://www.diodes.com/datasheets/sbr/SBR60A200CT.pdf

-FDP036n10a N channel mosfets: http://www.fairchildsemi.com/ds/FD/FDP036N10A.pdf

Two logic signals from the microare sent to the mosfet drivers, the PWM and Enable signal. everything has external pull down resistors to ensure reliable operation in case something goes wrong with the micro. Like i mentioned above, the pins of the microcontroller are broken out into two 14 pin headers, +5V and +12V are available to any header accessories. There is also a 5 pin programming port for the micro.

The header "shield" board looks like this:

Each sensor input goes to a low pass filter and an op amp buffer. This keeps the signal low noise ans allows me to temporarily ignore any real need for DSP at the moment.

Finally, the mosfet bank is watercooled using PC watercooling supplies i had lying around. This allows me to push them a little further than what you could normally get from just air cooling, ans allows me to reach the datasheet speced currents a little easier.

The Code

The code is written entirely in assembly. As of now it is 6 asm files:

main.asm - Just a file which includes all the other .ASM files.

Setup.asm - Includes the setup routine and configures on board peripherals.

FSM.asm - Contains the code for the Finite State machine.

int.asm - Contains interrupt code.

subs.asm - Contains all the subroutines.

A finite state machine was chosen because it allows very robust control loop. As long as next state/current state are in bounds, you wont have your code running out of boundaries. There is no checking to see if the next state variable is in range, but this can be easily added.

The state machine handles all the motor controller functions, including ensuring safe operation. Some of the safety features includes are to limit the change in throttle, disable the throttle when first turning on, and use of a key/killswitch. Any hazardous operation return to an idle default state which waits for key input. The code is incredibly modular and adding/removing states is relatively simple. The state diagram is shown below:

The interrupt routine simply polls all the analog inputs, stores each 10 bit a2d in two 8 bit registers and then transmits the data wirelesly to be processed off board. Also existing in the interrupt routine is limit code which defaults the controller to safe operation if it exceed software set limits.

Some features i plan to include are addition of code to read a tachometer output, and the ability to receive commands.

As of now, don't trust my comments too much, always best to look in the data sheet or ask me a question if you are confused.

Sauce

Below is a video of the controller running a MARS permamemnt magnet DC motor connected by chain to a lynch motor with it's terminals shorted by a piece of welding cable. I only go to about 50% duty cycle. I am still in the process of testing and verifying the current output of the controller.  I have been using the Allegro acs758 hall effect current sensor, but i haven't been getting reliable data from them. I hope that i can refine my current measurement procedure to get a better estimate of the current through the controller. Regardless, I have verified peak currents of 250 amps and continuous current of around 100-150 amps. The main limitation is cooling of the flyback diodes as they get very hot. In the video they are passively cooled with a failed waterblock, in the next revision they will be actively cooled and should allow me to push thing a little harder.

Also a little note about the setup, We made a simple dynometer to put load on our controller, its pretty simple, Motor controller drives the Mars motor, lynch motor provides load for it. By varying the restive load on the Lynch, we can vary the load on the Mars. So far we have two settings, no load and full load :P seems makng a many-KW power resistor is not simple...

Conclusion and things to improve

I am far from done with this project, but this is the first major milestone and i think most of the hard work is done. The micro code is very mature and very adaptable, the TX shield does what is suppose to, the XBEE sends data effortlessly. The Mosfet bank remains very cool and i feel safe pushing it further, the biggest limitation right now are my diodes and keeping them cool. I have reduced the switching frequency to around 250hz and this seems to be working. I will be pushing another revision of the PCB to make mounting the lugs easier, i want to also look into diodes that can be mounted on the bus bar directly with the mosfets (they are hard to find, most diodes are wired backwards of what i want).Another alternative is to make a full half bridge controller and using a back of mosfets for the back emf, this is much better than using a bank of diodes as power dissipated in a bank of mosfets is proportional to Rdson and not a constant voltage drop.  However, this adds much complexity to design as I now have to make the two banks of mosfets synchronous. Even worse, you cannot find PMOS transistors of these current ranges (In a cheap, useful package) so its best to use NMOS transistors on the high side with a boost cap mosfet driver. I've used them in the past and they can be unreliable, but i will be looking into the possibility of using them in my next design.

As with all my project, files are below, if you take from my design, give me credit. I dont mind you taking and improving where I leave off.

Files:

Controller Board:
Shield:
 

Announcing the Mcuplace Store! (and some new projects to come)

01/08/13 | by Jfkfhhfj | Categories: Code

I wanted to introduce the new store i am attaching to the blog. http://mcuplace.com/store/ . The primary purpose of the store is to sell the PCBs and designs featured in the blog. All projects will have all source code/project files on the project page and if you choose you can get any design as assembled, kit or bare PCB. I will also be selling a handful of basic components at competitive prices!

I hope that the store can hold the information in the blog in a more static nature.

In the next few weeks I have some exciting projects coming up including the documentation for my switching motor controller!

Thanks everyone, I cant wait for the PCBs to start rolling in.

 

Intro to finite state machines (FSM)

07/04/11 | by Jfkfhhfj | Categories: Informative, General, Code

I've been out of the game of writing blog posts for a while now, but i promise its only because I've been working on some cool stuff. I'm going to be making a few posts on simple concepts before i roll out some larger projects I've been working on.

Finite State Machines:
A very important part of digital/software design is being able to control how a piece of software "flows", how the user interacts the software and how it behaves to certain stimuli. Finite State Machines associate actions (things your software/hardware does) with a current state and an input combination (Mealy State Machine) or just the current state alone (Moore Machine).

Finite state machines involve breaking your problem down into states, in which each state; the possible inputs to your code produce different results. Its a very simple paradigm, but its often hard to abstract, so we'll start with an example.

An Example:
Lets say we have a digital clock, and we have 3 buttons, there are several features we'd like to implement. Lets say for now, we can only set the time, the alarm time and turn on and off the alarm. If we list all the possible actions we find we can:

-Turn on and off the alarm
-Change the hours of the alarm
-Change the minutes of the alarm
-Change the hours of the actual time
-Change the minutes of the actual time
-So and and so forth.

So now we can do several things; one thing we can do is to have a button for every action. This really tends to eat up hardware and makes the end result sloppy and very user unfriendly (imagine having a button for every aspect of the operation of a more complicated device) Plus we stated above, we only have 3 buttons!

A much better example is to use a finite state machine, in which each button will have a different function dependent on the state that it is in.

For simplicity sake, we will be implementing the design as a Mealy machine (where the action is determined by the inputs and current state.

State Diagrams:
A state diagram is a way of representing the states that a system can take, for our clock we have Three:

State 1: The idle state, in this state we simply keep time, the user can enter programming mode from here or turn on and off the alarm

Button 1 - Will turn on or off the alarm
Button 2 - Enters programming mode (goes to state 2)
Button 3 - Does nothing

State 2: Programming mode for clock/alarm HOURS setting

Button 1 - Does nothing
Button 2 - Sets the current HOURS setting (goes to state 3)
Button 2 - Increments the set value

State 3: Programming mode for clock/alarm MINUTES Setting

Button 1 - Does nothing
Button 2 - Sets the current MINUTES setting (goes back into run mode after)
Button 3 - Increments the set values

So here, we implemented all the functions we wanted to do, with the buttons we had, and we even had ability to add extra functionality to each state!

Here is the state diagram for such a system:

Why FSM:
Besides for making hardware much more simple, FSM also has some major code benefits. The most major is now we can abstract our design into actions. We assign actions to a state/input combination and call this action when the criteria is met, with this we can break down out code and avoid "spaghetti code." This allows us to segment our code into different parts, making things easier to work on in the long run. FSM also allows us to avoid duplicate code, for example, with our clock, we have to increment the minutes and the hours, with this FSM we can write one routine to increment a register and have the current state determine what register to increment. Here we use the current state as a parameter to avoid having two routines for one thing(a simple example). There are many benefits to using FSM in your code, below i included a FSM routine for PIC microcontrollers using lookup tables. The same routine can be written with ROM tables as well.

Some Code:
Below I've written very simple code for a PIC18F micro which allows you to implement a Mealy FSM into your design. It's very simple code and is completely stand-alone, so you can implement it into any project you wish!

cblock 0x00
	INPUT
	CURRENTSTATE
	NEXTSTATE
	TEMP
endc

org 0x00
 
PORTS:
;;;;;;;;;;;;;;;;;;;;;;;
;SETUP STUFF GOES HERE;
;;;;;;;;;;;;;;;;;;;;;;;
	

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;MAIN                             ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
INIT:
	movlw		0x00
	movwf		CURRENTSTATE
	movwf		NEXTSTATE      ;Initialize States to idle

	
MAIN_LOOP:
	call		FSM
        goto        MAIN_LOOP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;This is the main FSM Code           ;
;INPUT must be from 0-256            ;               
;Each state calls the INPUT numbered ;
;Action from the lookup table        ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
FSM:
	rlncf 		CURRENTSTATE,w
	call		GETSTATE
	movff		NEXTSTATE,CURRENTSTATE
	return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Lookup Tables
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
GETSTATE:
	movff		PCL,TEMP
	addwf 		PCL
	goto		STATE0
	goto		STATE1
	goto		STATE2
	return
STATE0:
	rlncf       INPUT,w
	movff		PCL,TEMP	;Pics are stupid
	addwf		PCL
	goto		ACTION0
	goto		ACTION1
	goto		ACTION2
	goto		ACTION3
	return
STATE1:
	rlncf       INPUT,w
	movff		PCL,TEMP	;Pics are stupid
	addwf		PCL
	goto		ACTION0
	goto		ACTION1
	goto		ACTION2
	goto		ACTION3
	return
STATE2:
	rlncf       INPUT,w
	movff		PCL,TEMP	;Pics are stupid
	addwf		PCL
	goto		ACTION0
	goto		ACTION1
	goto		ACTION2
	goto		ACTION3
	return

;State actions
ACTION0:
	movlw 0x01
	movwf NEXTSTATE
	return
ACTION1:
	movlw 0x02
	movwf NEXTSTATE
	return
ACTION2:
	movlw 0x03
	movwf NEXTSTATE
	return
ACTION3:
	movlw 0x00
	movwf NEXTSTATE
	return
end

Code Explanation:
Essentially, we have three system variables, NEXTSTATE, PRESENTSTATE and INPUT, when we call FSM the program uses PRESENTSTATE as an offset for a look-up table, calling the current set state. Once a state is loaded, we use INPUT as a offset for the action associated with a state. Thus we get an action that is associated with an input/state combination.

The major downside to this code is that STATE and INPUT MUST BE FORMATTED! If they are not, you will get erratic program behavior. (I.e. if there is 3 states, the first must be equivalent to 0x00, second, 0x01, and third 0x02, same for input)

This code can also be used with interrupts as well.

 

A new beginning

04/30/10 | by Jfkfhhfj | Categories: Code

Recently I've had the wonderful opportunity to TA ESE 123 at Stony Brook University with Professor David Westerfeld. Over the past semester I've been working in the student lab, helping with their assignments and lab tasks. For the second half of the class however, the students were to build a digital clock combined with a USB charger. The project took advantage of AVR mirocontrollers for the digital clock part of the design. Being a freshman level class, it would be a daunting task to teach not only basic electric theory, but combine that with basic microcontroller theory combined with learning assembly. I decided it would be best to write a handout that went over all the basics of computation theory, microcontrollers as well as completely outline the hardware and software aspects of the digital clock. The resulting document is about 30 pages of insight, knowledge and experience all dedicated to the project this semester of ESE 123. The audience of this document is the beginner and expert alike, I hope that anyone who reads it learns something new.

Attached to this post is the full handout, it introduces a new line of hardware for this blog, the AVR series of microcontrollers. The specific chip used in the project was the ATtiny48 microcontroller. I am also attaching the full commented code.

Get the full writeup here: Write Up
Grab the code here: Code

*Note, the code was written by Professor Westerfeld and cannot be used without his consent.

 

Accelerate

01/31/09 | by Jfkfhhfj | Categories: Code, Projects

The project:

This project is a simple but fun one. We are going to be using a tri-axis accelerometer to collect analog data from and then display it on the LCD. Our output on the LCD will look like:

|X||:|| ||Digit1||Digit2||Digit3|
|Y||:|| ||Digit1||Digit2||Digit3|
|Z||:|| ||Digit1||Digit2||Digit3|

The X:, Y:, and Z: are all static and only have to be sent once. (once you update the DDRAM address it stays there until power is cut) The digits however, have to be updated as we get new values, thus it is dynamically updated in our code and sent periodically.

This algorithm is fairly simple and works automatically. The procedure goes:

-Initialize the MCU
-Initialize the LCD
-Draw the static information
-Collect the analog information
-Convert the raw data into digits
-Display the data on the LCD.

The Code:

The Code:  #include p16F690.inc
     __config (_INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF)
org 0              ; Begginning of code
cblock 0x20
	CHARLOCATION
	RESULT
	ONES	
	TENS
	HUNDREDS
	TEMP
	DELAY1
	DELAY2
	COUNT
endc
PORTS:
	bsf       STATUS,RP1     ; Bank 2
	movlw     0x14           ; RA2 & 4 Configured for analog AN2,3
	movwf     ANSEL          ; 
	movlw     0x08           ; RB5 configured for analog (AN11)
	movwf     ANSELH
	bcf       STATUS,RP1
	bsf       STATUS,RP0     ; select Register Page 1
	movlw     0x00           ; Move the hex value of 0 to multi purpose register W
    movwf     TRISC          ; make IO PortC all output
	movlw     0x16           ; Make Ra4,2 and 1 Input
	movwf     TRISA
	movlw     0x20           ;RB5 Input
	movwf     TRISB
	movlw     0x10           ;set A2d Clock 
    movwf     ADCON1
	bcf       STATUS,RP0     ;Exit bank 1
	;initialize 
	clrf      PORTC
	clrf      PORTA
	clrf      PORTB
	clrf      COUNT
	clrf      TEMP
	call      INITIALIZELCD
	call      DRAWLCD
MAIN:                                           ; Our MAIN routine gets the analog value off a port, and updates the LCD with the info
	movlw     0x41                             
	movwf     ADCON0     ;Channel 2
	call      A2D        ;Get A2D value
	movwf     RESULT
	movlw     0x83       ;Char position 03
	call      UPDATELCD  ;Call Update LCD, having the char position in W
	movlw     0x4D  
	movwf     ADCON0     ;Channel 3
	call      A2D
	movwf     RESULT
	movlw     0xC3       ;Char Position 43
	call      UPDATELCD	
	movlw     0x6D  
	movwf     ADCON0     ;Channel 11
	call      A2D
	movwf     RESULT
	movlw     0x97      ;Char position 17
	call      UPDATELCD
	call      DELAY
	goto 	  MAIN      ; Return to main
A2D:
	nop 
	nop
	nop
	nop
	nop                 ; wait 5 uS
	bsf       ADCON0,GO ; Start the conversion
	btfss     ADCON0,GO ; Bit will stay high untill conversion is complete
	goto      $-1       ; Untill then keep going back
    movf      ADRESH,w
	return
INITIALIZELCD:          ;Initialization Routine for the LCD
	bsf       PORTC,0   ;Put CMD 0 on port C
	call      SEND      ;Call Send routine, Sends command and waits for busy flag
  	movlw     0x38      ; Set Function, 8 Bit, 5X10 Dots, Two line
  	movwf     PORTC
	call      SEND
	movlw     b'00001100' ; Turn on the Display (0x0B
	movwf     PORTC
	call      SEND
        movlw     0x06       ; Set entry mode to: Increment Adress counter, no shift.
	movwf     PORTC
	call      SEND
	return
DRAWLCD:                 ; Routine which draws static display
	movlw     0x80       ; Start with the DDRAM address 0x00
	call      WRITELOC   ; Call Function to write the DDRAM address
	movlw     'X'        ;Move the ASCII value of 'X' to W
	call      WRITECHAR  ; Call function to write the Char to DDRAM address
	movlw     0x3A       
	call      WRITECHAR
	movlw     0xC0       
	call      WRITELOC
	movlw     'Y'
	call      WRITECHAR
	movlw     0x3A
	call      WRITECHAR
	movlw     0x94       
	call      WRITELOC
	movlw     'Z'
	call      WRITECHAR
	movlw     0x3A
	call      WRITECHAR  ; Continue untill all static elements drawm
	return
WRITECHAR:               ; Function to write a characture 
	movwf     TEMP       ; Move value to temp register
	call      BUSYCHK    ; Check Busy flag
	bsf       PORTA,0    ; Turn on Data 
	movfw     TEMP       ; Move from Temp to W
	movwf     PORTC      ; Then to PORTC
	call      SEND       ; Send
	bcf       PORTA,0    ; Turn off Data
	return
WRITELOC:
	movwf     TEMP       ;Move value to temp
	call      BUSYCHK    ;Check busy flag
	movfw     TEMP       ;Temp -> W
	movwf     PORTC      ;W -> Portc
	call      SEND       ;SEND
	return
UPDATELCD:               ; Routine to update the dynamic elements of the LCD
	call      WRITELOC   ; Init the LCD at the proper DDRAM location
	clrf      ONES       ; Clear Vars
	clrf      TENS
	clrf      HUNDREDS
	call      CALCULATE  ; Call calculate routine which takes raw A2D value and separates them into digits
	movfw     HUNDREDS   ; Write the Hundreds digit to the LCD
	call      TABLE1
	call      WRITECHAR
	movfw     TENS       ;Write Tens
	call      TABLE1
	call      WRITECHAR
	movfw     ONES       ;Write Ones
	call      TABLE1
	call      WRITECHAR
	return
CALCULATE:               ; Basic math routine, takes Result from A2D and separates into 1's 10's and 100's
	movlw     0x00
	bcf       STATUS,2
	xorwf     RESULT,w
	btfsc     STATUS,2
	return    
	movlw     0x0A
	bcf       STATUS,2
	subwf     ONES,w
	btfsc     STATUS,2
	goto      UPDATETENS
	decf      RESULT,f
	incf      ONES,f
	goto      CALCULATE
UPDATETENS:
	clrf      ONES
	incf      TENS,f
	movlw     0x09
	bcf       STATUS,2
	subwf     TENS,w
	btfsc     STATUS,2
	call      UPDATEHUNS
	decf      RESULT,f
	goto      CALCULATE
UPDATEHUNS:
	clrf      TENS
	incf      HUNDREDS,f
	movlw     0x09
	bcf       STATUS,2
	subwf     HUNDREDS,w
	btfsc     STATUS,2
	incf      HUNDREDS,f
	return
SEND:                         ;Use this routine to send a command to the LCD. Placement doesnt matter as it does two BSYFLG checks
	;Send
	bsf       PORTB,4         ; Set E line High
	nop       
	nop
	nop
	nop
	bcf       PORTB,4         ;Wait a bit then set it low, sending command to LCD
BUSYCHK:
	bsf       STATUS,RP0      ; Port C, all input
	movlw     0xff	
	movwf     TRISC
	bcf       STATUS,RP0
	bsf       PORTA,5         ;Set Read
	nop
	nop
	bsf       PORTB,4         ;E Line high
	nop       
	nop
	nop
	nop
	bcf       PORTB,4          ;Wait, Now low, Polling busy flag
	btfsc     PORTC,7          ; Wait untill busy flag is clear.     
	goto      $-7
	bcf       PORTA,5          ;Turn off Read
	bsf       STATUS,RP0
	movlw     0x00	
	movwf     TRISC
	bcf       STATUS,RP0       ;Port c all output
	return
TABLE1:                        ;Generic Table: Decimal -> ASCII
	addwf     PCL;	
	retlw     0x30
	retlw     0x31
	retlw     0x32
	retlw     0x33
	retlw     0x34
	retlw     0x35
	retlw     0x36
	retlw     0x37
	retlw     0x38
	retlw     0x39
	return
DELAY:                       ;Generic Delay
	decfsz    DELAY1
	goto      DELAY
	decfsz    DELAY2
	goto      DELAY
	return
 end

The Breakdown:

This code is fairly simple, there are no interrupts to worry about. We set up our variables in available registers as always. Our ports routine is much of the same, we set the appropriate ports for analog function and then set the appropriate I/O

Initialize the LCD:

INITIALIZELCD:          ;Initialization Routine for the LCD
	bsf       PORTC,0   ;Put CMD 0 on port C
	call      SEND      ;Call Send routine, Sends command and waits for busy flag
  	movlw     0x38      ; Set Function, 8 Bit, 5X10 Dots, Two line
  	movwf     PORTC
	call      SEND
	movlw     b'00001100' ; Turn on the Display (0x0B
	movwf     PORTC
	call      SEND
        movlw     0x06       ; Set entry mode to: Increment Adress counter, no shift.
	movwf     PORTC
	call      SEND
	return

Remember all those commands? We have to first tell the LCD what we want it to do before we start writing to the DDRAM. We set our LCD up for how were going to use it. This a fairly common initialization routine. We turn on the display, set 8 bit data control, two line, 5x10 font, and we have the address counter increment on write to DDRAM.

This routine we load the command we want into PORTC and then call our Send routine

The Send Routine:

SEND:                         ;Use this routine to send a command to the LCD. Placement doesnt matter as it does two BSYFLG checks
	;Send
	bsf       PORTB,4         ; Set E line High
	nop       
	nop
	nop
	nop
	bcf       PORTB,4         ;Wait a bit then set it low, sending command to LCD
BUSYCHK:
	bsf       STATUS,RP0      ; Port C, all input
	movlw     0xff	
	movwf     TRISC
	bcf       STATUS,RP0
	bsf       PORTA,5         ;Set Read
	nop
	nop
	bsf       PORTB,4         ;E Line high
	nop       
	nop
	nop
	nop
	bcf       PORTB,4          ;Wait, Now low, Polling busy flag
	btfsc     PORTC,7          ; Wait untill busy flag is clear.     
	goto      $-7
	bcf       PORTA,5          ;Turn off Read
	bsf       STATUS,RP0
	movlw     0x00	
	movwf     TRISC
	bcf       STATUS,RP0       ;Port c all output
	return

To send data the the LCD we have to toggle the E line. We set it high momentarily then we set it low. Once we do that the LCD reads the command and performs an internal operation. As this happens the BSY Flag is set on the LCD and we poll this until it is clear. After which we continue with normal operation by exiting the routine

Drawing the Static Display:

We draw all the static information by first setting the DDRAM address position and then putting the value we wish to send into w and then calling a routine which sends the value to the LCD

WRITELOC:
	movwf     TEMP       ;Move value to temp
	call      BUSYCHK    ;Check busy flag
	movfw     TEMP       ;Temp -> W
	movwf     PORTC      ;W -> Portc
	call      SEND       ;SEND
	return

Basic routine that makes it a little more mechanical to write data to the LCD. The value in W is sent to the LCD

WRITECHAR:               ; Function to write a characture 
	movwf     TEMP       ; Move value to temp register
	call      BUSYCHK    ; Check Busy flag
	bsf       PORTA,0    ; Turn on Data 
	movfw     TEMP       ; Move from Temp to W
	movwf     PORTC      ; Then to PORTC
	call      SEND       ; Send
	bcf       PORTA,0    ; Turn off Data
	return

Another routine specially designed for writing a character to a DDRAM address. This sets our PORTA,0 (Instruction/Data) high as we send Data to the LCD.

The MAIN routine:
The main routine is fairly simple, we generate an A2D result and then call the routine which does the dynamic updating of the LCD (we do this 3 times for each of the 3 axis (xyz)

Updating the LCD:

UPDATELCD:               ; Routine to update the dynamic elements of the LCD
	call      WRITELOC   ; Init the LCD at the proper DDRAM location
	clrf      ONES       ; Clear Vars
	clrf      TENS
	clrf      HUNDREDS
	call      CALCULATE  ; Call calculate routine which takes raw A2D value and separates them into digits
	movfw     HUNDREDS   ; Write the Hundreds digit to the LCD
	call      TABLE1
	call      WRITECHAR
	movfw     TENS       ;Write Tens
	call      TABLE1
	call      WRITECHAR
	movfw     ONES       ;Write Ones
	call      TABLE1
	call      WRITECHAR
	return

This is another simple routine, we take the A2D result, break it down into its individual digits and send it to the LCD. We provide the DDRAM address in w before we call the function, thus this can be used for more than 1 axis making our code much more efficient.

Conclusion:

Overall this is a fairly easy project that goes over the basics of controlling an HD44780 character LCD using a tri axis accelerometer as an analog input.

Ill leave you with a picture of the completed project as well the source and hex. Enjoy!

Whats next: Making it display proper units

Source
Hex

 

Project: UChat

12/28/08 | by Jfkfhhfj | Categories: Code, Projects

The basis of this project is 5 7 segment displays, One of which the user controls and uses to select a number 0-9, and 4 of which store previously received values. There are 3 buttons; Increment decrement and send, all of do their respectable functions.

This project takes advantage of the following procedures:

Asynchronous Serial Communication – We use the UART module on the PIC16F690 to transmit data from one PIC to another. We use a 9600 baud with the transmitter and receiver set asynchronously. RB5 is RX and RB7 is TX

Multiplexed 7-segment Displays
– One problem with running 5 segment displays is the sheer number of pins we’d need to run each one individually. Each display has 7 segments and a common cathode. For all of the displays that’s 40 pins we’d need to run all those displays. Multiplexing is where we only run one display at an instantaneous moment. We do this but loading the data for one display onto PORTC, then using another port to switch the CC connection using a bipolar transistor. We keep rotating the output on PORTC and the CC we are switching until we’ve gotten to the last display. This happens so fast all the displays seem like they’re on at one given time. One thing to consider when using a multiplexed display is that the outputs must be turned off when you rotate the display, otherwise you will get ghosting of the previous value.

Interrupts – We use the interrupt on change and the receive interrupt to handle user input.

The Code:

#include p16F690.inc
     __config (_INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF)
org 0
;Declare Variables
	cblock 0x20
DISPLAY
FIRSTDISP
SECONDDISP
THIRDDISP
FOURTHDISP
CURRENTDISP
STATUS_TEMP
TEMP
DELAY1
DELAY2
GROUND
W_TEMP
	endc
	goto 	PORTS ;Skip interrupt
;;;;;;;;;;;;;;
;  INTERUPT  ;
;;;;;;;;;;;;;;
	org 0x04
INTERUPT:
	bcf       INTCON,7      ;Turn off interrupts
	movwf     W_TEMP        ; Move W to temp
	bcf       STATUS,RP0    ; Goto bank 0
	bcf       STATUS,RP1    ; Goto bank 0
	btfsc     INTCON,0      ; Check for IOCA/B int
	goto      BUTTONPRESS   ; Yes, goto button press routine
	btfss     PIR1,RCIF     ; No, Check if value received
	goto      OUT           ; No, leave interrupt
	call      RX            ; Yes , receive the value
OUT:
	movf      W_TEMP,w 		; Restore W
	bcf       PIR1,RCIF     ; Clear flag
	bcf       INTCON,0      ; Clear flags
	bsf       INTCON,7      ; Restart interrupts
	retfie                  ; Return from interrupt
BUTTONPRESS:
	btfss     PORTA,3       ;Check first button
	call      INCREASE      ;Goto said function
	btfss     PORTB,4       ;Check Decrement button pressed, 
	call      DECREASE      ;Goto Said function
	btfss     PORTB,6       ;Check if TX button pressed
	call      TX            ; Goto TX routine
	call      DELAY	     	; Button Debounce	
	goto      OUT
INCREASE:
	incf      DISPLAY       ;Increment display
	movlw     0x0A          ; Move 10d to W
	bcf       STATUS,2      ;Clear Z bit
	subwf     DISPLAY,w     ;Subtract w from display
	btfsc     STATUS,2      ;Check if z bit set
	clrf      DISPLAY       ; Yes, clear display
	return                  ; no, return
DECREASE:
	incf      DISPLAY       ;Increment display (incase 0)
	bcf       STATUS,2      ;Z bit
	decf      DISPLAY       ;Decrement display
	btfsc     STATUS,2      ; Check z bit
	goto      INITIALIZE    ;Yes, reinitialize the display
	decf      DISPLAY       ;No, Decrement display
	return                  
INITIALIZE:
	movlw     0x09          ;Move 9d to w
	movwf     DISPLAY       ; Move it to display
	return
TX:
	btfss     PIR1 , TXIF    ; TXIF is set if TXREG has no character
 	goto      $-1            ; Loop until TXIF is cleared
 	movf      DISPLAY,w      ; Data to be transmitted over serial port
 	movwf 	  TXREG          ; Copy data to transmit register
 	bsf       STATUS,RP0     ; RAM PAGE 1
	btfss 	  TXSTA , TRMT   ; Wait until transmission is completed
 	goto      $-1            ; Loop back until transmission completed
	bcf       STATUS,RP0     ; Bank 0
 	return
RX:
	btfss     PIR1,RCIF     ; Wait for value to be recieved
	goto      $-1           ; loop
	bcf       PIR1,RCIF     ; Clear flag
	movf      RCREG,w       ; Move recieved word to w
	movwf     TEMP          ; then to var temp
	clrf      FOURTHDISP    ;Clear 4th display <- 3rd Disp <- Second Display <- First Display <- Temp
	movfw     THIRDDISP
	movwf     FOURTHDISP
	movfw     SECONDDISP
	movwf     THIRDDISP
	movfw     FIRSTDISP
	movwf     SECONDDISP
	movfw     TEMP
	movwf     FIRSTDISP
	return
DELAY:                     ; Generic Delay routine
	decfsz   DELAY1
	goto     DELAY
	decfsz   DELAY2
	goto     DELAY
	return
PORTS:
	; Analog
	bsf       STATUS,RP1    ; Bank 2
	clrf      ANSEL         ; All pins are digital I/O
 	clrf 	  ANSELH        ; All pins are digital I/O
	bcf       STATUS,RP1    ; Bank 0 
	; Tris
	bsf       STATUS,RP0     ; goto bank 1
	movlw     0x00           ; Move the hex value of 0 to multi purpose register W
              movwf     TRISC          ; make IO PortC all output
	movlw     b'01010000'    ; Move move b'01010000' to w
               movwf     TRISB          ; move to tris
	movlw     b'00001000'    ;Make porta.3 input rest output
	movwf     TRISA
	bcf       STATUS,RP0    ; goto bank 0
	; Interrupt
	bsf       INTCON,7      ;Set up global interrupts
	bsf       INTCON,6      ;Peripferal Interupts
	bsf       INTCON,3      ; IOCA/B interupt
	bsf       STATUS,RP0    ; Bank 1
	bsf       PIE1,5        ; Enable Interupt on Recieve
	bcf       STATUS,RP0    ; Bank 0
	bcf       PIR1,RCIF     ; Clear Flag
	bcf       INTCON,0      ; IOCA/B change flag
	; IOCB
	bsf       STATUS,RP1    ;Bank2
	movlw     b'01010000'   ; Make ports 4 and 6 IOC
	movwf     IOCB
	bcf       STATUS,RP1    
	bsf       STATUS,RP0    ;Bank1
	; IOCA
	movlw     b'00001000'   ;Make porta.3 IOC
	movwf     IOCA
	;serial comm transmit
	movlw     d'25'          ; Move the value of 25 or 0x19, to the baud rate genrator
	movwf     SPBRG          ; this will make the baud rate = to 9600 at a 4mhz internal clock in high speed mode
	bsf       TXSTA,BRGH     ;baud rate high
	bsf       TXSTA,TXEN     ;Enable Transmission
	bcf       TXSTA,SYNC     ;asynchronous
	bcf       STATUS,RP0     ;Exit bank 1
	;Serial comm Receive
	bsf       RCSTA,SPEN     ;Set serial port enable high
	bsf       RCSTA,CREN     ; Enable receiver
	; Initialize
	clrf      DISPLAY
	clrf      FIRSTDISP
	clrf      SECONDDISP
	clrf      THIRDDISP
	clrf	  FOURTHDISP
	movlw     0x01            ;Move 0x01 to ground
	movwf     GROUND
	movlw     0x20            ;Start pointer at DISLAY location: 0x20
	movwf     FSR
;;;;;;;;;;;;;;;;;;
;  MAIN ROUTINE  ;
;;;;;;;;;;;;;;;;;;
MAIN:
	bsf       STATUS,RP0 ;goto bank 1 and turn off display
	movlw     0xff
	movwf     TRISA
	movlw     0xff
	movwf     TRISC
	bcf       STATUS,RP0 ;bank 0
;Begin rotate
	movfw     INDF
	call      TABLE
	movwf     PORTC
	movfw     GROUND
	movwf     PORTA
;Turn on Displays
	bsf       STATUS,RP0
	movlw     0x00           
  	 movwf     TRISC          
	movlw     b'00001000'           
	movwf     TRISA
	bcf       STATUS,RP0
;Finish rotating
	rlf       GROUND
	call      RA3
	incf      FSR
	movlw     0x25
	bcf       STATUS,2
	subwf     FSR,w
	btfsc     STATUS,2
	call      CLEAR
	goto      MAIN
RA3:
	btfss     GROUND,3
	return
	rlf       GROUND
	return
CLEAR:
	movlw     0x20	
	movwf     FSR
	movlw     0x01
	movwf     GROUND
	return
TABLE:                          ;Generic lookup table
	addwf     PCL      ;            1;
	retlw     b'00111111';0      ;;;;;;;
	retlw     b'00000110';1    6 ;     ; 2
	retlw     b'01011011';2      ;  7  ;
	retlw     b'01001111';3      ;;;;;;;
	retlw     b'01100110';4    5 ;     ; 3
	retlw     b'01101101';5      ;     ;
	retlw     b'01111100';6      ;;;;;;;
	retlw     b'00000111';7         4
	retlw     b'01111111';8
	retlw     b'01100111';9
	return
end
;end

The Breakdown:

Initialization:
We initialize our variables to hold temporary values as well as the current values in each display.
Ports:
We set all digital I/O, Port C all output, Port A 0,1,2,4,5 as output 3 as input, Port B 4,6 as input, 7 and 5 as TX and RX respectively. The I/O Structure for this code is:
PORTC – Primary 7-Segment driver
PORTA - Drives the line for the ground for the common cathode as well as the send input
PORTB – Handles serial comm., as well as increment and decrement inputs.
We use the interrupt on change for Port A and B; we set up that interrupt for its respectful inputs
We set up the UART module for asynchronous communication, high speed baud and we turn on TX and RX.
We finally clear the display variables, set the ground to the first position and set the pointer to look at the first digit’s memory location (0x20)

Main:
We turn off the outputs to prevent ghosting. Since we can only change one port at one time and the display uses to ports to display data we shut off the ports, change the value and then update the ports. Thus we prevent the previous value from being displayed on top of the current one. We display the current value by using the value in that digit’s block of memory into a lookup table and then outputting to PORTC, along with the respectable ground for that digit’s display being HIGH.
After that we update the pointer reference to the next digit and rotate the ground position. We must note however, when the ground is at RA3 (input only) we rotate it once more since were using it as input.
Once we reach the end of the rotation, we reinitialize the displays.

Interrupts:
The interrupt routine is fairly simple it handles 4 conditions.
-Increment pressed
-Decrement pressed
-Send pressed
-Value Receive
The first two conditions are fairly simple, the interrupt happens we pool for a button press and then we update the first digit’s value.
The send routine, pools for a button press, if it was the first digit is loaded into the TX reg and is sent out.
When a value is received, we enter the RX routine, we store the value into a temp register and we begin updating the displays.
Temp>>First>>Second>>Third>>Fourth>>Clear
We do this by rotating the display into the next one. I.e. the third display into the fourth, etc.
If no conditions are met, we just exit the interrupt.

Conclusion:

This is a fairly simple project but it takes advantage of the PIC’s resources and on board hardware, we end up using 19 of the 20 pins of the PIC MCU. This is a great demo for serial communication between two PIC MCUs. I’ve expanded this project to include two Zigbee RF modules in which the two modules talk to each other wirelessly.

Where to go from here:
There is one major downside to this project, and that is the displays. The 7-segments are horribly inefficient and lack much flexibility. One way I’d like to expand this project is to add a LCD screen and a keypad matrix to expand the user input and possible output.

General Note:

I went through the explanation of the code very quickly in this article. As the code gets more complicated I won’t be able to keep revisiting basic techniques and explaining everything. That is what the previous posts are for, to lay the basic ground work down. If you need further explanation feel free to email me, I’m glad to break it down if you need it.

Thanks once again for taking the time to read this, Source and hex as always, is below.

Source
Hex

 

Ground control to Major Tom. (Serial communication)

11/05/08 | by Jfkfhhfj | Categories: Informative, Code

One of the last modules you will come to know and love is that of the USART module. There are many communication modules that PIC MCUs offer, but USART is going to be one of the simplest and easiest to get off the ground, sending and receiving data.

Fundamentals of communication:

There are two predominate forms of communication serial and parallel. In serial you send bit after bit and parallel you send word after word. Parallel had an advantage before we got some fast transfer rates as it could transfer more data at lower speeds, but it required more infrastructure and it was more error prone as well as complicated. Serial communication is very simple and can be done with as little as two wires. Because of its simplicity we use serial communication. With the USART module there are two types of serial communication, they are synchronous and asynchronous. Either case we have a Baud Rate, or the clock speed of the transmission. We must declare the baud rate before transmission can be done so that the hardware is in sync and expecting results at the right time

Asynchronous:

Asynchronous is full duplex communication, which means you can send an receive data at the same time. We can do this because we have a dedicated wire for transmission(TX) and a dedicated wire for receiving data (RX) Data can be received and sent at the same time. This method is very simple to set up and you can have the same code on all your devices. It is primarily used for device to device communication where synchronization is not important.

Synchronous:

Synchronous is half duplex, which means you can only send data or receive it a given time. In this setup we use one wire as data and the other as clock. In this setup we have a master PIC that sets the clock rate and salve PICs that run off of the master's clock. This is very useful for multi device communication but it requires two sets of code, one for the master and one for the slaves. This however provides great control over your application where the master can control the show and the slaves collect and format data. Lets actually look at what we have to do to send data.

The Registers:

PIE1: (bank 1) Peripheral interrupt enable register 1. This is the register that controls what peripheral interrupts we have on. To use a peripheral interrupt we must first set INTCON bit 6 high. (INTCON,PEIE)

PIR1: (bank 0) Stores the flags for the peripheral interrupts.

SPBRG: (bank 1) Baud rate generator. We move a literal into here which correlates to a baud rate for transmission. The actual baud rate depends on what you send and what the settings for transmission are.

TXSTA: (bank 1)
Transmitter Status and Control register. This register controls weather we use asynchronous or synchronous communication as well as other functions related to the transmitter.

RCSTA: (bank 0)
receive Status and Control Register. This register controls various operation of the receiver and its associated properties.

TXREG: (bank 0) transmission register, we move values to here that we want to be transmitted.

RCREG: (bank 0)
receive register, bits received from transmission will be here. When we read this register the receive flag automatically clears.

Sample code:

#include 
     __config (_INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF)
org 0 
cblock 0x20
TEMP
DISPLAY
DELAY1
DELAY2
W_TEMP
endc
goto          PORTS;
org 0x04
INTERUPT:
	movwf     W_TEMP     ;Store w to TEMP
	bcf       INTCON,7   ; Turn of interrupts
	btfss     PIR1,RCIF  ; Wait until RCREG is full
	goto      $-1
	bcf       PIR1,RCIF  ;Clear flag
	movf      RCREG,w    ; Move RCREG to w
	movwf     DISPLAY    ; Move w to DISPLAY
	call      ROUTINE    
	movf      DISPLAY,w  ; Move DISPLAY to w
	movwf     PORTC      ; Move it to PORTC
	movf      W_TEMP,w   ; Restore w
	bsf       INTCON,7   ; Turn on interrupts
	retfie               
ROUTINE:
	bcf      STATUS,0    ;Clear flag
	rlf      DISPLAY     ;Rotate the display
	btfss    STATUS,0    ; Check flag
	return               ; Exit if not set
	movlw    0x01        ; If it is, reset display
	movwf    DISPLAY
	return
PORTS:
	bsf       STATUS,RP1 ;Bank 2
	clrf      ANSEL   ; All pins are digital I/O
 	clrf 	  ANSELH   ; All pins are digital I/O
	bcf       STATUS,RP1 ;Bank 0
	bsf       INTCON,7  ;Set up global interrupts
	bsf       INTCON,6  ; Peripheral Interrupts
	bsf       STATUS,RP0  ;Bank 1
	bsf       PIE1,5      ; Interrupt on RX buffer full
	bcf       STATUS,RP0  ; Bank 0
	bcf       PIR1,RCIF   ; Clear the flag
	bsf       STATUS,RP0          ; Bank 1
	movlw     0x00           ; Move the hex value of 0 to multi purpose register W
            movwf     TRISC          ; make IO PortC all output
	movlw     0xff           ; 0xff to w
	movwf     TRISA          ;PORTA input
;serial comm transmit
	movlw     d'25' ; Move the value of 25 or 0x19, to the baud rate generator
	movwf     SPBRG ; this will make the baud rate = to 9600 at a 4mhz internal clock in high speed mode
	bsf       TXSTA,BRGH   ;baud rate high
	bsf       TXSTA,TXEN   ;Enable Transmission
	bcf       TXSTA,SYNC   ;asynchronous
	bcf       STATUS,RP0 ;Exit bank 1
	;Serial comm Receive
	bsf       RCSTA,SPEN ;Set serial port enable high
	bsf       RCSTA,CREN ; Enable receiver
	clrf      PORTB     ; Clear 
	clrf      PORTC     ; Clear display
	movlw     0x01      ; Initialize Display
	movwf     DISPLAY
MAIN:
	bsf       PORTC,0   ; Portc high
	btfsc     PORTA,3   ; Poll the button press
	goto      $-1       ; Goto instruction - 1 
	call 	  TX        ; Call transmission
	bcf       PORTC,0   ; Clear port c,0
	call      DELAY     ; Call delay
	goto      MAIN
TX:
	btfss     PIR1 , TXIF  ; TXIF is set if TXREG has no character
 	goto      $-1          ; Loop until TXIF is cleared
 	movf      DISPLAY,w    ; Data to be transmitted over serial port
 	movwf 	  TXREG        ; Copy data to transmit register
 	bsf       STATUS,RP0   ; RAM PAGE 1
	btfss 	  TXSTA , TRMT  ; Wait until transmission is completed
 	goto      $-1          ; Loop back until transmission completed
	bcf       STATUS,RP0
 	return
DELAY:                ; Generic Dela routine
	decfsz   DELAY1
	goto     DELAY
	decfsz   DELAY2
	goto     DELAY
	return
end

This code is fairly complicated but its not horrible. This is basic code for asynchronous transmission which transmits a value, which triggers an interrupt for the receiving PIC. That receiving PIC then takes the transmitted value and performs an RLF operation on the data then out puts it to PORTC.

The Routine:

1) Startup – Declare environmental variables and goto main port routine
2) Ports – Set up the ports for digital I/O, Enable Interrupts, Enable peripheral interrupts, Enable asynchronous transmitter and receiver. Initialize Display to 1
3) Pool the button and wait for press
4) Button is pressed, Transmit DISPLAY by loading it into the TXREG register and waiting for it to shift out.
5) Delay is called to De-bounce switch.
6) Other PIC receives value, interrupt triggers when RXREG fills
7) Move RXREG into DISPLAY and rotate the bit (if carry reset to 0x01)
8) Return from interrupt

The Breakdown:

        ;serial comm transmit
	movlw     d'25' ; Move the value of 25 or 0x19, to the baud rate generator
	movwf     SPBRG ; this will make the baud rate = to 9600 at a 4mhz internal clock in high speed mode
	bsf       TXSTA,BRGH   ;baud rate high
	bsf       TXSTA,TXEN   ;Enable Transmission
	bcf       TXSTA,SYNC   ;asynchronous
	bcf       STATUS,RP0 ;Exit bank 1
	;Serial comm Receive
	bsf       RCSTA,SPEN ;Set serial port enable high
	bsf       RCSTA,CREN ; Enable receiver

That is the bulk of the code, This section sets up our PIC for serial communication.

The most important part of this section of code is the baud rate. We primarily derive the baud rate from the data sheet and the frequency we run our PIC at. We generally want the least amount of error for our application. For 4MHZ 9600bps in high speed mode is most efficient and least error-prone. If we look at the data sheet to set that baud rate we send 0x19 to SPBRG.

Because we want high speed mode, we get BRGH high, We want to enable the transmitter so we enable TXEN and we want asynchronous communication so we set SYNC to 0.

For RCSTA we want to enable the serial port so we set SPEN high and we want to enable the receiver so we set CREN high.

This is the basic setup for the most efficient asynchronous data transmission with a 4MHZ internal oscillator .

For synchronous and other types of setups refer to the data sheet, its all very self explanatory. One thing you have to consider, there is going to be different setups for the MASTER PIC and the SLAVE PICs.

TX:
	btfss     PIR1 , TXIF  ; TXIF is set if TXREG has no character
 	goto      $-1          ; Loop until TXIF is cleared
 	movf      DISPLAY,w    ; Data to be transmitted over serial port
 	movwf 	  TXREG        ; Copy data to transmit register
 	bsf       STATUS,RP0   ; RAM PAGE 1
	btfss 	  TXSTA , TRMT  ; Wait until transmission is completed
 	goto      $-1          ; Loop back until transmission completed
	bcf       STATUS,RP0
 	return

This is the primary code that does the transmission. We first poll PIR1,TXIF to make sure it is clear, then we move the value we want into TXREG starting the transmission. We wait for the transmission to complete and then we leave.

Once the other PIC receives a value it triggers an interrupt and the following code gets executed.

	btfss     PIR1,RCIF  ; Wait until RCREG is full
	goto      $-1
	bcf       PIR1,RCIF  ;Clear flag
	movf      RCREG,w    ; Move RCREG to w
	movwf     DISPLAY    ; Move w to DISPLAY

We wait until RCREG is full, then we move the value to DISPLAY (or wherever we want).

There we have it. Asynchronous data transmission from one PIC to another. Source and hex are below. Feel free to take and modify it. This will not be the only article on data transmission. This will be a topic we will be visiting frequently and getting very in depth with.

Source Assembly

Assembled HEX

 

Pointers

11/05/08 | by Jfkfhhfj | Categories: Informative, General, Code

Pointers are a very fundamental part of programming, often you need to reference a memory location without directly addressing or calling it, pointers help us do that. With c/c++ pointers can be a little daunting because how they work isn't directly apparent. Luckily for us, assembly works directly with the ram of the MCU and using pointers is extremely easy and almost effortless.

The use of pointers with a PIC mcu is called indirect addressing. We use the FSR(File Select register) and INDF. Essentially we load the value we want to point to into FSR and we address it using INDF. Fairly simple, lets look at an example:

VALUE equ 0x20

movlw   0x03
movwf   VALUE
movlw   0x20
movwf   FSR
movf    INDF,w
movwf   PORTC

The value on port c will be 0x03

First, we set up our variable VALUE to the general purpose address of 0x20.
Then, we set it equal to 0x03
We move the value of 0x20 to FSR (the location of VALUE)
Then we move INDF (The value of the address we loaded into FSR in this case 0x03) to w
Then w to PORTC

Very simple, with this in place you can save memory and make your programs extremely efficient and quick. Using the FSR and INDF will be essential to any remotely complicated code such as communications and robotics and other advance topics.

 

Time

11/04/08 | by Jfkfhhfj | Categories: Informative, General, Code

As your code gets more complex and so does the tasks you wish to perform its often important to take advantage of the build in hardware and gain the ability to multitask. One of the last modules to go over is the timer module. There are several timers build in but we will be focusing on the TMR0 module.

Using the module is fairly straight forward you can turn it “on” with about 3 lines of code. The TMR0 module is a 8 bit timer, thus after 256 clocks it carries over. We can also set a prescaler that sets what the ratio of the timer to the clock is. This prescale value has a max of 256 as well. With max prescaler our timer will reach a max time before rollover of 65536 instructions or .06 seconds. If you need a timer that lasts longer, the other modules hold larger values and thus rollover at a later time.

The Registers:

OPTION_REG: (bank 1) Holds timer configuration information.

The Code:

  #include (p16F690.inc)
     __config (_INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF)
org 0              ; Begginning of code
PORTS:
	bsf       STATUS,RP1
	clrf      ANSEL          ; All pins are digital I/O
 	clrf 	  ANSELH         ; All pins are digital I/O
	bcf       STATUS,RP1	
	bsf       STATUS,RP0
	movlw     b'0111'    	 ; Turn on 256 prescaler
	movwf     OPTION_REG
	bcf          OPTION_REG,3   ; Prescaler set to timer0
	bcf       OPTION_REG,5	 ; set timer for internal osc
	movlw     0x00           ; Move the hex value of 0 to multipurpose register W
              movwf     TRISC          ; make IO PortC all output
	bcf       STATUS,RP0
	clrf      PORTC
MAIN:
	movlw     0x01
	xorwf     PORTC
	btfss     INTCON,2  ; Check if timer overflow flag is set
	goto      $-1
	bcf       INTCON,2
	goto      MAIN
end


This code enables the timer and uses it to flash an LED when it rolls over.

Breaking it down:


movlw b'0111' ; Turn on 256 prescaler
movwf OPTION_REG
bcf OPTION_REG,3 ; Prescaler set to timer0
bcf OPTION_REG,5 ; set timer for internal osc

This is all the code we need for the timer module. It’s fairly straight forward. We mostly change OPTION_REG to set the timer for what we want.

OPTION_REG:

bit 7 RABPU: PORTA/PORTB Pull-up Enable bit

1 = PORTA/PORTB pull-ups are disabled
0 = PORTA/PORTB pull-ups are enabled by individual PORT latch values

bit 6 INTEDG: Interrupt Edge Select bit
1 = Interrupt on rising edge of RA2/INT pin
0 = Interrupt on falling edge of RA2/INT pin

bit 5 T0CS: Timer0 Clock Source Select bit
1 = Transition on RA2/T0CKI pin
0 = Internal instruction cycle clock (FOSC/4)

bit 4 T0SE: Timer0 Source Edge Select bit
1 = Increment on high-to-low transition on RA2/T0CKI pin
0 = Increment on low-to-high transition on RA2/T0CKI pin

bit 3 PSA: Prescaler Assignment bit
1 = Prescaler is assigned to the WDT
0 = Prescaler is assigned to the Timer0 module

bit 2-0 PS<2:0>: Prescaler Rate Select bits
(table in datasheet)

For the TMR0 module the bits we care about are 0,1,2,3 and 5. For basic operation we set bit 5 low, using the internal oscillator for the clock source. We assign the prescaler to the timer module and not the Watch Dog Timer. We also assign 111 to the prescaler which is the max value.

With that set, our basic code does a XOR operation with the current contents of PORTC, waits until the timer overflows by polling the TMR0 overflow flag bit (INTCON,2) and then returning to main and doing it all over again. In the end we get a blinking LED using highly efficient code.

There we have the TMR0 module in a nutshell. Most of the other timers work in a similar fashion. With this hardware under your belt the possibilities are endless.

Assembly Source

HEX

 

A2D

11/04/08 | by Jfkfhhfj | Categories: Informative, General, Code

It is often very useful to create a digital representation of an analog signal. The primary way we do this is through a analog to digital conversion. This is done by comparing Vin with a reference voltage (Vref) and converting their ratios to a binary number.

Fortunately for us the PIC microcontroller has analog to digital converters built into the hardware. Doing an analog to digital conversion does take time however, but it is an extremely valuable resource. We often use analog to digital conversions for sensor applications, or anything where voltage varies with conditions.
Doing an A2D conversion

As with everything in the PIC, the first thing we have to do is enable the hardware and then set up the port to do an A2D conversion.

The registers:

ADCON0: (bank 0) Stores the config information for the A2D module
ADCON1: (bank 1) Sets the clock for the A2D conversion
ANSEL: (bank 2) Determines whether a port is digital or analog, for A2D we obviously want whatever port were using to be analog
ADRESH: (bank 0 ) Results of conversion are here.

The Code:

    #include 
     __config (_INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF)
org 0                                               ; Beginning of code
DISPLAY equ 0x22                        ; DISPLAY var stored at General purpose register 0x22
PORTS:
	bsf       STATUS,RP0        ; select BANK 1
	movlw     0x00               ; Move the hex value of 0 to multipurpose register W
        movwf     TRISC              ; make IO PortC all output
	movlw     0xff                 ; Move the hex value of FF(255) to multipurpose register W
	movwf     TRISA              ; Make  PortA all inputs
	movlw     0x10               
        movwf     ADCON1         ;set A2d Clock to FOSC/32
	bcf       STATUS,RP0       ;Exit bank 1
	bsf       STATUS,RP1       ; BANK 2
	movlw     0xff     
	movwf     ANSEL            ; Set PortA for all analog 
	bcf       STATUS,RP1      ; Exit bank 2, Enter bank 0
	movlw     0x01            
	movwf     ADCON0      ; Configure ADCON and turn it on {Left just, VDD=VREF, RA0,A2D ON}
	clrf      DISPLAY             ; Clear Display Var
MAIN:
	call      A2D                    ; Call subroutine
	movf      DISPLAY,w     ; Move Display to w	
	movwf     PORTC         ; Move W to portC
	goto 	  MAIN            ; Return to main
A2D:
	nop              
	nop
	nop
	nop
	nop                               ; wait 5 uS
	bsf       ADCON0,GO  ; Start the conversion 
	btfss     ADCON0,GO ; Bit will stay high until conversion is complete
	goto      $-1                 ; Until then keep going back
        movf      ADRESH,w  ; Move result to w
	movwf     DISPLAY    ;Then w to Display
	return
 end

The Breakdown:

Most of the code is fairly straight forward, we set up the A2D module in the ports routine and then we do a conversion on RA0 and display the results on PORTC.

movlw     0x10               
movwf     ADCON1         ;set A2d Clock to FOSC/32

These lines move a literal into ADCON1, ADCON1 is the register for the clock of the conversion module, this one sets the time to the oscillator by 32, which is fairly slow. The higher the impedance of your input, the longer you should let the conversion take. The longer the clock, the more data you acquire and the more accurate your results are. However set this to what best fits your application


movlw     0xff     
movwf     ANSEL            ; Set PortA for all analog 

This merely tells the PIC to set porta up for analog signals, if you’re doing a purely digital circuit it’s wise to set these low

movlw     0x01            
movwf     ADCON0      ; Configure ADCON and turn it on {Left just, VDD=VREF, RA0,A2D ON}

ADCON0 is the configuration register of the A2D module, here you set the VREF source, how the data is outputted, and what port to run the conversion on and weather to start or stop the conversion. The datasheet explains what each bit does. This setup, we have our data left justified (A2D is a 10 bit operation, bit we only want 8, so we have the data start with the first 8 bits rather than the first 2), Our VREF is VDD, channel 000 is set (ra0) and we turned the module on.

A2D:
	nop              
	nop
	nop
	nop
	nop                               ; wait 5 uS
	bsf       ADCON0,GO  ; Start the conversion 
	btfss     ADCON0,GO ; Bit will stay high until conversion is complete
	goto      $-1                 ; Until then keep going back
        movf      ADRESH,w  ; Move result to w
	movwf     DISPLAY    ;Then w to Display

This is the main A2D subroutine. The module works by charging an internal capacitor; this takes time to charge so it’s always good practice to kill some time before the conversion starts. Once you set the ADCON0 GO bit high the conversion starts. After the conversion finishes the bit will go low, so we continue to poll that bit until it does so, and then we move the contents of ADRESH to wherever we want. In this case we move it to DISPLAY.
There we have it, a basic Analog to digital conversion. It is worth your time to check out the chapter on A2D conversion in the PIC hand guide. Good luck on your future code, the source and compiled hex are located below

Assembly Source A2D.asm

HEX

 

The Matrix.

09/08/08 | by Jfkfhhfj | Categories: Code, Projects

Matrices are somewhat interesting. They present a way to organize large and similar data sets. And because of the similarities you can do some interesting things with the numbers. In an electronic sense matrices are grids of components that can simplify i/o over a large amount of components of a similar function. The two most widely known are keypads and displays. Keypad uses a matrix of switches while a display uses a matrix of LEDs or liquid crystals. I'll be demonstrating the schematics and code to operate a simple static 8X4 LED matrix.

The Circuit

This project relies heavily on a correct circuit layout. Matrices are special because of how they're hooked up. Essentially, you provide power to one column and ground one row where they intersect, that LED lights.

The program used for drawing the schematics and making the board is called EAGLE. It is free to download and evaluate at their site

The basic schematic is as follows:

PORTC controls power and PORTB controls ground. Where PORTC = 1 and PORTB = 0 we get an illuminated LED

The CODE

The objective: To implement a 8X4 LED matrix in a way that is useful for displaying useful information.

The procedure:

1 - Set up the variables
2 - Set up the port
3 - call a table to display the pattern for the first column
4 - Rotate to the next column
5 - Increment count bit to keep track of bit location
6 - check if overflowed
a - if overflowed, clear count
7 - goto beginning

Essentially, Portc cycles so rapidly it seams that all the columns are on at one even though only one is on at a time.

The the "0"s in the look up table also essentially represent which LEDs are going to be lit.

What can be improved: The code can be improved by removing the COUNT variable and just observing and rotating PORTC, the current configuration however is more flexibly and allows expandability.

This version is also STATIC, there is no user implementation if the user wants a different design he has to go into the code and modify the look up table. Future versions will be dynamic.

As always, this is just a short description of the code and its functions. Full code is available below and discussion is welcome!

The Board

The board layout was designed is EAGLE as well and is meant to be as compact as possible. No voltage regulation features or external oscillators have been implemented yet. For troubleshooting purposes, this is fine.

http://mcuplace.com/mcu/media/blogs/blog/LED.asm (ASSEMBLY FILE)
http://mcuplace.com/mcu/media/blogs/blog/LED.HEX (HEX)

 

A little PWM

08/27/08 | by Jfkfhhfj | Categories: Code, Projects

Now that we have most of the fundamentals down and you understand the basics behind microcontrollers, lets have a little fun.

Today's project is a 3 speed PWM(Pulse Width Modified) fan controller.

In this project the user selects the fan level (low, medium, and high) and the program adjusts the duty cycle (%on vs %off) of a 20KHZ signal accordingly. By adjusting the duty cycle we can control how fast the fan runs by limiting how long it is on vs how long it is off.

The code is fairly simple and very inefficient, but we will be working on it time to time to improve its efficiency and not waste clock cycles or memory.

The general flow of the program is like so:

1 - Set up environmental variables
2 - Set up ports
3 - Goto main loop
4 - Check for button press, increment/decrement COUNT variable depending on button press
5 - Check the count variable to see what "level" it is set at (High, Medium, Low)
6 - Adjust the duty cycle based on the level
7 - Return to main loop
8 - Call the table and output the level to 7 segment display
9 - Reset loop

Its fairly simple and works pretty well. However i think i should break down each part to help you better understand the garble of letters. The source is free to grab and modify at the end of the post.

Part 1:

We set up environmental variables by using the equ function, we set labels to places in the memory that are unoccupied. Thus, they will store a value like a variable and have convenient names. The three main variables are DELAY, COUNT and TEMP. DELAY is used for the delay loop, TEMP is used for temporary storage and COUNT is the variable we use to set the speed level.

Part 2:

We set up the ports by first switching to bank 1. After we do that we send values to the TRISA, TRISB, and TRISC registers which control ports a,b, and c. A 1 makes it an input and a 0 makes it an output. We send vales buy moving a literal value to multipurpose register w and then moving the contents of w to the appropriate register.

Part 3:

We use a simple goto statement to go to the main LOOP

Part 4:

We use the function BTFSS (Check if bit is SET) and BTFSC (Check if bit is CLEAR). We provide it a register location as well as a bit. btfsc PORTA,0 means check PORTA bit 0 if it is clear. If it is, jump over the next line. If not, continue as normal.

Increment routine:

If port a bit 0 is set, the program goes into the increment routine. This routine increments count by 1 and checks to make sure it didn't go past 2. It does this by doing an arithmetic operation with w, in this case, subtraction. If a zero is returned the Z flag of the status register is set. We can use this to advance further into our program. If it is not set (thus it is under 2) we return back to the main loop with count only incremented. If it returns a zero, we clear count, and then return back to the loop.

Decrement routine:

Same as the increment routine except we add zero and decrement count.

Part 5:

We call the check level routine. In this routine we do several arithmetic operations to count to see what value it has in it. Its value send it to the proper routine to adjust the duty cycle.

Part 6:

High: Set the fan constantly on, call no delay (100% duty cycle)

Medium: Make sure fan is off, call a delay, turn fan on, call another delay, turn it off. (50% duty cycle)

Low: turn off fan, call 3 delays, turn fan on, call one delay, turn fan off. (approx 25% duty cycle)

DELAY Routine:

The Delay routine decrements the variable DELAY1 by 1 until it reaches 0, once it does it resets DELAY1 to 100 and exits the loop, this eats up 100 program cycles or roughly 100(micro)s

Part 7:

RETURN brings us back from the subroutines back to the main loop

Part 8:

We move COUNT to w, call the TABLE sub routine and add W to the program counter, this makes the program counter advance over the other values not desired and return a value to w back to the main loop. We send this value to PORTC which our 7 segment display is located.

Part 9:

We use a goto function to begin again at the front of the loop

*QUICK NOTE*

GOTO VS CALL

Goto: Simply GOES TO the part of the code, does not return a value

Call: Goes to a section of code and will return to where it was called from once a value was returned with it.

http://mcuplace.com/mcu/media/blogs/blog/pwm2.asm (SOURCE)
http://mcuplace.com/mcu/media/blogs/blog/pwm2.HEX (HEX)

 
February 2017
Sun Mon Tue Wed Thu Fri Sat
 << <   > >>
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28        

Ads by Google

Mcuplace.com Store!

This blog is dedicated to working with digital circuitry and the use of microcontrollers, small compact computers on a chip. I will be encompassing many techniques to develop projects, tools to use to write and assemble code and i will be sharing any projects i am currently working on. User feedback is a must! I do not know it all, hell im not even that experienced, but without a general place to get all the info needed i find it very hard to get into the world of microcontrollers without pursing a CE degree. So come one come all and enter the world of mystery and creativity!

Search

  XML Feeds

multiple blogs