├── .gitignore ├── LICENSE ├── README.md ├── Rakefile ├── include └── main.h └── src └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | build/ 3 | scaffold.bin 4 | scaffold.hex 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 John Van Enk 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 2 | 3 | ## Usage 4 | 5 | ```bash 6 | $ rake -T # see all tasks 7 | $ rake # build the project, flash to the arduino 8 | $ rake clobber # clean the project 9 | $ rake SERIAL_PORT=/dev/ttyS99999 # use a different serial port 10 | $ rake target:backup[backup.hex] # dump the flash from the target into backup.hex 11 | $ rake target:preprocess # build a preprocessed version of the C files 12 | ``` 13 | 14 | ## Board Resources 15 | 16 | * ATMega328P datasheet - http://www.atmel.com/dyn/resources/prod_documents/doc8161.pdf 17 | * Arduino UNO Schematic - http://arduino.cc/en/uploads/Main/arduino-uno-schematic.pdf 18 | 19 | ## Getting Started 20 | 21 | The following are instructions for getting up and running under different 22 | operating systems. This will take you through installing avr-gcc, avr-libc, 23 | avrdude, and the AVR GNU Binutils. 24 | 25 | ### Windows 26 | 27 | First, install a recent version of Ruby. You can grab a Windows installer for 28 | Ruby from the [RubyInstaller for Windows][rifw] page. After installing Ruby, 29 | you'll need to open a console and use the ```gem``` command to install the 30 | Cucumber and 31 | 32 | Second, you'll need a copy of [WinAVR][WinAVR_DL]. Download the latest version 33 | and install it. You will want to make sure the option to 'Add directories to 34 | PATH' is checked. 35 | 36 | WinAVR contains all the tools you need to use this scaffolding including (but 37 | not limited to): 38 | 39 | * AVR GNU Binutils 40 | * AVR GNU Compiler Collection (GCC) 41 | * AVRDUDE 42 | * avr-libc 43 | 44 | In order to complete the next step, you'll need to download the Arduino 45 | software package this can be found on the [Arduino download page][adp]. 46 | 47 | After you've installed Ruby and WinAVR, connect the Arduino UNO to your Windows 48 | computer via the USB cable. It will attempt to install a driver, but will 49 | fail. Follow the instructions on [arduino.cc][acc] to complete the 50 | installation of the drivers. 51 | 52 | ### OSX 53 | 54 | I recommend installing the needed AVR tools by means of [Homebrew][hb]. If 55 | you're already setup with homebrew, then you should just have to execute the 56 | following commands. If not, take a moment to read a bit about Homebrew and get 57 | it setup on your Mac. 58 | 59 | Once you're ready, here are the commands to install the needed tools: 60 | 61 | ```bash 62 | $ brew install avrdude 63 | $ brew tap larsimmisch/avr 64 | $ brew install avr-binutils 65 | $ brew install avr-gcc 66 | $ brew install avr-libc 67 | ``` 68 | 69 | **These are built from source and will take a while to complete.** 70 | 71 | ### Linux 72 | 73 | Getting the environment up and running on Ubuntu Linux is quite simple. One 74 | need only install 4 packages. These are all supported in the Ubuntu package 75 | manager. Open a terminal and run the following command: 76 | 77 | ```bash 78 | $ sudo apt-get install binutils-avr avr-libc avrdude gcc-avr 79 | ``` 80 | 81 | ## Building and Running the Sample Project 82 | 83 | The scaffold is driven by a Rakefile. If you're not familiar with 84 | [Rake][rake], you'll want to take some time to familiarize yourself with it. 85 | It's integral to the operation of this scaffolding. 86 | 87 | The different stages of the build process are broken down into different rake 88 | tasks. These can be viewed by running the command ```rake -T``` in the 89 | scaffold directory. You'll see something like this: 90 | 91 | $ rake -T 92 | rake clean # Remove any temporary products. 93 | rake clobber # Remove any generated file. 94 | rake target:backup[backup_name] # Make a backup hex image of the flash con... 95 | rake target:build # Build the project for the Arduino 96 | rake target:convert # Convert the output binary to a hex file ... 97 | rake target:link # Link the built project for the Arduino 98 | rake target:preprocess # Generate the preprocessed source files 99 | rake target:program # Program the Arduino over the serial port. 100 | 101 | The name of the serial port used by the Arduino UNO needs to be passed as an 102 | environment variable to the rake command. If you do not know how to identify 103 | the name of the serial port used by the Arduino UNO, consult with the section 104 | corresponding with your operating system under the 'Identifying the Name of 105 | the Arduino UNO Serial Port' later in this document. 106 | 107 | ### Building the Scaffold 108 | 109 | The scaffold can be built, linked, and flashed by running the following 110 | command: 111 | 112 | rake SERIAL_PORT=[serial port name] 113 | 114 | The following is an example of the output one can expect to see when running 115 | the ```rake``` command in a windows environment. In this example, the serial 116 | port is COM3. It may be different in your environment. 117 | 118 | C:\Development\arduino_c_scaffold>rake SERIAL_PORT=COM3 119 | avr-gcc -DTARGET -DF_CPU=16000000UL -mmcu=atmega328p -Iinclude/ -Wall -Os -c -o build/src/main.o src/main.c 120 | avr-gcc -mmcu=atmega328p build/src/main.o -o scaffold.bin 121 | avr-objcopy -O ihex -R .eeprom scaffold.bin scaffold.hex 122 | avrdude -F -V -c arduino -p ATMEGA328P -P COM3 -b 115200 -U flash:w:scaffold.hex 123 | 124 | avrdude: AVR device initialized and ready to accept instructions 125 | 126 | Reading | ################################################## | 100% 0.02s 127 | 128 | avrdude: Device signature = 0x1e950f 129 | avrdude: NOTE: FLASH memory has been specified, an erase cycle will be performed 130 | To disable this feature, specify the -D option. 131 | avrdude: erasing chip 132 | avrdude: reading input file "scaffold.hex" 133 | avrdude: input file scaffold.hex auto detected as Intel Hex 134 | avrdude: writing flash (304 bytes): 135 | 136 | Writing | ################################################## | 100% 0.08s 137 | 138 | avrdude: 304 bytes of flash written 139 | 140 | avrdude: safemode: Fuses OK 141 | 142 | avrdude done. Thank you. 143 | 144 | If the file at ```src/main.c``` hasn't been altered, you should notice that 145 | the yellow surface-mount LED on the Arduino UNO has begun to blink. It should 146 | be cycling on and off with durations of about 1 second. 147 | 148 | ## Identifying the Name of the Arduino UNO Serial Port 149 | 150 | ### Windows 151 | 152 | TBD 153 | 154 | ### OSX 155 | 156 | TBD 157 | 158 | ### Linux 159 | 160 | TBD 161 | 162 | ## Exploring the Source Code 163 | 164 | TBD 165 | 166 | [WinAVR_DL]: http://sourceforge.net/projects/winavr/files/ "WinAVR Download" 167 | [rifw]: http://rubyinstaller.org/ 168 | [acc]: http://arduino.cc/en/Guide/Windows#toc4 169 | [adp]: http://arduino.cc/en/Main/Software 170 | [rake]: http://en.wikipedia.org/wiki/Rake_(software) 171 | [hb]: http://mxcl.github.com/homebrew/ 172 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake/clean' 2 | require 'fileutils' 3 | 4 | MCU = ENV['MCU'] || 'atmega328p' 5 | PARTNO = ENV['PARTNO'] || MCU 6 | F_CPU = ENV['F_CPU'] || '16000000UL' 7 | SERIAL_PORT = ENV['SERIAL_PORT'] 8 | PROG = 'scaffold' 9 | SRCDIR = 'src' 10 | BUILDDIR = 'build' 11 | SRC = FileList["#{SRCDIR}/**/*.c"] 12 | 13 | # Create a mapping from objects 14 | # to source files. 15 | OBJ = SRC.inject({}) do |cont, s| 16 | obj_file = File.join(BUILDDIR, s.ext('o')) 17 | cont[obj_file] = s 18 | cont 19 | end 20 | 21 | # Setup what should be cleaned. 22 | CLEAN.include(OBJ.keys, BUILDDIR, "#{PROG}.bin", "#{PROG}.hex") 23 | 24 | # Some toolchain info. 25 | TARGET = { 26 | :compiler => 'avr-gcc', 27 | :compiler_args => [ 28 | '-DTARGET', 29 | "-DF_CPU=#{F_CPU}", 30 | "-mmcu=#{MCU}", 31 | '-Iinclude/', 32 | '-Wall', 33 | '-Os', 34 | '-c' 35 | ], 36 | :linker => 'avr-gcc', 37 | :linker_args => [ 38 | "-mmcu=#{MCU}" 39 | ], 40 | :objcopy => 'avr-objcopy' 41 | } 42 | 43 | # Map the default task to the chip programming task 44 | task :default => ['target:program'] 45 | 46 | # Dummy task to ensure that the SERIAL_PORT environment variable is set. 47 | # It can be set on the command line as follows: 48 | # $ rake SERIAL_PORT=[serial port name] 49 | task :serial_port do 50 | unless ENV['SERIAL_PORT'] 51 | raise "SERIAL_PORT is not defined in the environment!" 52 | end 53 | end 54 | 55 | namespace :target do 56 | # Define tasks to make .o files from .c files 57 | # using the mapping deined in the OBJ hash. 58 | rule '.o' => lambda { |tn| OBJ[tn] } do |t| 59 | FileUtils.mkdir_p(File.dirname(t.name)) 60 | cc = TARGET[:compiler] 61 | args = TARGET[:compiler_args].join(" ") 62 | sh "#{cc} #{args} -o #{t.name} #{t.source}" 63 | end 64 | 65 | # Define tasks to make preprocessed sources from 66 | # c files. 67 | rule '.e' => lambda { |tn| OBJ[tn.ext('o')] } do |t| 68 | FileUtils.mkdir_p File.dirname(t.name) 69 | cc = TARGET[:compiler] 70 | args = TARGET[:compiler_args].join(" ") 71 | sh "#{cc} -E #{args} -o #{t.name} #{t.source}" 72 | end 73 | 74 | desc "Build the project for the Arduino" 75 | task :build => OBJ.keys 76 | 77 | desc "Generate the preprocessed source files" 78 | task :preprocess => OBJ.keys.map {|o| o.ext('.e')} 79 | 80 | desc "Link the built project for the Arduino" 81 | task :link => :build do 82 | ld = TARGET[:linker] 83 | args = TARGET[:linker_args].join(" ") 84 | objs = OBJ.keys.join(" ") 85 | sh "#{ld} #{args} #{objs} -o #{PROG}.bin" 86 | end 87 | 88 | desc "Convert the output binary to a hex file for programming to the Arduino" 89 | task :convert => :link do 90 | objcopy = TARGET[:objcopy] 91 | sh "#{objcopy} -O ihex -R .eeprom #{PROG}.bin #{PROG}.hex" 92 | end 93 | 94 | desc "Program the Arduino over the serial port." 95 | task :program => [:convert, :serial_port] do 96 | sh "avrdude -F -V -c arduino -p #{PARTNO} -P #{SERIAL_PORT} -b 115200 -U flash:w:#{PROG}.hex" 97 | end 98 | 99 | desc "Make a backup hex image of the flash contents." 100 | task :backup, [:backup_name] => :serial_port do |t, args| 101 | sh "avrdude -F -V -c arduino -p #{PARTNO} -P #{SERIAL_PORT} -b 115200 -U flash:r:#{args.backup_name}:i" 102 | end 103 | end 104 | -------------------------------------------------------------------------------- /include/main.h: -------------------------------------------------------------------------------- 1 | #ifndef __MAIN_H__ 2 | #define __MAIN_H__ 3 | 4 | /* Assuming a clock rate of 16Mhz and a CLK/64 prescaler, 5 | * we will see 250 ticks per millisecond. If we want to 6 | * have the timer overflow every millisecond, we need to 7 | * initialize the counter to 5 after each tick. */ 8 | #define TIMER_RESET_VAL 5 9 | 10 | void configure(void); 11 | void task(void); 12 | int main(void); 13 | 14 | #endif /* __MAIN_H__ */ 15 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "main.h" 6 | 7 | int main(void) 8 | { 9 | configure(); 10 | 11 | while(1) {;} 12 | 13 | return 0; 14 | } 15 | 16 | void configure(void) 17 | { 18 | /* disable interrupts */ 19 | cli(); 20 | 21 | /* configure TIMER0 to use the CLK/64 prescaler. */ 22 | TCCR0B = _BV(CS00) | _BV(CS01); 23 | 24 | /* enable the TIMER0 overflow interrupt */ 25 | TIMSK0 = _BV(TOIE0); 26 | 27 | /* set the initial timer counter value. */ 28 | TCNT0 = TIMER_RESET_VAL; 29 | 30 | /* confiure PB5 as an output. */ 31 | DDRB |= _BV(DDB5); 32 | 33 | /* turn off surface mount LED on */ 34 | PORTB &= ~_BV(PORTB5); 35 | 36 | /* enable interrupts. */ 37 | sei(); 38 | } 39 | 40 | void task(void) 41 | { 42 | static uint16_t tick = 0; 43 | 44 | /* toggle every thousand ticks */ 45 | if (tick >= 1000) 46 | { 47 | /* toggle the LED */ 48 | PORTB ^= _BV(PORTB5); 49 | 50 | /* reset the tick */ 51 | tick = 0; 52 | } 53 | 54 | tick++; 55 | } 56 | 57 | ISR(TIMER0_OVF_vect) 58 | { 59 | /* preload the timer. */ 60 | TCNT0 = TIMER_RESET_VAL; 61 | 62 | /* call our periodic task. */ 63 | task(); 64 | } 65 | 66 | --------------------------------------------------------------------------------