AdSense

Friday, 26 July 2013

Atmega/Arduino: nRF24L01+ basics

(Deutsche Version)

Today I will write another "guest post". This time, I'm going to present the wireless modules called nRF24L01+ (or nRF24L01P) by Nordic Semiconductor. These modules can be bought very cheap on ebay. The modules are "Ultra low power 2.4GHz RF Transceivers" - so the can send AND receive and they work in 2.4GHz band. According to Nordic Semiconductor, they are very energy efficient and consume only 15mA. The transfer rate is up to 2MBit/s. The modules are interfaced using the SPI bus.

That's enough of theory, further information can be found in the datasheet, which is going to be important later.

For the nRF24L01+ there are "ready to use" libraries for Arduino and Atmega. As described  in my last post I prefer to use the Arduino IDE. So I'm going to explain how to use the modules with an Arduino board or an Atmega8 with Arduino libraries.


As an easy example, I will use the ping programm included in the Arduino library. The Arduino Uno sends its "system time" to the Atmega, which sends it back immediately. Then the Uno calculates the ping from its current "system time" and the received time.


Hardware

At first, we have to wire everything. The modules have 8 pins: VCC and GND for voltage supply (IMPORTANT: the modules need a supply voltage of ~3V), MISO, MOSI and SCK for SPI communication, CSN (tells the module that there is going to be a new command), CE and IRQ. IRQ is an interrupt pin and can be active low on 3 events: data has been received, data was send and ending failed. These interrupts can be (de)activated as desired.


Most of the modules have te following pin mapping:
IRQ     8  7  MISO
MOSI  6  5  SCK
CSN    4  3  CE
VCC    2  1  GND

These pins have to be connected to the Arduino Uno or the Atmega:
MISO to pn 12 of the Arduino (pin 18 of the Atmega, pin 50 for the Arduino Mega)
MOSI to pin 11 (pin 17, 51)
SCK to pin 13 (pin 19, 52)
IRQ to pin 2 or 3 (pin 4 or 5, there are too many interrupts for the Arduino Mega, so I will not specify a certain pin)
CSN to pin 7 (pin 13, 7)
CE to in 8 (pin 14, 8)



As alreday mentioned, the supply voltage has to be 3V. The signal pins accept 5V.

Software Arduino

For this example I used the example included in the library.

The library can be found HERE.

The following code runs on the Arduino board:

#include <SPI.h>
#include <Mirf.h>
#include <nRF24L01.h>
#include <MirfHardwareSpiDriver.h>

void setup(){
  Serial.begin(9600);
  
  //if CE and CSN pins aren't connected to the standard pins
  
  //Mirf.cePin = 7;
  //Mirf.csnPin = 8;
  
  Mirf.spi = &MirfHardwareSpi;
  Mirf.init();
  
  //set receive address   
  Mirf.setRADDR((byte *)"clie1");
  
  //set payload length   
  Mirf.payload = sizeof(unsigned long);
  
  //switch to 250kbit/s (0x26) (2Mbit => 0x0f)
  Mirf.configRegister(RF_SETUP, 0x26);
 
  //change channel
  //Mirf.channel = 10;

  //write settings to configuration
  Mirf.config();
  
  Serial.println("Beginning ... "); 
}

void loop(){
  unsigned long time = millis();
  
  Mirf.setTADDR((byte *)"serv1");
  
  Mirf.send((byte *)&time);
  
  while(Mirf.isSending()){
  }
  Serial.println("Finished sending");
  delay(10);
  while(!Mirf.dataReady()){
    if ( ( millis() - time ) > 1000 ) {
      Serial.println("Timeout on response from server!");
      return;
    }
  }
  
  Mirf.getData((byte *) &time);
  
  Serial.print("Ping: ");
  Serial.println((millis() - time));
  
  delay(1000);
}  
 
