Friday, January 30, 2015

A spinning top for your LCD





Sometimes we need to wait for the end of a task or for an event to occur and at the same time to keep the user informed that the system is not hung. This is the right occasion for a spinning top!

In this post we propose a very simple one. Indeed, it is a good occasion to see how to use the character generator of the LCD and insert new character. The module that we are going to use is based on the famous Hitachi HD44780 chip. This chip has the possibility to increase its char set with 4 additional user defined chars. This easily done using the command createChar which take in input the id code for the new char (an integer between 0 and 3) and an array of 8 bytes which are interpreted as a bitmap (here we use the 5x8 charset).

What you need
  • 1x Arduino Uno
  • 1x LCD module
Just that! For more on the LCD module you can also have a look here.

Configuration
Modify the definitions of the pins to adapt to your LCD module. In order to change the speed of the spinner decrease the value of FRAMETIME. This variable control the frame rate. Indeed, frame rate is approx 1/FRAMETIME.

The sketch
/**
 * Spinning top
 *
 * \author Enrico Formenti
 * \version 1.0
 * \date 30 january 2015
 *  \copyright BSD license, check the License page on the blog for more information. All this text must be 
 *  included in any redistribution.
 *  <br><br>
 *  See macduino.blogspot.com for more details.
 */

#include <LiquidCrystal.h>

// modify the definitions below according to your LCD module
 
#define LCD_RS_PIN 7
#define LCD_ENABLE_PIN 6
#define LCD_DATA4_PIN 5
#define LCD_DATA5_PIN 4
#define LCD_DATA6_PIN 3
#define LCD_DATA7_PIN 2

LiquidCrystal lcd(LCD_RS_PIN, LCD_ENABLE_PIN, LCD_DATA4_PIN,
                  LCD_DATA5_PIN, LCD_DATA6_PIN, LCD_DATA7_PIN);


// modify the value below to change the frame rate
// framerate per second is approx 1/FRAMETIME 
#define FRAMETIME 120


// the backslash character is not in the standard char set
// of the LCD so let's redefine it

uint8_t backslash[8] = {
    0b00000,
    0b10000,
    0b01000,
    0b00100,
    0b00010,
    0b00001,
    0b00000,
    0b00000
};

// we use special character 0 here
char spinningTop[] = {'/', '-', 0, '|'};
uint8_t k;

void setup() {
  // put your setup code here, to run once:
  k = 0; // init spinner
  
  // init screen
  lcd.begin(16, 2);
  lcd.createChar(0, backslash);
  lcd.clear();
  lcd.print("Look at me... ");
}

void loop() {
  // update spinner and...
  lcd.setCursor(14,0);
  lcd.write(spinningTop[k%4]);
  k = (k+1) & 0b11;
  // ...waste some time to let user see it spinning
  delay(FRAMETIME);
}

Suggestions
Remark that our implementation of the spinner uses busy waiting. In order to avoid it one can use interrupts. However, if you decide to use interrupts, recall that they can interfere with other modules or sensors and that Arduino Uno only has two of them!

Enjoy!

Sunday, January 11, 2015

FM radio receiver for your Arduino (TEA5767/TEA5768/TEA5757)
(Part 3/4)




Abstract

In this third part we provide a class for the FM module and a simple test program. The next post will provide a basic FM Radio front-end as an application of the FM module.

The FM module
As we have already said in past posts, this is a decoration module for a module based on the TEA5767HN chip. In particular, it fixes some important parameters such as the clock and the bus. Here are the main characteristics of the module:
Main characteristics
 
Supply voltage:
3V-6V
Working current:
25mA
Audio output:
2x10mW
Clock:
32768Hz
Bus:
I2C
I2C address:
0x60

We have therefore conceived a wrap class which hides all the details about TEA5767HN, sets up the above constraints and provides few basic methods for the final user. Please look into the code below for the methods and for documentation. The pinout of the module is given here.


We start with the file FM_Module.h:
/**
 *  \file FM_Module.h
 *
 *  \brief A class for the FM module based on the TEA5767HN chip.
 *
 *  \author Enrico Formenti
 *  \date 23 dec 2014
 *  \version 1.0
 *  \copyright BSD license, check the License page on the blog for more information. All this text must be
 *  included in any redistribution.
 *  <br><br>
 *  See macduino.blogspot.com for more details.
 */

#ifndef ____FM_Module__h
#define ____FM_Module__h

#include "TEA5767HN.h"

/**
 * \class FM_Module
 * \brief A class for the FM module based on the TEA5767HN chip.
 * \details This is a simple class for a FM module based on the TEA5767HN chip. The class implements
 * faithfully the module functioning. Moreover, it provides some basic commands for FM radio playing.
*/

