Skip to content
rhn edited this page Jun 14, 2012 · 21 revisions

Jazda started as a tiny project to get a LCD screen running with AtTiny2313. As time passed, its functionality grew and it switched to AtMega8. Because of that legacy, it might not have the best build system, the cleanest code or the widest portability, but great efforts were put to make it comfortable to program it.

The whole source code of jazda is placed in the src/ directory, with exception of examples. It's recommended to start playing with the examples, if you're new to AVR programming.

Hardware

The hardware was designed by someone having no clue about design and knowing very little about electronics. However, it has a few nice features:

  • 7 input-only debounced pins for sensors and buttons (all trigger interrupts)
  • a SPI-like interface for the screen (currently not easily extended)
  • removable CPU

...and that's about it :) The next version will have a proper SPI bus, exposed I/O pins, dedicated button pins and mount holes.

connection schematic

Software architecture

The software part of Jazda is separated into several areas - support:

  • configuration
  • libraries (calculation, drawing)
  • module management

data capture:

  • hardware support (AVR)
  • signal handlers
  • sensors
  • modules/builtins

data display:

  • main loop
  • modules/builtins

In the project, there is a strong separation between data capture and display. Capturing data usually happens quickly and can happen often, it needs to be precise and reliable at the same time, therefore it's interrupt-driven.

Displayed data only needs to be updated after a new data piece arrives, and can take a long time. It doesn't need to be super-reliable (as long as the underlying data is fine). Therefore it's placed in the main loop and can be interrupted when new data comes.

To illustrate this separation, let's consider a wheel sensor pulse and its way to the display:

  1. physical: Sensor triggers an interrupt, AVR wakes up
  2. physical->signal: interrupt handler calls an appropriate signal handler (on_wheel_pulse)
  3. signal->sensor: Signal handler calls on_wheel_pulse_collect_data
  4. sensor: wheel.c collects the data and updates its pulse table
  5. signal calls the remaining data collectors and the interrupt returns

At this point, the program remembers what happened at which time. This event can happen multiple times without anything trying to display the data - it's fine because every event is remembered.

Once the main loop resumes, displaying happens:

  1. Main loop calls speed_redraw

    Pulse data is fetched from the memory

    Speed is calculated (takes a long time)

    Speed is displayed (takes a long time)

  2. Main loop calls other builtins and the currently visible module.

At any time a new interrupt can come and change the data a displayer uses - therefore it's important that no displayer ever modifies the data. In exchange, you can have race conditions here and not worry about them - they will fix themselves at the next display :)

Modules

Modules are short programs that are dedicated to getting and display data. They take input from sensors, timers or buttons and they display data on the screen.

The majority of a module code lives in modules/modulename.c, but it needs some hooks in signals.c and modules/base.c. This is a good point to start hacking around the project, see adding modules.

Sensors

The second most important part of the project. While modules remember and display data. sensors actually gather them.

Most of a sensor's code is contained in sensors/sensor.c with its sensors/sensor.h file. If the sensor defines a signal that is not defined yet, it may modify sensors.h and different code in avr/.

See adding sensors for more info.

preferences.h

This file contains the most important constants of the project. These constants are usually required by a builtin or a module and should be explained in detail there.

  • *PORT, *DIR, *PIN - these are pin designations on the microcontroller. All three of them must be present for any pin defined:

    PULSE* - wheel sensor pin

    CRANK* - crank sensor pin

    BUTTON* - interrupt splitter interrupt pin

    LEFT*, RIGHT*, SELECT* - button pins

    CLK*, SDA*, D_C*, SCE*, RST* - LCD pins

  • ONE_SECOND - project-wide constant defining the number of clock ticks in one second

  • METRIC_PULSE_DIST - default wheel circumference in mm

  • HIGH_PRECISION_CALCULATIONS, LONG_CALCULATIONS - switches for src/lib/calculations.h

  • FRAC_BITS - project-wide constant which belongs somewhere in the calculations

  • DEBUG - enables the debugging portions of the project

  • The sensors: Wheel sensor can't be disabled, therefore there is no switch for it at the moment. CRANK - the crank sensor

  • The rest are features that can be switched on and off. You can find the switches and their modules in user manual. Most of the features rely on some kind of sensors - they must be enabled first. Some features require special calculations as well.

Clone this wiki locally