In the setup() function, the modul is initialised using the init() command. Then the address of the module and the payload length are set. The payload length tells how many data bytes are send when transmitting. Up to 32 bytes are possible but you shouldn't choose more than necessary. To increase the range of the modules I have set the transmission rate from 2MBit/s to 250kBit/s. Therefor the registers of the modules have to be accessed. Thanks to the library this can be done with only one line of code. The command configRegister(RF_SETUP, 0x26) writes the value 0x26 to the register RF_SETUP and sets the transmission rate. The names of the registers and their functions can be found in the datasheet (page 57 and following).
At the end of the setup, the settings are applied using the config() command.

The loop() is easy and self-explaining. With the setTADDR command you can choose to which address the data is sent. The send() command send the desired data (here it's the system time). The while loop let's the Arduino do nothing until transmission is completed. With the next while loop and the condition !Mirf.dataReady, the µC waits until the wireless modul hast received some data. getData() reads this data from the module.

Software Atmega8

#include <SPI.h>
#include <Mirf.h>
#include <nRF24L01.h>
#include <MirfHardwareSpiDriver.h>

void setup(){

  Mirf.spi = &MirfHardwareSpi;
  Mirf.init();
  
  //set receive address    
  Mirf.setRADDR((byte *)"serv1");
  
  //set payload length   
  Mirf.payload = sizeof(unsigned long);
  
  //switch to 250kbit/s (0x26) (2Mbit => 0x0f)
  Mirf.configRegister(RF_SETUP, 0x26);
  
  ///change channel
  //Mirf.channel = 10;

  //write settings to configuration
  Mirf.config();
  
}

void loop(){

  //cache for received data
  byte data[Mirf.payload];
   
  if(!Mirf.isSending() && Mirf.dataReady()){
         
    Mirf.getData(data);
         
    Mirf.setTADDR((byte *)"clie1");
   
    Mirf.send(data);
    
  }
}

The setup() function is almost the same as in the Arduino code, so I'm not going to explain it again.


The loop() is very short. The programm is running in a loop and waits for the wireless modul to receive some data (Mirf.dataReady()). The data is written to a cache, the target address is set and the data is sent back. The !Mirf.isSending() condition prevents overlapping on sending.


Of course, the code on the Atmega is not very efficient because the controller doesn't do anything but waiting for new data. It would be much better to use interrupts. I'm going to explain the usage of interrupts in my next post.

Thursday, 25 July 2013

C# WPF - 3D graphics

(Deutsche Version) Until now, I was not able to create 3D graphics. Today I found out that WPF is able to do this, too. Within 2 hours, I was able to create a small tank who can be controlled with the arrow keys. Creating 3D graphics with WPF is extremely simpel.

As you can see in the code, the main part of the program is the function Tank(), which creates all objects by hand. Basically, it is much easier to do these things with a 3D editor but for the moment, this will do fine.

Other functions I use are moveThread, this function moves the the tank and the function RefreshEverything which changes the position of the displayed tank. Here is the code for MainWindow.xaml.cs:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace Wpf3dTest1
{
    public partial class MainWindow : Window
    {
        Dictionary<Key, Boolean> pressedKeys = new Dictionary<Key, Boolean>();
        bool quit = false;
        public MainWindow()
        {
            InitializeComponent();

            pressedKeys.Add(Key.Left, false);
            pressedKeys.Add(Key.Right, false);
            pressedKeys.Add(Key.Up, false);
            pressedKeys.Add(Key.Down, false);

            this.KeyDown += MainWindow_KeyDown;
            this.KeyUp += MainWindow_KeyUp;

            this.Closing += OnWindowClosing;

            //create a Tank object and add to viewport
            mod = Tank(new Point3D(xPos, 0, 0), 2.2, 1.8, 0.7, 0.3, 0.25, 0.5, 0.25, new DiffuseMaterial(Brushes.Green), new DiffuseMaterial(Brushes.Black));
            viewport3D.Children.Add(mod);

            //start a Thread which moves the object
            Thread thread = new Thread(moveThread);
            thread.Start();
        }

        void MainWindow_KeyUp(object sender, KeyEventArgs e)
        {
            if (pressedKeys.ContainsKey(e.Key))
            {
                pressedKeys[e.Key] = false;
            }
        }

        void MainWindow_KeyDown(object sender, KeyEventArgs e)
        {
            if (pressedKeys.ContainsKey(e.Key))
            {
                pressedKeys[e.Key] = true;
            }
        }

        ModelVisual3D mod;

        private delegate void RefreshEverythingDelegate();
        private void RefreshEverything()
        {
            TranslateTransform3D translate = new TranslateTransform3D();
            translate.OffsetX = xPos;
            translate.OffsetZ = zPos;

            RotateTransform3D rotate = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1,0),angle));
            rotate.CenterX = xPos;
            rotate.CenterZ = zPos;


            Transform3DGroup transform = new Transform3DGroup();
            transform.Children.Add(translate);
            transform.Children.Add(rotate);
            mod.Transform = transform;
        }

        //Parameters for turn and move
        private double posIncrementor = 0.02;
        private double xPos = 0;
        private double zPos = 0;

        private double angle = 0;
        private double angleIncrementor = 0.4;

        private void moveThread()
        {
            while (!quit)
            {
                if (pressedKeys[Key.Up])
                {
                    xPos += posIncrementor * Math.Cos(angle * Math.PI/180) ;
                    zPos -= posIncrementor * Math.Sin(angle * Math.PI / 180);
                }
                if (pressedKeys[Key.Down])
                {
                    xPos -= posIncrementor * Math.Cos(angle * Math.PI / 180);
                    zPos += posIncrementor * Math.Sin(angle * Math.PI / 180);
                }
                if (pressedKeys[Key.Left])
                {
                    angle += angleIncrementor;
                }
                if (pressedKeys[Key.Right])
                {
                    angle -= angleIncrementor;
                }
                DispatcherOperation dispOp = this.viewport3D.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new RefreshEverythingDelegate(RefreshEverything));
                Thread.Sleep(10);
            }
        }

        public void OnWindowClosing(object sender, CancelEventArgs e)
        {
            quit = true;
        }

        private ModelVisual3D Tank(Point3D position, double length, double width, double height, double chainWidth, double chainHeight1, double chainHeight2, double chainNotLength, Material bodyMaterial, Material chainMaterial)
        {
            //Tank Body
            MeshGeometry3D bodyMesh = new MeshGeometry3D();
            Point3D a = new Point3D(position.X - length / 2, 0, position.Z + width / 2);
            Point3D b = new Point3D(position.X + length / 2, 0, position.Z + width / 2);
            Point3D c = new Point3D(position.X + length / 2, 0, position.Z - width / 2);
            Point3D d = new Point3D(position.X - length / 2, 0, position.Z - width / 2);
            Point3D e = new Point3D(position.X - length / 2, position.Y + height, position.Z + width / 2);
            Point3D f = new Point3D(position.X + length / 2, position.Y + height, position.Z + width / 2);
            Point3D g = new Point3D(position.X + length / 2, position.Y + height, position.Z - width / 2);
            Point3D h = new Point3D(position.X - length / 2, position.Y + height, position.Z - width / 2);
            BuildRectangle(bodyMesh, a, b, f, e, new Vector3D(0, 0, 1));
            BuildRectangle(bodyMesh, b, c, g, f, new Vector3D(1, 0, 0));
            BuildRectangle(bodyMesh, c, d, h, g, new Vector3D(0, 0, -1));
            BuildRectangle(bodyMesh, d, a, e, h, new Vector3D(-1, 0, 0));
            BuildRectangle(bodyMesh, e, f, g, h, new Vector3D(0, 1, 0));
            BuildRectangle(bodyMesh, a, d, c, b, new Vector3D(0, -1, 0));

            //Build the model object
            GeometryModel3D BodyModel = new GeometryModel3D(
            bodyMesh,
            bodyMaterial);


            //Chain 1
            MeshGeometry3D chain1Mesh = new MeshGeometry3D();//links, also -width
            a = new Point3D(position.X - length / 2 + chainNotLength, 0, position.Z - width / 2 - chainWidth);
            b = new Point3D(position.X - length / 2 + chainNotLength, 0, position.Z - width / 2);
            c = new Point3D(position.X + length / 2 - chainNotLength, 0, position.Z - width / 2 - chainWidth);
            d = new Point3D(position.X + length / 2 - chainNotLength, 0, position.Z - width / 2);
            e = new Point3D(position.X - length / 2, position.Y + chainHeight1, position.Z - width / 2 - chainWidth);
            f = new Point3D(position.X - length / 2, position.Y + chainHeight1, position.Z - width / 2);
            g = new Point3D(position.X + length / 2, position.Y + chainHeight1, position.Z - width / 2 - chainWidth);
            h = new Point3D(position.X + length / 2, position.Y + chainHeight1, position.Z - width / 2);
            Point3D i = new Point3D(position.X - length / 2, position.Y + chainHeight2, position.Z - width / 2 - chainWidth);
            Point3D j = new Point3D(position.X - length / 2, position.Y + chainHeight2, position.Z - width / 2);
            Point3D k = new Point3D(position.X + length / 2, position.Y + chainHeight2, position.Z - width / 2 - chainWidth);
            Point3D l = new Point3D(position.X + length / 2, position.Y + chainHeight2, position.Z - width / 2);

            BuildRectangle(chain1Mesh, a, b, d, c, new Vector3D(0, -1, 0));
            BuildRectangle(chain1Mesh, a, e, f, b, new Vector3D(-1, -1, 0));
            BuildRectangle(chain1Mesh, c, d, h, g, new Vector3D(1, -1, 0));
            BuildRectangle(chain1Mesh, a,c,g,e, new Vector3D(0, 0, -1));
            BuildRectangle(chain1Mesh, i, j, f, e, new Vector3D(-1, 0, 0));
            BuildRectangle(chain1Mesh, k, g, h, l, new Vector3D(1, 0, 0));
            BuildRectangle(chain1Mesh, e,g,k,i, new Vector3D(0, 0, -1));
            BuildRectangle(chain1Mesh, i, k, l, j, new Vector3D(0, 1, 0));

            //Build the model object
            GeometryModel3D Chain1Model = new GeometryModel3D(
            chain1Mesh,
            chainMaterial);


            //Chain 2
            MeshGeometry3D chain2Mesh = new MeshGeometry3D();//rechts, also +width
            a = new Point3D(position.X - length / 2 + chainNotLength, 0, position.Z + width / 2);
            b = new Point3D(position.X - length / 2 + chainNotLength, 0, position.Z + width / 2 + chainWidth);
            c = new Point3D(position.X + length / 2 - chainNotLength, 0, position.Z + width / 2);
            d = new Point3D(position.X + length / 2 - chainNotLength, 0, position.Z + width / 2 + chainWidth);
            e = new Point3D(position.X - length / 2, position.Y + chainHeight1, position.Z + width / 2);
            f = new Point3D(position.X - length / 2, position.Y + chainHeight1, position.Z + width / 2 + chainWidth);
            g = new Point3D(position.X + length / 2, position.Y + chainHeight1, position.Z + width / 2);
            h = new Point3D(position.X + length / 2, position.Y + chainHeight1, position.Z + width / 2 + chainWidth);
            i = new Point3D(position.X - length / 2, position.Y + chainHeight2, position.Z + width / 2);
            j = new Point3D(position.X - length / 2, position.Y + chainHeight2, position.Z + width / 2 + chainWidth);
            k = new Point3D(position.X + length / 2, position.Y + chainHeight2, position.Z + width / 2);
            l = new Point3D(position.X + length / 2, position.Y + chainHeight2, position.Z + width / 2 + chainWidth);

            BuildRectangle(chain2Mesh, a, b, d, c, new Vector3D(0, -1, 0));
            BuildRectangle(chain2Mesh, a, e, f, b, new Vector3D(-1, -1, 0));
            BuildRectangle(chain2Mesh, c, d, h, g, new Vector3D(1, -1, 0));
            BuildRectangle(chain2Mesh, b, f, h, d, new Vector3D(0, 0, 1));
            BuildRectangle(chain2Mesh, i, j, f, e, new Vector3D(-1, 0, 0));
            BuildRectangle(chain2Mesh, k, g, h, l, new Vector3D(1, 0, 0));
            BuildRectangle(chain2Mesh, f, j, l, h, new Vector3D(0, 0, 1));
            BuildRectangle(chain2Mesh, i, k, l, j, new Vector3D(0, 1, 0));

            //Build the model object
            GeometryModel3D Chain2Model = new GeometryModel3D(
            chain2Mesh,
            chainMaterial);


            //Build the whole object
            Model3DGroup model3DGroup = new Model3DGroup();
            model3DGroup.Children.Add(BodyModel);
            model3DGroup.Children.Add(Chain1Model);
            model3DGroup.Children.Add(Chain2Model);

            ModelVisual3D model = new ModelVisual3D();
            model.Content = model3DGroup;
            return model;
        }

        //Funktioniert auch mit nicht-Rechtecken, also wenn nicht alle Innenwinkel 90° sind.
        private void BuildRectangle(MeshGeometry3D geometry,Point3D a, Point3D b, Point3D c, Point3D d,Vector3D normal)
        {
            int baseIndex = geometry.Positions.Count;

            //Add vertices
            geometry.Positions.Add(a);
            geometry.Positions.Add(b);
            geometry.Positions.Add(c);
            geometry.Positions.Add(d);

            //Add normals
            geometry.Normals.Add(normal);
            geometry.Normals.Add(normal);
            geometry.Normals.Add(normal);
            geometry.Normals.Add(normal);

            //Add indices
            geometry.TriangleIndices.Add(baseIndex + 0);
            geometry.TriangleIndices.Add(baseIndex + 2);
            geometry.TriangleIndices.Add(baseIndex + 1);
            geometry.TriangleIndices.Add(baseIndex + 2);
            geometry.TriangleIndices.Add(baseIndex + 0);
            geometry.TriangleIndices.Add(baseIndex + 3);
        }
    }
}