class FM_Module {
    /**
     * \var TEA5767HN FM
     * \brief This is the internal FM receiver which pratically does all the job.
     * \see TEA5767HN
     */
    TEA5767HN FM;
    /**
     * \var bool standby
     * \brief Flag for signalling if the standby is enabled. This is a quick trick to avoid unuseful call to the TEA5767HN module.
     */
    bool _standby;
public:
    /**
     * \fn void begin()
     * \brief Initialize the FM module. This command has always to be issued before using the module.
     */
    void begin();
    /**
     * \fn void setFrequency(float f)
     * \brief Set the frequency of the FM receiver.
     * @param float f The desired frequency in MHz.
     * \see getFrequency()
     */
    inline void setFrequency(float f) { FM.setFrequency(f); };
    /**
     * \fn float getFrequency()
     * \brief Returns the frequency on which the receiver is tuned
     * \return Returns the frequency in MHz on which the tuner is tuned.
     */
    inline float getFrequency() { return FM.getFrequency(); };
    /**
     * \fn bool searchForward()
     * \brief Search for a station in the direction of the superior band limit. 
     * \return \c true if the search is successful, \c false otherwise.
     * \see searchBackward()
     *
     * Search for a station in the direction of the superior band limit. If the superior band limit is reached, then the search restarts from the inferior band limit.
     * The search is abandoned after one complete loop.
     */
    inline bool searchForward() { return FM.search(SEARCH_DIR_FORWARD, SEARCH_LEV_MEDIUM); };
    /**
     * \fn bool searchBackward()
     * \brief Search for a station in the direction of the inferior band limit.
     * \return \c true if the search is successful, \c false otherwise.
     * \see searchForward()
     *
     * Search for a station in the direction of the inferior band limit. If the inferior band limit is reached, then the search restarts from the superior band limit.
     * The search is abandoned after one complete loop.
     */
    inline bool searchBackward() { return FM.search(SEARCH_DIR_BACKWARD, SEARCH_LEV_MEDIUM); };
    /**
     * \fn void refresh()
     * \brief Refreshes the internal state of the FM module.
     *
     * Refreshes the internal state of the FM module. This is particularly useful before issuing commands like isStereo() or getLevel()
     */
    inline void refresh() { FM.read(); }
    /**
     * \fn void standby()
     * \brief Put the FM module in standby state
     */
    inline void standby() { if(!_standby){_standby = true; FM.standby(STANDBY_ON); FM.write();}};
    /**
     * \fn void play()
     * \brief The tuner is set into play state.
     */
    inline void play() { if(_standby) {FM.standby(STANDBY_OFF); FM.write(); _standby=false;}};
    /**
     * \fn bool isStereo()
     * \brief This function queries for the current quality of the reproduction: stereo or mono.
     * \return \c true if the reproduction is stereo, \c false if it is mono.
     * \see refresh()
     * \warning It is better to issue a \c refresh() command before calling this function in order to have fresh informations from the receiver.
     */
    inline bool isStereo(){ return FM.isStereo(); };
    /**
     * \fn uint8_t getLevel()
     * \brief This function returns the output level of the receiver.
     * \return An integer between 0 and 15 indicating the output level of the receiver.
     * \see refresh()
     * \warning It is better to issue a \c refresh() command before calling this function in order to have fresh informations from the receiver.
     */
    inline uint8_t getLevel() { return FM.getLevel(); };
};


#endif /* defined(____FM_Module__h) */


Here is the file FM_Module.cpp:
/**
 *  \file FM_Module.cpp
 *
 *  \brief Implementation of a class for the FM module based on the TEA5767HN chip.
 *
 *  \author Enrico Formenti
 *  \date 18 dec 2014
 *  \version 1.0
 *  \copyright BSD license, check the License page on the blog for more information. All this text must be
 *  included in any redistribution.
 *  <br><br>
 *  See macduino.blogspot.com for more details.
 */

#include "FM_Module.h"

void FM_Module::begin() {
    Wire.begin();               // Init I2C bus
    FM.begin(TEA576X_XTAL_32768Hz, TEA5767HN_I2CBUS); // Init TEA receiver
    _standby = false;
}

And finally a very basic test sketch :-)
#include <Wire.h>
#include <SPI.h>
#include "FM_Module.h"
#include "TEA5767HL.h"

FM_Module mod;
TEA5767HL tea;

void setup() {

  mod.begin();

  mod.searchForward();
  
  mod.play();
}

void loop() {
  // listening to Radio don't care here ;-)

}


What's next

As promised in the last post of this series we will see a basic FM Radio front-end with LCD support.

See also