On this website I present all my projects with the AVR range of microcontrollers from Atmel. I like this microcontroller a lot because it of it's easy to use architecture and because there are lots of open source development tools available. And programmers also cost next to nothing. I built my first programmer with 5 resistors on a parallel port and I used it only once to program a better programmer: Application note: AVR910. Later I bought a kit for a faster and better programmer. The info on this website is meant for techies and other nerds who just like me love to play with these little critters.
All light blue projects in the menu (which are also the projects with the gray titles) are not yet implemented. The're just ideas for future reference.
Oh, and by the way, Avrfreaks is an absolutely great resource for these microcontrollers.
This is just an empty project to make it easier (faster) to start a new project.
This little library was initially written because I had some trouble with the reliable operation with my LCD library. If I chose another clock frequency for my AVR the timing was sometimes so far off that the LCD didn't work any more. Therefore I wrote some small functions: DelayUs( ) and DelayMs( ). Both functions just waste some time.DelayUs( ) Selects a little delay loop written in assembly. The actual loop chosen depends on F_CPU. Here is a snippet:
#elif F_CPU < 4500000 // Use a 4 cycle loop " subi %0,2 ;Loop=4 Cycles, subi gains 2*4-1=7 Cycles. \n\t" "1: dec %0 ;1 Clock cycle \n\t" " nop ;1 Clock Cycle \n\t" " brne 1b ;2/1cycle" : "=r" (__count) : "0" (__count) #elif F_CPU < 5500000 // Use a 5 cycle loop
Because this function compensates for the function call overhead, arguments smaller then 3 give unexpected results. This library has become partly obsolete lately because of the _delay_us( ) and _delay_ms( ) functions in GCC. Although these GCC functions are more accurate for most clock frequencies they do have 2 disadvantages. They can only be called with a constant argument. If _delay_us(a) is used then the whole floating point library gets pulled in which makes the timing very inaccurate and bloats the code unacceptably. The second disadvantage is that optimization MUST be enabled for these delay routines to work properly. Some weeks ago 2009-12 I changed of my projects to use the avr delay functions instead of my own delay lib. Strangely avr-size reported an increment in flash size of 100 bytes. This could be because my lib uses 2 functions while the code for the AVR delay lib is inlined in each instance where it it used.
When I wrote this library back in februari 2005 there weren't so many lcd library's on the internet as there are today.
//--------------------------------------------------------------------------- void LcdWriteByte( uint8_t Data); // Write a single byte to the LCD. void LcdInit(void); // Initialize the LCD. #if USE_LCD_CLEAR void LcdClear(void); // Clear the LCD. #endif void LcdCommand(uint8_t Data); // Send a command to the LCD. void LcdSetCursor(uint8_t Pos); // Move the Cursor around. void LcdPutC(uint8_t Data); // Write a single character to the LCD. void LcdPutS(char *pS); // Write a string to the LCD. #if USE_LCD_PUT_X_HEX void LcdPut1Hex(uint8_t C); // Write a nibble to the LCD. void LcdPut2Hex(uint8_t C); // Write a byte (as 2 hex nibbles) to the LCD. void LcdPut4Hex(uint16_t C); // Write a int (as 4 hex digits) to the LCD. #endif void LcdWriteCgRam_P(uint8_t const *,uint8_t); // Write custom char's to lcd.Some predefined commands to write to the lcd are:
//--------------------------------------------------------------------------- #define LCD_COMMAND_SET_DDRAM_ADRESS 0x80 #define LCD_COMMAND_SET_CGRAM_ADRESS 0x40 #define LCD_COMMAND_FUNCTION_SET 0x20 #define LCD_COMMAND_SHIFT 0x10 #define LCD_COMMAND_CURSOR_ON 0x0E #define LCD_COMMAND_CURSOR_BLINK 0x0D #define LCD_COMMAND_DISPLAY_ON 0x0E #define LCD_COMMAND_CURSOR_OFF 0x0CA last hint: If you want to move the cursor of the lcd to the 5th character on the second line use:
LcdSetCursor( LCD_LINE_2+5);This way you can move the cursor all over the display with only one argument in LcdSetCursor( ).
This little hardware timer library was originally written by Patrick Crombach who put it on the AVR Freaks website in the projects section as project number 221.
This library consists of just 3 little functions and uses up only 150 bytes of program space:
void Timer0Init(void); int8_t TimerReached(int16_t *, int16_t); int16_t TimerRead(void);
At program startup the timer must be initialized with Timer0Init( ). Every time the timer overflows an int16_t software counter is incremented in the timer interrupt. The resolution at which the timer runs can be set in main.h (TIMER_RESOLUTION) and must be between 10 and 50000 us. For a complete overview of all supported combinations of TIMER_RESOLUTION and the clock frequency of the AVR you can have a look at the timer.h file. Each timer needs it's own int16_t var to store the time value and that is the only limitation on the amount of timers you can use. In the following projects you can find examples on how to use this timer:
I made Crombach's timer a little smaller and more accurate and robust. But to keep it small I also deleted some functionality of his timer library.
The goal of this collection of projects is to completely define a network for (AVR) controllers and this is an ongoing project. This definition includes all layers of the OSI model. This includes: cables, connectors, Power supply and everything else needed to make a home network complete. It should be possible for anyone with a bit of programming and soldering experience to make a network node which can be directly plugged into a network somebody else made and have a high probability that it works immediately.
The original network code was developed for a AT90S2313. After switching to an AT90S4433 (now obsolete) the code broke because there are some subtle differences in the USART of that chip. The code broke because the switching between the first byte of a packet (9-bit data) and the rest of the packet (8-bit data) did not work properly. There is still a #define in the network code to force the code to work on an AT90S4433.
There is lots of documentation written in the network.h file and I'm not going to repeat that all here. I'll just pick a few interesting things. There is no initialization needed for the network. Even though there is a NetworkInit( ) function. Because this function is so small and simple it is called every time a packet is send to the network. If the node does not send a packet very soon after startup but only listens for incoming packets it is wise to call the NetworkInit( ) function at the start of your program.
Only one of these arrays actually ends up in the code. If the microcontroller is only expected to handle small packets and it receives a big packet the remaining bytes of the packet are automatically ignored because the array indexes which are meant for big packets are limited to the small packet size this microcontroller can handle. Note that the packet sizes are incremented in steps of 50%.
The first task this function handles is to check if the microcontroller is already sending a packet. If that is so the function aborts with an error code (0). If that is ok, it checks the network for an idle state. The network is assumed to be idle if it cannot find any activity in a 100us period. That is slightly longer than the time to send 1byte over the network. Each byte has a start bit which will always be detected. If the network is idle the function aborts. If everything is ok the function sets the RS-485 driver in the transmit mode and writes the first byte of the packet to the uart and returns with an ok status (1). The rest of the packet is send by the uart data register empty interrupt. The only time a collision can occur on the network is if 2 nodes decide to start sending data at the same time. Because the time slot between deciding it is safe to send and the actual start of the transmission is very short ( Approx 0.5us @ 11.0592MHz) The only situation where it is likely that a collision occurs if when multiple nodes answer to a broadcast message. Some random delay could come in handy there.
Because most network nodes only have to handle small packet I usually use 2 buffers for packets. 1 buffer for sending packets and 1 buffer for receiving packets. If ram is to precious to do that it's possible to use the same array for both receiving and sending packets. The programmer is responsible for avoiding unintentional overwrites
In november 2009 the code for the network stack had a big update. Before that date all packets were just defined as arrays. After that date packets are of type "TPacket". A structure with dedicated places for Adresses, Bitfields, Data and checksum. The old version is no longer maintained. It is here only because it might be used in some of my oldest projects.
When I first had the sublime idea to build a home network (in Jan 2005) I had the problem of where to start. I decided that the network had to be connected to my PC. So that's where I started. My only experience with writing Windows software was a very simple terminal emulator to communicate with some of my microcontroller projects. This of course had the big disadvantage that only one microcontroller could be connected to a rs232 interface. Time for a change. I started work on a network analyzer / sniffer for my home network. I didn't need anything fancy, just the basic functionality for grabbing packets from and sending packets to the network. A year and a half later my application worked good enough to be useful. That is about the time I stopped working on the project, which means it never really got finished.
When I started with my network sniffer I re used the code for my terminal program. You can still see that because the top window of the sniffer is still the terminal window of the original program. In the main menu there is also still a menu item for selecting all the different combinations of comport settings for a comport. This was useful for debugging in the beginning but at the moment this is a bit useless.
In the "File" menu item only the "eXit" button works. Very straight forward.
The second menu item "COM1:115200,8,n,1" shows the current comport settings. If you click on it there are 4 menu items which don't need any further explanation. Except for the "Settings" menu. That one has to be adjusted the first time for your configuration. If you select the "save" checkbox these settings are stored in the registry (along with the position and size of the main window) and you never have to worry about it again.
The third menu Item is just a small clock. It shows how long the comport is opened. If you click it the comport is closed and the clock stops running. If you click it again the comport is re opened and the clock restarts at 0.
The last menu item is for clearing the terminal and sniffer windows.
This was very useful in the beginning but that has ceased to be so when the sniffer window got further to completion. The program reads the buffer of the comport every 100ms and shows the data in the terminal window. This means that if there are 2 packets on a line they are read together in one buffer read. Sometimes when the network is busy a packet is split in 2 and is read by 2 consecutive buffer reads.
This is where all the packets end up. From left to right:
This area is for making and sending packets to the network.
As stated before this program has never been finished and there is lot's of room for improvements. Some things which can (should) be improved are:
The main features of the network sniffer work under wine but for some unknown reason the menu does not work properly. This is not a very big problem because there is not much to do in the menu. Only the settings of the comport (which are wrong by default) have to be changed. You can do this by adding the following key's to the registry: HKEY_LOCAL_MACHINE-> SOFTWARE-> Hoeven Design-> NetworkSniffer
You would also like to add the font "VGAFIX.FON" to the windows/fonts directory.
There are a few incompatibilities between a standard RS232 connection and my network and therefore an interface is needed. Voila: A new project is born.
The PC cannot send directly to the network because all network nodes wait for a word with 9 data bits as the mark for the start of a packet and the PC can only send data with 8 bits of data. This could be overcome by some fancy programming in the PC and sending a carry bit with the first byte of a packet but I chose to take another approach because I noticed the RS232 drivers for windoze are a bit buggy and I wanted to avoid that mess. Therefore all data from the PC is buffered in a microcontroller. The microcontroller adds 1 extra bit to the first byte of a packet and simply retransmits the rest of the packet. All data from the network to the PC is simply level shifted RS485 levels to RS232 levels. This Generates some data overrun errors in the PC but we just ignore those because we know they are because of the extra 9th bit in the first byte of every packet. Unfortunately this also means that the PC cannot synchronize properly on packets because it does not receive the 9-th bit of the start byte from a packet but we solve that as best we can by detecting the pause on the RS485 lines between 2 packets
If the network sniffer is running it uses a RS232 port from the PC. This means that that port is unavailable for other programs. This can be a problem if you want to debug the network part of another PC program. At the moment there is a simple workaround. I just built 2 network interfaces and they are connected to 2 different comports of my PC. A better way to do this is to write a proper device driver to handle the network traffic but I don't know how to write a device driver. This approach also has an advantage: I can monitor the network from my Windows PC and my Linux pc at the same time.
The software is very straight forward. After some initialization an interrupt function writes all received bytes continually into a circular buffer. As soon as there is data in the circular buffer the main loop waits for 480us. After this small delay we check the amount of data in the buffer. If there's less than 4 bytes in the buffer we assume it is garbage and throw it away. This is needed because when the PC is turned off the RS232 line changes state and the PC interface interprets this as a start bit. If the received data is not garbage then SendAllData( ) transforms the data to a proper packet and sends it to the network.
//--------------------------------------------------------------------------- int main(void) { PORTD=0x02; // 0 = RS485 Driver is Disabled TxD Idle = '1' DDRD=0xFA; // D3 = connected to RS485 Driver Enable DDRB=0xFF; UBRR=UART_BAUD_SELECT; // Set baudrate for the Receiver. UCR=BV(RXCIE)|BV(RXEN); // Enable RxD and int. sei(); for(;;) // Executed once for every sent Packet { while(!CircularBufBytes()) // wait until there is enough data to send ; DelayUs(240); DelayUs(240); if( CircularBufBytes() < 4) CircularBufClear(); else SendAllData(); } }
This is the simplest Network node possible. It's only ability is to turn a relay on or off. As a small extension there is also a pushbutton to toggle the relay. Because of it's simplicity it is an excellent example on how to write software for a network node.
The hardware is pretty simple. There are 2 RJ-45 connectors, that way it's easy to daisy chain the whole network together. If you want some redundancy you can ad the (optional) transformer. This makes it possible to use the switch if the network is defective. In the past I've had some trouble with a clean power supply for my microcontrollers, since then I always put a little coil in the power supply. The other parts of the schematic are the microcontroller itself and the switch.
Let's have a look at main( ). Every time this node is reset it sends a welcome message to the network. After that it enters the main loop. There it checks every 10ms the state of the switch and toggles the relay if needed. After that it checks if a packet is received and if that is true the packet is examined and "executed" if it contains a valid command. After a packet is received the packetreceiver interrupt is automatically disabled to give the software time to examine the received packet. After the packet is handled the receiver is re-enabled again for the next packet. I'm a little bit paranoid, therefore I re-enable the PacketReceiver every time there was no traffic on the network for 10 minutes. If there is nothing to do the microcontroller is put to sleep.
//--------------------------------------------------------------------------- int main(void) { int16_t Timer; static uint16_t IdleCount; DDRD = RELAY_OUTPUT; PORTB = 0xFF; // Enable Pullup. PORTD = (1<<3)|(1<<4); // Enable Pullup. MCUCR|= 1<<SE; PacketDataSizeSet(MsgOut, 2); PacketChecksum(MsgOut); PacketSend(MsgOut); PacketReceiverEnable(MsgIn); Timer0Init(); sei(); for(;;) { if (TimerReached(&Timer, 1)) // Every 10 ms (1/100s) { ManualSwitch(); if( PacketReceived()) { IdleCount =0; HandlePacket(); PacketReceiverEnable(MsgIn); } IdleCount++; if (IdleCount > 60000) // Every 10 minutes of receiving nothing: { IdleCount = 0; PacketReceiverEnable (MsgIn); } } else asm volatile ("sleep"); } }
Every time a packet is received it must be checked for valid info. There are 3 cases where we don't have anything to do with the packet:
//--------------------------------------------------------------------------- void HandlePacket(void) { if(!PacketChecksum(&MsgIn)) return; if(!PacketIsForMe(&MsgIn, 0)) return; if(!(MsgIn.Header.Flags & PACKETHEADER_FLAG_C)) return; // if Command flag is not set. if (MsgIn.Data[0] == '1') PORTD |= PIN_RELAY; else if (MsgIn.Data[0] == '0') PORTD &= ~PIN_RELAY; if (MsgIn.Header.Flags & PACKETHEADER_FLAG_A) PacketSendReply(&MsgIn.Header.Source); }
This function formats and transmits a message to the network. First it sets the header of the packet, and then it adds a single data byte which has current the state of the relay. Then it calls the library function PacketChecksum( ) to add a valid checksum to the packet and keeps on trying to send the packet to the network until it succeeds.
//------------------------------------------------------Function Definitions. void PacketSendReply(TAdress *Dest) { uint8_t SendCnt; MsgOut.Header.Dest.Complete = Dest->Complete; MsgOut.Header.Source.Byte.High = NODEADRESSHIGH; MsgOut.Header.Source.Byte.Low = NODEADRESSLOW; PacketChecksum(&MsgOut); for(SendCnt = 0; SendCnt < 100; SendCnt++) // Stop infinite loop. if(PacketSend(&MsgOut)) break; }
After a switch a light dimmer is one of the most common home automation nodes.
Back in 2006 I also built a 2 channel thermometer which is connected to the network. This is the first time in my life that I used some fixed point math and that's why I wrote some help on how I did the 16.16 math in the software.
Before every temperature measurement the circuit is zero-ed by connecting the SWITCH_GND pin to ground and setting the SWITCH_VCC pin to input. This gives a constant voltage (adjustable with P1) on the input of the AD-converter. After enough time has passed to stabilize the circuit it is switched into sample mode. SWITCH_GND is turned into high impedance and SWITCH_VCC is set as an high output. This makes the current through the temperature sens transistor 11 times higher then it was before. This increment in current through the transistor translates into a higher voltage over the B-E diode in the transistor. This voltage DIFFERENCE is amplified approximately 100 times by an opamp before it is fed into the AD-converter. Because this voltage difference depends of the temperature of the transistor it can be used to calculate the temperature of the sensor. The reason for this somewhat strange measurement principle is that according to the theory the voltage difference should be linear with the temperature change which makes the temperature easy to calculate. The schottky diodes (bat85) are for protection. If the temperature sensor is disconnected the voltage over C1 (and C2) rises to 5V. If the temperature sensor is reconnected the voltage over the sensor is approximately 0.6V which gives a voltage of -4.4V on the other side of C1. Because this side of the capacitor is connected directly to the microcontroller this does all kind of nasty things to the circuit.
Because this is a one of project for me the whole thing is calibrated in software. The potmeter is only used for a very coarse adjustment for the temperature range that has to be measured. At first the sensor is put in melting ice water for a stable temperature at the low end of the range. Then the potmeter is adjusted to give a small reading on the AD-converter. This (averaged) value is hard coded and is the "TEMP_OFF_0" value in the software. After that the temperature sensor is put in boiling water to give a "hot" reading. This is the "TEMP_HOT_0" value in the software. From those calibration values the temperature is linearly interpolated or extrapolated.
If this is the first network related program you see then first have a peek at Network Switch . A lot of the software is similar to that project. The HandlePacket( ) function has a few more commands to handle the temperatures of the 2 sensors in fixed point or in human readable form (string). It can also send the raw value of the (summed) AD-converter. Something extra in this project is a watchdog reset if the network has been quiet for 12 minutes. There are of course also the ADC-routines and some functions for averaging, converting and formatting the output.
The alarm clock opens the roller blind in the living room every morning and closes it every evening. The atmel controls a motor from a little accu drill to control the position of the roller blind. Because the network can't deliver enough power I run the motor from the batteries which came with the drill. Every few months when the batteries are getting empty I type in a little network command which activates a charge pump which charges the batteries. It takes about a week to recharge the batteries and then the charger automatically shuts itself off. To prevent having to build a full bridge I just use a simple relay to control the direction of the motor. The speed of the motor is controlled by a PI controller in the micro. Position feedback is from a little encoder disk I glued onto the motor shaft. This setup can run for months without having to adjust the position of the roller blind (There are no end switches). On the schematic there is also an connector for an Lcd which has only been used for debugging during development.
There are 2 interesting parts in this software. The first is a interrupt function which takes it's input from the encoder and keeps track of the position of the roller blind. The second is the PI controller. The PI controller actually has 2 nested loops. The inner loop for the speed of the motor and an outer loop for the position. Note that overshoot in this application is unacceptable because of the simple setup of the motor control (Just 1 power fet and a relay) and therefore the deceleration of the motor must be slower then when it slows down on itself without power. Braking the motor would mean activating the reverse realy and that would mean the motor would brake very hard because it is shorted via the freewheel diode. And I actually like it to see the roller blind decelerate slowly until it reaches it's end position.
A list of all commands which this network node can handle:
After the roller blind in my living room was working properly for about a year and a half I decided is was time to motorize the screen of my beamer. This project is not finished yet (2010-03-11) but it's getting along pretty well. For this project I made some hardware and software changes. You can see the hardware changes by comparing the 2 schematics above. Differences in the software are made with:
#define PROJECT_NUMBER 1
The main differences are:
This is one of the first projects I made which connect to the network. I bought a cheap little Dcf clock (EUR8) and I took it apart the moment I got home. After playing a bit around with my scope I quickly found a wire to tap the DCF signal from. Another thing I found out was that the DCF signal was not continuously available but that was also quickly fixed with a bit of solder. Threw in an AVR and a RS-485 driver and some bits and chips for a power supply and a level converter for the DCF signal and the hardware was finished.
The software took a bit more of my time and some thought. I don't know if other people read the dcf time in a similar way as I do but I'd like to think I came up with a neat little algorithm. I do not sample the bits but I measure the time between the flanks of the dcf signal with an external interrupt on pin change. This gives just a few possibilities: A time of 100ms is a logic zero, a time of 200ms is a logic one. 800 or 900ms is the time between 2 bits and 1800 or 1900ms is a skipped pulse and the start of a new minute. Because each pin change is carefully monitored (and a tolerance can be set with PULSETOL) this is far more accurate than the parity bits in the data stream and therefore I ignore the parity bits. If a skipped pulse is detected and we counted 59 bits we assume the time is received correctly and we copy it to the internal clock. That was the first part of the algorithm. In the second part of the algorithm (the big switch) we put the received bits in a buffer (the structure Dcf) every time a nibble is received we copy that nibble to the right place in the time structure. Unfortunately the DCF clock has a bug at this moment (April 2009) sometimes it resets itself when it shouldn't. I'm not sure whether this is a software or a hardware bug.
//--------------------------------------------------------------------------- /* Interrupt routine called on every pin change. DCF bit times should be 100ms for logic 0 and 200ms for logic 1 but my receiver has bit times of approx. 90 and 180 ms. TIMER_RESOLUTION is 2.5ms Which means there are 400 ticks in a second. */ SIGNAL(SIG_INTERRUPT0) { uint8_t Dummy; // BitBucket for useless data. int16_t TimeDiff; int16_t NewTime; static int16_t OldTime=0; static uint8_t Error=0; // Set on receiver errors. static uint8_t BitCnt=0; // Number of bits received for this minute. static uint8_t *pTime=&Dcf.Minutes;// Start for Storing incoming bits. NewTime = TimerRead(); TimeDiff=NewTime - OldTime; OldTime=NewTime; // My dcf clock has a bit different pulse widths as the official dcf protocol. if( (TimeDiff > 40 - PULSETOL) &&(TimeDiff < 40 + PULSETOL)) // 100ms Pulse=logic 0 ; else if( (TimeDiff > 73 - PULSETOL) &&(TimeDiff < 73 + PULSETOL))// 200ms Pulse=Logic 1 ; else if( (TimeDiff > 316 - PULSETOL) &&(TimeDiff < 372 + PULSETOL))// 800 or 900ms Time between bits. ; else if( (TimeDiff > 716 - PULSETOL) &&(TimeDiff < 804 + PULSETOL))// 1.8 or 1.9s = Skipped pulse. { if((BitCnt==59) && (Error==0)) // If we read all bits without error: { Synchronized = 1; // Seconds since last sync, but cannot be 0. Dcf.Seconds = Dcf.Hundredths = 0; Dcf.WeekDay--; // Weekday is 0...6 instead of 1...7. memcpy(&Clock, &Dcf, sizeof(TTime)); // Copy Dcf to Clock memcpy_P(MsgOut+PACKETHEADERSIZE, StrSync_P, sizeof(StrSync_P));// Debug. PacketDataSizeSet(MsgOut, 4); Status |= FLAG_DEBUG_SEND; } BitCnt=0; // Fresh start for the next minute. Error=0; // No errors at start of a minute. pTime=&Dcf.Minutes; // Start storing data with minutes. } else // Else: TimeDiff has an illegal value. { Error=1; // We encountered an illegal pulse with. if(Status&FLAG_DEBUG) // Debug { itoa(TimeDiff,(char*)MsgOut+PACKETHEADERSIZE,10); // Debug Status|=FLAG_DEBUG_SEND; // Debug } } if(TimeDiff<100 + PULSETOL) // <200ms, we read a bit from DCF receiver { // Set a pointer to where to store the Dcf databits switch (BitCnt) { case 29: // If we stored the minutes Dcf.Minutes&=0x7F; // Erase parity bit pTime= &Dcf.Hours; // Start Storing Hours break; case 36: // If we stored the Hours Dcf.Hours>>=1; // Hours are coded in 7 bits. Dcf.Hours&=0x3F; // Throw parity bit away pTime= &Dcf.Days; break; case 42: // If we stored the day (of the month) Dcf.Days>>=2; pTime= &Dcf.WeekDay; break; case 45: Dcf.WeekDay>>=5; pTime= &Dcf.Months; break; case 50: Dcf.Months>>=3; pTime= &Dcf.Years; break; case 58: pTime= &Dummy; // Make it point to a harmless place break; } *pTime>>=1; if(TimeDiff > 40 + PULSETOL)// Time was logic 1 { *pTime|=0x80; // Store the DCF bit in the msb of *pTime } BitCnt++; } }
The commands for this clock are mostly single letter packets. I tend to use lower case letters for human readable format (easy to type into the Packet sniffer) and upper case for binary formats.
-
As a last thought about this project I want to share some pictures of the electronics in the clock. If the pcb was any bigger it wouldn't fit into the casing. There wasn't any room for the 2nd RJ-45 connector and at that time I also didn't know that 2 of those connectors is a smart thing to do. The ISP connector is accessible through a slit in the case so I don't have to take the box apart to reprogram the microcontroller.
This project is a clock with a big and clear display (70mm x 280mm). For size reference: On the left side of the 7-segment displays there is a standard AA battery. This clock is capable of storing / executing 32 different programs.
This is the first program in which I used all of my standard libraries together: Network, Lcd, Delay, Timer and Network Transceiver and the total code sizeof this 10368 Bytes. One of the challenges of the design of this clock was that there are 5 different "tasks" which all need regular attention. but when I started this project my programming skills were limited to one main task and a few interrupt routines.
After some thought I came up with the idea of using sort of very simple "task switcher". This switcher is nothing more than a for loop which checks every timer tick (from the Timer lib) if one or more of the tasks is due to run. The tasks are executed via a function pointer. Because the main loop starts again with searching for a task to execute every timer tick, the tasks have a natural priority: the order in which they are in the list. This scheduler works so good that I later added as a standard component to the _Main project for use in all future project. If it's not needed it is deleted again with just a few mouse clicks.
For a project like this a lot of pins are needed if everything is driven directly. To save some pins I used 2 simple tricks.
To make the brightness of the 7-segment displays (which need 8 Volts because each segment has 3 led's in series.) the same as the leds for the days of the week, I use current sources for the segments. To compensate for the multiplexing (less brightness) of the display the current sources are set to a current of 0.6V/15 Ohm = 40mA. Which is more than the display's can handle continuously. On the left of the schematic are the Lcd display ( With a charge pump to generate a negative voltage for the contrast), the network interface (with 2 RJ-45 connectors) and the microcontroller (atmega16) which controls everything. Because I have had some problems with spontaneous resets with some projects in the past I have learned not to trust on the internal pull-up resistor of the reset pin. I always add an external pull-up resistor. To make the schematic complete there are also a crystal, a voltage regulator for the LCD and ATMEGA16 and the layout for the connector I use to connect the 7-segment displays to the PCB. The white connector is for the LCD.
To synchronize the time nicely we keep track of time in 10ms increments. That means that if a packet is received from the network to synchronize the time, the times are so near to each other the seconds change at virtually the same time. From this 10ms reference we increment the seconds, minutes, hours and weekday. At the start of every minute it checks if any alarms are due and if so it sets a flag to service those alarms which are thereafter handled by the network thread. Because the world we live in is not perfect this function also makes some small adjustments to correct the speed the clock runs.
In the array MultiplexDisplay[] are all the segments for the 6-digit 7-segment display's and for the leds for the day's of the week. We just increment a counter each time the loop executes and write the bit pattern for the new display to the outputs. Because the displays are pretty big an I had to use some external (and slow) transistors to control the displays a few very small delay routines are inserted. Without those delays there is some "ghosting" on the display's.
The network task has two functions to perform. It synchronizes the clock with a reference from a DCF clock and it sends packets over the network if alarms are due. The packet transceiver is used for both these tasks. For the alarms the transceiver just wants a reply from the remote node and therefore the callback function is not needed. If it's task is to synchronize the clock then it expects a packet which contains the time (in packed BCD format). This packet is given to the callback function which sets the current time accordingly.
This alarm clock has 4 push buttons to adjust it's settings and ThreadButtons( ) handles them. It is a small thread and it performs a few simple functions:
The user interface is handled by the function ThreadUpdateDisplay( ). This function handles the input from the buttons (The global var Buttons). and updates the content of the display's accordingly. This user interface was a bit more complex than I first anticipated and took quite some time to debug. The layout of the 4 buttons is the same as the cursor keys of a normal 101-key pc- keyboard. In the middle are an "up" and a "down" button. On the left there is a "left" button which also serves as an escape or cancel button if is pressed repeatedly. And as you might have guessed, on the right there is a "right" button. This last button is also used as an "enter" or "confirm" button if it is pressed if the cursor is at the most right position of the display.
Regardless of the state the clock is in, if the "left" button is pressed repeatedly you always go back to displaying the current time. If then the "right" button is pressed one of the "day of the week" leds blink and the weekday can be adjusted with the "up" and "down" keys. Press the "Right" button again and the Tens of the hours start blinking and can be adjusted. Same for "Hours Units", "Minutes Tens", "Minutes Units", "Seconds Tens", and "Seconds Units". If the "Right" button is pressed if the cursor is on the "Seconds Units" display the newly entered time is copied to the clock and the clock starts counting from there.
If the clock is displaying the current time and the "up" or "down" buttons are pressed the clock shows one alarm out of the list of alarm. The number of the alarm is in the place the seconds are normally displayed. At the moment there are 9 possible alarms. Adjusting the alarms is very similar to adjusting the current time. When an alarm time is on the display press "right" to start editing that alarm. The days of the week are handled differently when programming alarms. "left" and "right" go through the days of the week, an "up" or "down" toggles the currently selected weekday on or off. This means you have to press "right" 7 times to start adjusting the "Hours Tens". Adjusting the Hours and minutes works the same as with the clock, but there are no seconds. When pressing "right" if the cursor is on the "minutes Units" digit the cursor moves to an lcd display on which the network address and the data of the packet can be adjusted. The first 4 digits of the lcd is the (hexadecimal) adress of the node to which the packet has to be send, then comes a space, followed by 6 digits for the command which has to be send to the remote node. Data for the commands can be: 0-9, a-z, and A-Z and a space. When the cursor is on the 6th digit of the command and "right" is pressed the alarm time with it's adress and packet date are stored in the ram of the clock and a copy is saved in eeprom. The eeprom is read at power up of the clock and copied to ram. If you changed your mind during entry of any data just press "left" repeatedly untill the normal time shows on the display and none of the settings will have changed.
This project is running since 2009-03-29 and is now (2010-02-20) still running fine. But there is always room for improvements. Some things I thought about are:
This mini project really helps in trying out some new ideas fast for network nodes. It is a little pcb which connects to the network on one side and on the other side it provides some signals to directly hook up an AVR to the network.
On the board there are also connections to measure the current consumption of the connected circuit (with 3 selectable shunt resistors. 1, 10, or 100 Ohm) and a connector to directly tap into the network signals.
The purpose of this library is to ad a certain degree of reliability to the network. It is a state machine which will resend a packet a preset number of times (with exponential increments in the time delay) if it does not receive an acknowledge from the addressed node. If an acknowledge is received an (optional) function is called with the address of the received packet as parameter.
The state machine is not very difficult, it only has 8 different states. Every state is written in it's own function and switching between states is done by changing a function pointer. The whole state machine is drawn within the big circle. The interface between the program and the state machine is drawn outside the big circle. The single characters between quotes are for debugging. States which allow the state machine to exit are marked with an arrow which is labeled with "run = 0". All other states change the function pointer SMState immediately to one of the other states. The only delay in the state machine is when a packet has to be send to the network. PacketSend() checks for 1 byte time (approx 100us) if the network is idle before it sends the packet to the network. If PacketSend() fails it tries a maximum of TRANSCEIVER_SQUEEZE_TIMES to send the packet before it aborts. The rest of the timing of the state machine is determined by the delays between the calls of the TransceiverRun() function.
The api of this library is very simple and consists of just 3 functions.
This is the function which makes the state machine tick and should be called at (somewhat) regular intervals. (Every 1 to 200ms is usually ok). To short an interval does not give the addressed node enough time to respond before the first resend. And to long an interval just makes everything real slow. TransceiverRun() starts by setting the STATUS_RUN flag to indicate that the state machine is running. After that it just keeps calling states until the run flag is cleared, and then it returns. The current state is responsible for clearing the STATUS_RUN flag or for changing the function pointer to another state. This construction makes it possible for several states to be executed before the state machine returns.
void TransceiverRun(void) // Run the Statemachine, call this repeatedly. { Status|=STATUS_RUN; while(Status&STATUS_RUN) SMState(); // Execute the current/new state. }
This function must be called to initialize and start the PacketTransceiver. It's arguments are a pointer to the callback function and a pointer to the packet which is to be transmitted to the network.
This function reads the internal status of the state machine. This status consists of 3 bits in an uint8_t. The bits are:
If the callback function is used it is called rom the Callback state if the state machine has received a valid packet from the remote node. This is used as a simple example in the Network Alarm Clock project. For more complex communication protocols which need multiple packets, the callback function can build the next packet and call TransceiverStart( ) to send that packet away and the new packet will be send immediately to the network.
There is lots of debugging info in the code, but it is all disabled with a single #define. If debug info is turned on then all the single letter text such as 'R' or 'X' in the state machine diagram are written on every state change to an lcd.
The state machine uses some global variables. All these variables are declared as static and therefore they are only visible in the state machine file itself. A list of these global variables is in the top of the file Transceiver.c.
This project has been ongoing for more then 25 years. Every 5 years or so I take it out of the closet and do some work on it. The ultimate goal of this project is to build a small 3d milling machine for milling out pcb's and other small stuff. When I started this project It was very ambitious (I think I was about 15 years old). Nowadays the Internet is full of similar projects. Most of those projects use a mix of self build hardware and already build software. I am a bit stubborn and want to design it all myself. Sometimes I cheat a little bit. I still need a good and simple algorithm for radius compensation...
There is so little hardware it isn't worth putting an schematic on the internet. It has an ATMEGA16 with my own network interface and 4 times the 2-bit u Stepper because the y-axis has 2 stepper motors. Things like end switches and other safeties and tool bays for automatic tool switching are not built yet. Don't be fooled by this simple hardware though. The project is far enough to make some test run's. Here are the pictures:
The software for this project is divided in 2 parts. A program for the microcontroller and a PC program. Each program is designed for the part of the job it does best. The PC software is good and fast at things as file transfer, calculations and interpretation visualization and translation of commands. The embedded software is kept as simple as possible and only does what it's good at: Sensitive timing stuff.
This is by far the part which needs the most work.
For this program I have designed my own programming language. This is a very simple language which looks somewhat like assembly language. This language has 3 big advantages over the standard G & M code which is normally used for machines like this.
To explain the basic structure of this programming language I have made a little example program which explains how it works.
; This is a line of comment because everything after a ";" is comment. ; Test program for milling a little square. ; Mainly useful as an example of how to use the commands. start ; The start point of the program. include "initialisation.mil" abs ; Coordinates are absolute now. pd ; HPGL command. This draws on the screen. tool 3 ; Use tool nr 3. rpm 10000 ; Spindle Speed is 10000 rpm. feed 30 ; Milling is done with a feedrate of 30mm/min. spon ; Spindle on movf 100.5 200,12 ; Move fast to position: x=100.5 y=200.12 ; Dots and comma's are treated the same. ; Parameters are separated by whitespace. ; Fraction is truncated to 3 digits. rel ; Use Relative Coordinates from now on. movf 0 0 -23 ; Move fast to just above the material. mov 0 0 -2 ; Move with feed rate speed ( 30mm/min ). gosub square ; Mill a little square in the material gosub square ; Make the square a little deeper. lab1: gosub square loop lab1 10 ; Loop 10 times back to label lab1. mov 0 0 5 ; Move 5 mm up, Tool is (just) above material. movf 0 0 30 abs movf 130 200 23 ; Move the Tool away from the material. gosub Hello_world ; Now do this subroutine. spoff ; Turn the spindle off. end ; End of program. (Not needed?) ; Subroutine for making a little square hole. sub square ; Start definition of a subroutine. mov 0 0 -1 ; Move 1 mm down. mov 10 0 ; Move 10 mm to the right mov 0 10 ; Move 10 mm in Y direction mov -10 0 ; Move 10 mm to the left. mov 0 -10 return ; End of subroutine. sub Hello_world include "Fonts\Verdana.mil"; include subroutines for the whole font. gosub H e l ; Call the subroutines: "H", "e" and 'l'. gosub l l o space w o r l d dot ; The subroutines for the letters must do something like: ; 1). pd ; 2). move a bit around. ; 3). pu ; 4). move to the start of the next letter. returnThere are some more commands which are not in this example. Some of these are:
The embedded software is kept very simple, it doesn't know about coordinate systems, it doesn't know what a circle is, it doesn't know what radius compensation is, etc. All it knows are some basic linear move commands, Speed limits while moving and acceleration and deceleration of the stepper motors. When searching for algorithm's for acceleration for stepper motors I found an article written by D.Austin on www.embedded.com . It is an excellent algorithm for controlling stepper motors and it uses only one division for calculating the delay for the next step. Some time later this article was adopted by Atmel in their design note 446 and it can also be found on lots of other places on the internet. After some experimentation with this code I decided is was overkill for this project and I settled for a simple look-up table for the delays. Just recently I found an article which claims to be even simpler than David Austin's but I haven't tried it yet.
Just a little function which accepts commands from the network and writes them into a circular buffer. If there are commands in the buffer then main( ) executes them. All commands which move the motors eventually end up in the function BresenhamStep( ). This function translates the move commando's to step bits for the motors and delay's for the interrupt routine and writes those to a second circular buffer.
The interrupt associated with this timer is used to generate the step and direction pulses for the stepper motors. It reprograms itself with different delays to change the speed of the motors. If it has nothing to do it reprograms itself to a low interrupt rate untill there is some action going on. This interrupt routine takes it's input from the step buffer which is filled by BresenhamStep( ).
In the file frees.c are all stepper commands which are supported by this little program. Most of these commands end up with calling BresenhamStep( ) which writes moves to the step buffer. All commands with only the content: "CommandFunc=NULL;" are discontinued or not implemented yet.
Mr.Bresenham invented a really ingenious algorithm do transform a line into something representable on a raster. His line drawing algorithm is quite famous and I'm not going to explain here how it works. A small limitation of the original algorithm is that it only works on 2 axis. The simplestt way to add more axis is to introduce an extra variable. That variable is simply set to the axis with the most movement and is the variable which is used for every step iteration of the algorithm. This way it is easy to add as many axis as you want to the algorithm.
This little box is a loudspeaker management system made by Behringer. It has 3 analog inputs and one stereo AES/EBU digital input. (shared with an analog input). It also has 6 outputs to connect 6 amplifiers and 6 speakers. The main disadvantages of this box is the low quality of it's output stages and it's lack of volume control. To solve both these problems I want to put some volume control chips( PGA2311) into it and control them via my network. I haven't started on this project yet but with a bit of luck it will be finished during the summer of 2009.
The DCX2496 only has one digital input and I have 2 PC's with digital outputs. At the moment I am constantly plugging optical connectors in and out but that has to change soon. My thought is to use an 74HC151 multiplexer to connect multiple digital inputs to one digital output for the DCX2498. Of course I want to control this little project with an avr and my network.
Another project on which is not finished yet is a little gateway for my network. At the moment all network nodes are connected in one big daisy chain. This means that when a node in the middle of the chain is disconnected then all nodes "behind" that node are robbed from their power supply and wil reset when reconnected. This is a bit of a nuisance. This project started to prevent that from happening.
There are several projects on this site which need a decent amount of low voltage power. Because big transformers are bulky and expensive I will add a network node which can provide that power. The big transformer I have (500VA 2x12V 2x20.83A) uses about 2.5Watt when Idle and to keep things as green as possible this network node is capable of turning the power transformer off when it is not in use.
The power wil be distributed through my house by 2.5qmm loudspeaker cable. This will prevent making any mistake with the 230V wiring and it is easy to work with this supple cable.
Projects which benefit from this power supply are:
When designing most of my projects I temporarily use a HD44780 based LCD for debug information. This relatively uses a lot of internal (delay's in writing characters to the lcd) and external (AVR pins / Board space) resources. All networked projects would benefit to ofload the lcd output to a network node. This debug node can be easily extended with some extras which could come in handy for some projects (switches, potmeters, buzzer). Non-networked nodes can also benefit from this interface by using the network interface from the Network Breadboard Interface.
This interface would be especially usefull for debugging nodes which must be in a physically unhandy place. For example: While adjusting the PI parameters from the motor of the Roller Blind project, the node must be above the window because the wires to the motor are short and the weight of the roller blind influence the motor parameters.
This debug interface could also be a PC program to benefit from the high IO capability of a PC.
This node is for controlling all common functions in my home automation project. The main challenge for this project was to design a control panel for functions which do not exist yet because my Home Automation project is constantly changing. The solution I came up with is to make an array of 14 X 3 buttons which can each be used for different functions. The functions of each button changes by selecting the right "page" by pushing one of the top 6 buttons. The functions for the shown page (the piece of paper on the left) are selected by pushing the button marked with the big black donut. This makes it very easy to extend the functionality of the control panel. Just put on a new piece of paper on the front panel and write the software for the new page.
An even more modern and flexible way for controlling all these functions would be to use a touch screen lcd panel for selecting all the functions. These can be bought for reasonable prices nowaday's. Just recentyl I found a nearby shop: Watterott. They sell a range of Friendly Arm boards which are very suitable for this purpose. These boards also have enough resources to act as a Error logger node and they have built in Ethernet for a more convenient connecton to a PC than my old PC Interface .
For analyzing errors on all network nodes it would come in handy to have a node which coninuously monitors the network and logs all packets which indicate errors on a node. errors would be:
One of the many ideas I have been toying around with in my head is an automated testbed which connects all kind of lab equipment on my desktop to my home built Network. This enables the user to write some kind of script or control program on the PC to make all kinds of automated measurements which can be very time consuming or even impossible to do by hand if you have to do it manually. I have just been brain storming a bit about what I think are useful project to design and build for my home automated testbed. At the moment (2010-10-23) none of these projects are realized yet but a week ago I had some spare time and I started on the first project of my home testbed: The LC meter. Because designing, prototyping and testing all these projects is a lot of work and even my time is limited, I would appreciate it very much if there are some guys and gals who are willing to design one or several of these projects. If you are enthusiastic about my website and are capable and willing to take the design of one of these projects on you, then your help will be appreciated very much. If your enthusiasm is awakened you can reach me at: for a mind melt on the subject of how to proceed on the project of your choice. All unimplemented projects are light blue or light gray.
Katja & Guido at Tuxgraphics sell a very affordable little AVR controlled power supply. That power supply can be controlled by sending it commands by I2C. Because I already have a pretty universal network connected to my PC it seems very logical to me to modify the software for that power supply to accept commands via my network. Even though I also like his idea to use a standard laptop power brick as the power source for the power supply it's also easy to let it take it's power directly from the network. That would limit the maximum power to somewhere around 20 Watts, but with a decent SMPS that can be easily turned into 3.5Amps @ 5V which is plenty of juice for loads of little gadgeds. An electronics lab never has enough power supply's.
One of the basic things in electronics is measuring voltages. This simply has to be added to my test environment. When dealing with low voltages the inputs could be made floating by charging a capacitor at the input and then switch the capacitor to gnd and the adc with help of an CD4053. This would probably also work for high side current sense resistors.
One of the basic things in electronics is measuring currents. This simply has to be added to my test environment.
One of the basic things in electronics is measuring frequencies. This simply has to be added to my test environment. I'm probably going to build different versions of this project. It would be a logical extension of the LC Meter to also make it capable of measuring frequencies. The stand alone frequency meter is probably more going to be like an universal timing device with multiple inputs. Then you could for example measure the time between some kind of start event and a stop event which can be on another signal line.
A simple example where this could be usefull is measuring the time of running the 100m. It's also a cool idea to measure the phase difference between 2 signals. If you combine that with the Function Generator and the Volt Meter then you can easily write some script to make automatically make bode plots of filters.
This is the project I am currently working on (2010-01-23). The LC meter is based on Elmcie or Elsie and other similar LC meters. This project is in a very early prototype stage and is at the moment only capable of measuring inductors. Even though I have build the first prototype on a breadboard the results are very impressive. No problems with instability of the LC oscillator. Seems to measure the inductance of a small wire in the nH range. Just for fun I connected a power transformer and I measured an inductance in the range of 7H for the primary winding. I did some calculation based on the current consumption of the unloaded transformer and it seems to work out O.K. On the pictures I connected a big LCD (2x40) but that is just for debugging. The final meter will have a nice and small 1x16 display. The debug values on the top row of the display are: 57466: Accumulated value of Timer1. 19 is the amount of (20ms)interrupts it took to accumulate the Timer1 value. 151226 is the calculated frequency of the LC oscillator.
Just a little reminder to myself of what I want to make
The hardest part of this project untill now was figuring out how to link with the -lm switch for floating point calculations and for using printf(). I can do without both of these parts for this project but I figured it was time for me to look into this so I can use it if I really need it. Besides that it is some pretty convenient stuff to use if you don't want to bother with carefully scaling fixed point values to the range you need. The biggest disadvantage of this approach is that the code grows pretty fast. I use up 8.7kB just for calculating the induction of the coils and displaying the calculated values.
Added schematics and preliminary code today. The goal of the LC meter project changed significicantly over the last few months. It's changing from a simple LC meter to a fully featured multimeter. You can see some of the Ideas I have in the schematic. This also means it is a lot more work to finish this project. On the other hand this project is becoming a combination of at least 4 different projects. I'am a bit chaotic and am working on at least 6 different projects simultaneously at this moment. To make it a bit easier for myself I decided to put this project in the freezer for some time.
A simple box with some relays for all kind of universal purposes. For example: If you combine a relay with a battery, a resistor(or a current sink) and the Voltage Meter you can use it to build an ad hoc device to measure the capacity of the battery and disconnect the battery before it is drained to far.
Some extra ideas:
Designing a professional digital oscilloscope is a pretty complex task wich makes them also pretty expensive. Therefore I concluded it's nothing more than a daydream to design one of those. It's far more realistic to limit the design of this instrument to something a bit more realistic. If you connect something like the JYE Tech Oscilloscope to the network then you still have a pretty usefull instrument.It's basic specifications are:
A function generator is a usefull instrument in most electronics labs. In an automated test environment it can for example be used to make bode plots of all kind of filters. Add a power amplifier an you can make a graph of the frequency characteristic of a loudspeaker filter. It's probably a good idea to put a DDS chip in it.
Some time ago I found a cool project on http://hackaday.com. An usb connected Hour glass. I don't really have a use for this gadget but my intuition tells me it could be usefull in some situations and therefore it is worth mentioning.
Usefull as battery charger, Power supply tester.
Interface for industry standard 2-10mA or 4-40mA Current sensors.
When tuning any electronic circuit for a maximum or minimum or other optimal value most people prefer the needle of an analog meter above a display with a digital number. Some digital multimeters have a bar graph display to try to imitate an analog instrument.
Another cool idea would be to make a little sockle and put a vertical pipe on it which is slit open in the length. Put a long row of a leds in the pipe and controll the leds with Multiplexing / Charlieplexing / Shift registers. Unfortunately this method does not have a very high resolution. If the pipe is 400mm high and you can find small leds which are only 1mm then you still only have 400 leds. Stepper motor with a pointer on a string seems to be the winner. Position can be easily recalibrated every few hours or so on a moment it does not seem to be used or after a certain amount of meters of traffic.
This is the second stepper motor driver circuit I build. The first one had 4 linear current sources and got so hot it was barely usable. It had some cool leds though. When I was searching for a better schematic on the internet I couldn't find any to my linking. I did find some commercial micro steppers but they were all prohibitively expensive and had lots of electronics on a circuit board. Time to design my own. The result is the picture on the right which is from the Mill project and on the top board are 4 stepper motor circuitss
The microcontroller has 2 simple DA converters. One for each motor winding. Note that these DA converters are not linear. They are not supposed to be. To control a stepper motor with the smoothest speed, the input to the phases should be 2 sine waves with a 90 degree phase difference. The output of the DA converters goes into the input of a comparator. The comparator compares this reference voltage with the motor current. (Voltage over the 0.47 Ohm resistors). If the current reaches the limit a flip-flop is reset (7474), which turns of the L298 drivers. The 2 flip-flops are set at a steady rate by the microcontroller to turn the motor current back on. The width of the PWM pulses is determined by the self induction of the motor coils and the load of the motor.
At initialization the smallest interrupt routine I ever wrote is set up to trigger at a regular interval. This interrupt just generates a 1 instruction wide pulse to set the flip-flops and turn the motor current on. The main loop is almost as simple. It waits for a rising edge on the step input. If a rising edge is detected the direction input is checked and a counter is incremented or decremented. After that the Counter value is used as an index in a look up table and the indexed value is written to PORTB to control the 2 DA converters and the motor phases.
//--------------------------------------------------------------------------- SIGNAL(SIG_OVERFLOW0) // Timer 0 overflow int. handler { sbi(PORTD,6); // Toggle PD6 cbi(PORTD,6); } //--------------------------------------------------------------------------- int main(void) { PORTB=0xFF; DDRB=0xFF; PORTD=0xFF; // Enable Pullups. DDRD=0x40; // Output for PWM Clock. uint8_t Teller=0; uint8_t Table[]= {0xC9,0x99,0x69,0x39,0x35,0x65,0x95,0xC5, 0xC6,0x96,0x66,0x36,0x3A,0x6A,0x9A,0xCA}; TCCR0=0x01; // Timer0 count with F_CPU. TIMSK|=BV(TOIE0); // Enable Timer 0 overflow int. sei(); // Global Interrupt Enable. for(;;) { while(!(PIND&0x04)) // Wait until PORTD,2 goes High. ; if(PIND&0x08) // Read Direction. Teller++; else Teller--; Teller&=0x0F; // Keep Teller within Range. PORTB=Table[Teller]; // Output the right bit Pattern to Motors. while(PIND&0x04) // Wait until Step input goes low. ; } }
I made this project for Leo, a brother of mine. We had an old remote control of a video recorder laying around and he wanted some dimmers for all the lights in his house. I didn't have much experience with programming microcontrollers at that time but I was willing to start something new. The basic hardware was built within 2 weeks of spare time and I got the software working in one or 2 months more. After I thought the project was finished the requirements changed a few times and I was stupid enough to accept those changes as an extra challenge. One change was an extra light switch to turn one light channel on so you wouldn't have to search for the remote control in the dark if you come home late. And another was for the control of some motorized curtains. The curtains never worked properly but the light switch works. I also made a few basic errors in the design. I do far to much work in interrupt routines which make all the lights blink if the controller receives an infrared signal it wants to try to decode. Another big problem is that the lights are controlled via a loop in main and the timing sometimes get upset. A third problem is a hardware bug because the controller resets itself every few weeks or months. At this time I am able to write better software but that would mean a complete rewrite of all the software (2kB). Maybe I shouldn't complain to much. The project has been in use since 2001 and sometimes he complains a bit but he is still using it.
This is a project I made for Henk, another brother of mine. He is very into High end audio and has been searching for some time for a volume control for his stereo set which is up to his standards. He couldn't find any and so we finally decided to design and build one ourselves. He did most of the electronics design and I only did the micro controller part. The software is really simple. There is one master (an ATMEGA8 because it has an AD-converter) and 3 slaves. The master reads an analog signal (0-5V) from a potmeter which controls the volume of his stereo set. That voltage is transformed into a relay number and that number is transmitted at a low baud rate to the slaves. Each of the slaves controls 3 relay boards in parallel. (He only build 8 channels). The slaves each have a bunch of shift register (74HC595) to control 31 relay outputs each and one relay for shorting the output at startup.
I made this little project for Hans, yet another brother of mine. (I have 5 brothers.) He had a garage door to control and bought a little RF transmitter and receiver to control his door. Leo made a nice cabinet with relays to control the 400V 800VA motor. They turned to me to connect those 2 together. The software consists of 2 little state machine's. 1 State machine wit just 2 states do debounce the input and 1 state machine for opening and closing the door. These state machines are implemented in a bit strange way. They are not implemented in a big switch() statement but each state of the state machines is a little function on it's own and the current states are remembered by 2 function pointers. This project has been working flawlessly ever since I installed it in October 2000
I made this project for Henk. He has a beamer for watching movies and a motor controlled screen. The purpose of this project is very simple. If he turns his beamer on, the screen must to down. And if the beamer turns of, the screen must go up again. This project has some analog electronics which I salvaged from one of my old project which I didn't use any more. A current transformer with some signal conditioning. The output of the Current transformer goes to an ATTINY2313, and the ATTINY2313 controls 2 relays. 1 for moving the screen up and one for moving the screen down. The screen needs 17 seconds to move so I set the control of the relays to 20s to be on the safe side.
The programmer I use is built from a kit I bought at Tuxgraphics. There are several reasons I bought this kit.
This morning (2009-12-31) I built my 4th AVR programmer. USBasp. I built this one because I wasn't completely happy with my 3rd programmer and because I want to play with a software USB stack. I do know that it works (I just reprogrammed 2 old projects just to try it out. Programming seems to be a little bit faster than with my Tuxgraphics programmer, but they are both shorter than 10s, speed is not really an issue. Avrdude needs a few seconds more to initialize this programmer (Each time before programming). Another little quirk of this programmer is that if I change the USB port it is connected to, to a port which I never used for this programmer, then windoze wants to reinstall the usbasp driver. To suit my own stubborn needs I made a few little changes to the original schematic:
Here is another software based USB stack This site also seems to have a very understandable explanation on how the driver works. Interesting stuff.
Objective Development also has a similar sw usb stack and tey seem to have made an comparison of different SW AVR USB stacks.
At the moment I do not have a kitchen timer. Not because I do not want one, (they're cheap enough to buy) but because it seems impossible to buy a kitchen timer with a decent user interface. That means I have to build my own, which I just the kind of stuff I like. The user interface is probably going to look like the picture. Using this timer is extremely simple: If you need an count down time of for example 7 minutes then all you have to do is press the "down" button for the units of the minutes 3 times and then press "Start/Add" and the countdown will start. Up and down buttons also guarantee you don't have walk through all digits if you accidentally press one of the "up buttons" once to many. That is a "feature" which I find very annoying on most simple timers.
I usually drink a lot of tea, and sometimes the tea tastes a lot better than average, which means that most times I make tea I fail at least partially.
I always have the problem that if I make my tea I forget to take the tea out of the water at the right time because I'm always distracted by a multitude of things after putting the tea in the water. To take care of this I had a simple to make but effective idea. Above the kitchen sink under the cupboards I mount a little motor with a little winch which takes the tea out of the water after a pre programmed time. Because the winch is not directly above the teapot the bitter tea dripping from the container is allowed to drain harmlessly into the kitchen sink. This will give me a fair amout of control over the hardest to grasp variable when making tea.
When I'm fiddling about with electronics I want to be comfortable about it. Therefore I built some things to make by breadboarding life a little simpler. One example is the Network Breadboard Interface. Another one is this little project. These little pcb's can be put directly into a breadboard and they have the same pinout as the AVR it is designed for. Each of these circuits also has a 74HC4053 and a programming connector on them. The 74HC4053 is from an application note from atmel and it switches the programming pins ( MOSI, MISO, and SCK between the connector on the bottom and the programming connector on the top. The 74HC4053 is switched by the level on the reset pin. I also added a reset button for the microcontroller. I build 3 of these interfaces but the one for the ATMEGA 16 and consorts is not shown on the picture. Also note the small labels for the pin names I glued on to the pcb's.
On the lower right corner of the picture is a big copper loop. This loop is always connected to the GND of the breadboard and is used to easily connect measurement equipment such as frequency meters, oscilloscopes, and multimeters to the circuit. The ground loop has 2 rows of 5 pins with which it is connected to the breadboard. Only one of those 2 rows are soldered to the loop, the other 5 pins are isolated because they are usually plugged into the 5V rail. If you look carefully you'll see a similar ground connection on almost every pcb I have made.
The orange wires on the breadboard are not just ordinary wires. I made a lot of them in different lengths and they have very sturdy gold plated ends on both sides which I scavenged from old headers. They also have some shrink wrap on the ends which I glued in place with hot glue for longevity.
Have you ever wanted to connect a data bus from one side of a breadboard to the other side?
Some of the components which I use often on breadboards also have the sturdy header pins soldered to them. I have never bend any pins on those components.
For powering the breadboard I have a small switched mode power supply which delivers 5V @ 650mA (From an old cellphone charger). For easy connection I've also soldered a new plug to the power supply which fits directly into the breadboard. There is one row of 5 pins with +5V and the other row of 5 pins is connected to Gnd. For easy attachment of all kind of measurement equipment I've also solderded a sturdy loop to the Gnd pins. This is also very convenient for (dis)connecting the plug from the breadboard. When breadboarding network connected projects I usually use the Breadboard Interface as a power supply.