And here the code for MainWindow.xaml:

<Window x:Class="Wpf3dTest1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Viewport3D Name="viewport3D">
            <Viewport3D.Camera>
                <PerspectiveCamera Position="20,10,10"
                                   LookDirection="-20,-10,-10"
                                   UpDirection="0,1,0"
                                   FieldOfView="45"
                                   NearPlaneDistance="1"
                                   FarPlaneDistance="100"></PerspectiveCamera>
            </Viewport3D.Camera>
            <ModelUIElement3D>
                <AmbientLight Color="White"></AmbientLight>
            </ModelUIElement3D>
        </Viewport3D>
    </Grid>
</Window>

Wednesday, 24 July 2013

Windows 7 - Desktop background depending on time of day with C#

(Deutsche Version) I wanted to have a desktop background which changes depending on the time of day. Windows is not able to do this and I did not want to install software. Luckily, I found a solution with C#.

I created a folder which contains 24 files: 00.jpg, 01.jpg, ... 23.jpg. These files are for the corresponding hours during the day (00.jpg is for 0 to 1 o'clock). The desktop background is set via C#, as described here. This is my code:

Ich wollte unbedingt einen Tageszeitabhängigen Hintergrund am PC haben. Windows kann das prinzipiell nicht, und ich wollte auch keine fremde Software installieren. Mit ein paar Zeilen C# war es dann jedoch ganz einfach möglich.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Runtime.InteropServices;

namespace TimeDependantBg
{
    class Program
    {
        [DllImport("user32.dll")]
        private static extern Int32 SystemParametersInfo(UInt32 uiAction, UInt32 uiParam, String pvParam, UInt32 fWinIni);

        private static UInt32 SPI_SETDESKWALLPAPER = 20;
        private static UInt32 SPIF_UPDATEINIFILE = 0x1;

        static void Main(string[] args)
        {
            int hour;
            string basePath = @"C:\Users\Udo\TimeDependantBg\";
            string fileName;
            while (true)
            {
                hour = DateTime.Now.Hour;

                fileName = basePath + (hour % 24).ToString("00") + ".jpg";

                if (System.IO.File.Exists(fileName))
                {
                    SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, fileName, SPIF_UPDATEINIFILE);
                }
                object lockObj = new object();
                lock (lockObj)
                {
                    Monitor.Wait(lockObj, 100000);// ms, check every 100 seconds
                }
            }
        }
    }
}


Afterwards, a shortcut to TimeDependantBg.exe is created in C:\Users\Udo\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup so the program will be started when the computer is starting.

Sunday, 21 July 2013

Using an ATMega as I2C master

(Deutsche Version) Using an ATMega as I2C master was definitely not easy. In the end, I was using an implementation of Manfred Langemann from which I copied the code right into my *.c file for the AVR Studio. To use an ATMega for I2C, you only have to copy the following lines to the beginning of the program:

#define F_CPU 16000000

#define TRUE 1
#define FALSE 0

#include <avr/interrupt.h>

//#include "General.h"
//#include "TWI_Master.h"

/****************************************************************************
  TWI State codes
****************************************************************************/
// General TWI Master staus codes                     
#define TWI_START                    0x08  // START has been transmitted 
#define TWI_REP_START                0x10  // Repeated START has been transmitted
#define TWI_ARB_LOST                0x38  // Arbitration lost

// TWI Master Transmitter staus codes                     
#define TWI_MTX_ADR_ACK                0x18  // SLA+W has been tramsmitted and ACK received
#define TWI_MTX_ADR_NACK            0x20  // SLA+W has been tramsmitted and NACK received
#define TWI_MTX_DATA_ACK            0x28  // Data byte has been tramsmitted and ACK received
#define TWI_MTX_DATA_NACK            0x30  // Data byte has been tramsmitted and NACK received

// TWI Master Receiver staus codes 
#define TWI_MRX_ADR_ACK                0x40  // SLA+R has been tramsmitted and ACK received
#define TWI_MRX_ADR_NACK            0x48  // SLA+R has been tramsmitted and NACK received
#define TWI_MRX_DATA_ACK            0x50  // Data byte has been received and ACK tramsmitted
#define TWI_MRX_DATA_NACK            0x58  // Data byte has been received and NACK tramsmitted

// TWI Slave Transmitter staus codes
#define TWI_STX_ADR_ACK                0xA8  // Own SLA+R has been received; ACK has been returned
#define TWI_STX_ADR_ACK_M_ARB_LOST    0xB0  // Arbitration lost in SLA+R/W as Master; own SLA+R has been received; ACK has been returned
#define TWI_STX_DATA_ACK            0xB8  // Data byte in TWDR has been transmitted; ACK has been received
#define TWI_STX_DATA_NACK            0xC0  // Data byte in TWDR has been transmitted; NOT ACK has been received
#define TWI_STX_DATA_ACK_LAST_BYTE    0xC8  // Last data byte in TWDR has been transmitted (TWEA = “0”); ACK has been received

// TWI Slave Receiver staus codes
#define TWI_SRX_ADR_ACK                0x60  // Own SLA+W has been received ACK has been returned
#define TWI_SRX_ADR_ACK_M_ARB_LOST    0x68  // Arbitration lost in SLA+R/W as Master; own SLA+W has been received; ACK has been returned
#define TWI_SRX_GEN_ACK                0x70  // General call address has been received; ACK has been returned
#define TWI_SRX_GEN_ACK_M_ARB_LOST    0x78  // Arbitration lost in SLA+R/W as Master; General call address has been received; ACK has been returned
#define TWI_SRX_ADR_DATA_ACK        0x80  // Previously addressed with own SLA+W; data has been received; ACK has been returned
#define TWI_SRX_ADR_DATA_NACK        0x88  // Previously addressed with own SLA+W; data has been received; NOT ACK has been returned
#define TWI_SRX_GEN_DATA_ACK        0x90  // Previously addressed with general call; data has been received; ACK has been returned
#define TWI_SRX_GEN_DATA_NACK        0x98  // Previously addressed with general call; data has been received; NOT ACK has been returned
#define TWI_SRX_STOP_RESTART        0xA0  // A STOP condition or repeated START condition has been received while still addressed as Slave

// TWI Miscellaneous status codes
#define TWI_NO_STATE                0xF8  // No relevant state information available; TWINT = “0”
#define TWI_BUS_ERROR                0x00  // Bus error due to an illegal START or STOP condition

#define TWIM_READ    1
#define TWIM_WRITE   0

/*******************************************************
 Public Function: TWIM_Init

 Purpose: Initialise the TWI Master Interface

 Input Parameter:
     - uint16_t    TWI_Bitrate (Hz)

 Return Value: uint8_t
     - FALSE:    Bitrate too high
     - TRUE:        Bitrate OK

*******************************************************/
uint8_t TWIM_Init (uint32_t TWI_Bitrate)
    {
/*
** Set TWI bitrate
** If bitrate is too high, then error return
*/
    TWBR = ((F_CPU/TWI_Bitrate)-16)/2;
    if (TWBR < 11) return FALSE;

    return TRUE;
    }
/*******************************************************
 Public Function: TWIM_Start

 Purpose: Start the TWI Master Interface

 Input Parameter:
     - uint8_t    Device address
     - uint8_t    Type of required Operation:
                TWIM_READ: Read data from the slave
                TWIM_WRITE: Write data to the slave

 Return Value: uint8_t
      - TRUE:        OK, TWI Master accessible
     - FALSE:    Error in starting TWI Master

*******************************************************/
uint8_t TWIM_Start (uint8_t Address, uint8_t TWIM_Type)//1 = read, 0 = write
    {
    uint8_t        twst;
/*
** Send START condition
*/
    TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
/*
** Wait until transmission completed
*/
    while (!(TWCR & (1<<TWINT)));
/*
** Check value of TWI Status Register. Mask prescaler bits.
*/
    twst = TWSR & 0xF8;
    if ((twst != TWI_START) && (twst != TWI_REP_START)) return FALSE;
/*
** Send device address
*/
    TWDR = (Address<<1) + TWIM_Type;
    TWCR = (1<<TWINT)|(1<<TWEN);
/*
** Wait until transmission completed and ACK/NACK has been received
*/
    while (!(TWCR & (1<<TWINT)));
/*
** Check value of TWI Status Register. Mask prescaler bits.
*/
    twst = TWSR & 0xF8;
    if ((twst != TWI_MTX_ADR_ACK) && (twst != TWI_MRX_ADR_ACK)) return FALSE;

    return TRUE;
    }
/*******************************************************
 Public Function: TWIM_Stop

 Purpose: Stop the TWI Master

 Input Parameter: None

 Return Value: None

*******************************************************/
void TWIM_Stop (void)
    {
/*
** Send stop condition
*/
    TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
/*
** Wait until stop condition is executed and bus released
*/
    while (TWCR & (1<<TWINT));
    }
/*******************************************************
 Public Function: TWIM_Write

 Purpose: Write a byte to the slave

 Input Parameter:
     - uint8_t    Byte to be sent

 Return Value: uint8_t
      - TRUE:        OK, Byte sent
     - FALSE:    Error in byte transmission

*******************************************************/
uint8_t TWIM_Write (uint8_t byte)
    {
    uint8_t   twst;
/*
** Send data to the previously addressed device
*/
    TWDR = byte;
    TWCR = (1<<TWINT)|(1<<TWEN);
/*
** Wait until transmission completed
*/
    while (!(TWCR & (1<<TWINT)));
/*
** Check value of TWI Status Register. Mask prescaler bits
*/
    twst = TWSR & 0xF8;
    if (twst != TWI_MTX_DATA_ACK) return 1;

    return 0;
    }
/*******************************************************
 Public Function: TWIM_ReadAck

 Purpose: Read a byte from the slave and request next byte

 Input Parameter: None

 Return Value: uint8_t
      - uint8_t    Read byte

*******************************************************/
uint8_t TWIM_ReadAck (void)
    {
    TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
    while (!(TWCR & (1<<TWINT)));   

    return TWDR;
    }
/*******************************************************
 Public Function: TWIM_ReadAck

 Purpose: Read the last byte from the slave

 Input Parameter: None

 Return Value: uint8_t
      - uint8_t    Read byte

*******************************************************/
uint8_t TWIM_ReadNack (void)
    {
    TWCR = (1<<TWINT)|(1<<TWEN);
    while(!(TWCR & (1<<TWINT)));
   
    return TWDR;
    }

Read acceleration sensor MPU-6050 with Raspberry PI

(Deutsche Version) To read the MPU-6050 via a Raspberry PI, a I²C library is needed. I used the bcm2835 library (http://www.airspayce.com/mikem/bcm2835/). To read the acceleration, i used the following C code. It is very important to disable the sleep mode (set register 107 to 0), otherwise the sensor will not work properly.

If you want to use an ATMega instead of a Raspberry PI, see this post.

#include <bcm2835.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>

int main (int atgc, char** argv)
{
    bcm2835_init();
    bcm2835_i2c_begin();

    char addr = 0x68;

    //I found this address somewhere in the internet...
    char buf[1];
    char regaddr[2];
    int x = 0;
    int ret;

    bcm2835_i2c_setSlaveAddress(addr);

    //disable sleep mode!!!!!

    regaddr[0] = 107;
    regaddr[1] = 0;
    //This is the basic operation to write to an register
    //regaddr[0] is the register address
    //regaddr[1] is the value
    bcm2835_i2c_write(regaddr, 2);
   
    regaddr[0] = 59;
    ret = BCM2835_I2C_REASON_ERROR_DATA;
    while(ret != BCM2835_I2C_REASON_OK)
    {
        //This is the basic operation to read an register
        //regaddr[0] is the register address
        //buf[0] is the value
        bcm2835_i2c_write(regaddr, 1);
        ret = bcm2835_i2c_read(buf, 1);
    }
    x = buf[0]<<8;

    regaddr[0] = 60;
    ret = BCM2835_I2C_REASON_ERROR_DATA;
    while(buf[0] == 99)
    {
        bcm2835_i2c_write(regaddr, 1);
        ret = bcm2835_i2c_read(buf, 1);
    }
    x += buf[0];


    //because of the sign, we have here 32-bit integers,
    //the value is 16-bit signed.

    if (x & 1<<15)
    {
        x -= 1<<16;
    }

    double x_val = x;
    x_val = x_val / 16384;

    //This is only valid if the accel-mode is +- 2g
    //The range can be controlled via the 
    //GYRO_CONFIG and ACCEL_CONFIG registers

    printf("accel: %g\n", x_val);

    bcm2835_i2c_end();
}


All other applications should be simple with this code, the Register Map will provide further information: http://www.invensense.com/mems/gyro/documents/RM-MPU-6000A.pdf