Call OpenCV functions from C#.net (Bitmap to Mat and Mat to Bitmap)


This is the second article of the article series which provide answers to following question! How to call OpenCV functions from C#.net or VB.net. Specially this article describes, how to pass System.Drawing.Bitmap to OpenCV and get a resultant image as System.Drawing.Bitmap from OpenCV.


Note that System.Drawing.Bitmap is the class type which allow you to manipulate images in C# while OpenCV treat images as cv::Mat (matrix). Therefore we need a way to convert from Bitmap to Mat vice versa in order to process and show processed images. This is the place where wrapper involved. For more details about wrappers, please refer previous article. 


From Previous Article...
So now we are going to create this wrapper for our application. Since we are dealing with .net framework, we can use CLR (Common Language Runtime) technique to create this wrapper. First you have to create a CLR project in Visual Studio. This post will describe, how to call Opencv functions from winfrom/C# and apply an Opencv filter to an image and show the Opencv window from winform.


Download complete Visual Studio project.

Step 1 - Create CLI Project

First of all we need to have a CLI project where we can call C++ functions from .net. You can follow the steps from previous article to create a CLI project.

Step 2 - Create converter function from Bitmap to Mat

Now we need to convert System.Drawing.Bitmap to cv::Mat. To do this conversion, we need to get to the primitive level of both data type. That's mean, we can simply think every image has created from a set of bytes. So bytes can live in both C++ and C#. Therefore the cv::Mat should be created from the set of bytes of Bitmap. Simply copy all bytes from Bitmap to Mat, finally it will create Mat from Bitmap. Use following function to do this conversion.

Mat BitmapToMat(System::Drawing::Bitmap^ bitmap)
{
    IplImage* tmp;

    System::Drawing::Imaging::BitmapData^ bmData = bitmap->LockBits(System::Drawing::Rectangle(0, 0, bitmap->Width, bitmap->Height), System::Drawing::Imaging::ImageLockMode::ReadWrite, bitmap->PixelFormat);
    if (bitmap->PixelFormat == System::Drawing::Imaging::PixelFormat::Format8bppIndexed)
    {
        tmp = cvCreateImage(cvSize(bitmap->Width, bitmap->Height), IPL_DEPTH_8U, 1);
        tmp->imageData = (char*)bmData->Scan0.ToPointer();
    }

    else if (bitmap->PixelFormat == System::Drawing::Imaging::PixelFormat::Format24bppRgb)
    {
        tmp = cvCreateImage(cvSize(bitmap->Width, bitmap->Height), IPL_DEPTH_8U, 3);
        tmp->imageData = (char*)bmData->Scan0.ToPointer();
    }

    bitmap->UnlockBits(bmData);

    return Mat(tmp);
}

Step 3 - Add System.Drawing namespace reference.

Once you add above function to your CLI project, you will get an error on System::Drawing::Bitmap. The reason for this error is that the project has no reference to System::Drawing::Bitmap. Follow below steps to add the reference.

Open project properties of CLI project.
















Select Common Properties then References , Click on Add New References... It will open a window that can select dll files where you can add as references.















Select Framework under Assembly category. Then mark System.Drawing.









Now the project has referenced System.Drawing, and you should not see any compile errors in the function.

Step 4 - Create converter function from Mat to Bitmap

Same as previous conversion, we need to perform this conversion from the primitive level. That's mean we need to reconstruct the Bitmap from Mat's image data bytes. Use following function to convert from cv::Mat to System.Drawing.Bitmap.

System::Drawing::Bitmap^ MatToBitmap(Mat srcImg){
    int stride = srcImg.size().width * srcImg.channels();//calc the srtide
    int hDataCount = srcImg.size().height;
   
    System::Drawing::Bitmap^ retImg;
       
    System::IntPtr ptr(srcImg.data);
   
    //create a pointer with Stride
    if (stride % 4 != 0){//is not stride a multiple of 4?
        //make it a multiple of 4 by fiiling an offset to the end of each row


       
//to hold processed data
        uchar *dataPro = new uchar[((srcImg.size().width * srcImg.channels() + 3) & -4) * hDataCount];

        uchar *data = srcImg.ptr();

        //current position on the data array
        int curPosition = 0;
        //current offset
        int curOffset = 0;

        int offsetCounter = 0;

        //itterate through all the bytes on the structure
        for (int r = 0; r < hDataCount; r++){
            //fill the data
            for (int c = 0; c < stride; c++){
                curPosition = (r * stride) + c;

                dataPro[curPosition + curOffset] = data[curPosition];
            }

            //reset offset counter
            offsetCounter = stride;

            //fill the offset
            do{
                curOffset += 1;
                dataPro[curPosition + curOffset] = 0;

                offsetCounter += 1;
            } while (offsetCounter % 4 != 0);
        }

        ptr = (System::IntPtr)dataPro;//set the data pointer to new/modified data array

        //calc the stride to nearest number which is a multiply of 4
        stride = (srcImg.size().width * srcImg.channels() + 3) & -4;

        retImg = gcnew System::Drawing::Bitmap(srcImg.size().width, srcImg.size().height,
            stride,
            System::Drawing::Imaging::PixelFormat::Format24bppRgb,
            ptr);
    }
    else{

        //no need to add a padding or recalculate the stride
        retImg = gcnew System::Drawing::Bitmap(srcImg.size().width, srcImg.size().height,
            stride,
            System::Drawing::Imaging::PixelFormat::Format24bppRgb,
            ptr);
    }
   
    array^ imageData;
    System::Drawing::Bitmap^ output;

    // Create the byte array.
    {
        System::IO::MemoryStream^ ms = gcnew System::IO::MemoryStream();
        retImg->Save(ms, System::Drawing::Imaging::ImageFormat::Png);
        imageData = ms->ToArray();
        delete ms;
    }

    // Convert back to bitmap
    {
        System::IO::MemoryStream^ ms = gcnew System::IO::MemoryStream(imageData);
        output = (System::Drawing::Bitmap^)System::Drawing::Bitmap::FromStream(ms);
    }

    return output;
}



Now we can convert cv::Mat to System.Drawing.Bitmap. You can call BitmapToMat() function to convert C# Bitmap and use converted Mat to do image processing from OpenCV, so OpenCV will produce processed image as Mat type, here you can use MatToBitmap() function to return Bitmap type object back to C#. Now you can treat this processed Bitmap as a normal Bitmap in C#.

Step 5 - Use converter functions and do image processing.

You can use above 2 functions and your own image processing code to create processed image from an input image. Here, I am trying to apply "medianBlur" opencv filter to C# Bitmap image. This is a sample code which show how to use these conversion functions along some opencv functions.


System::Drawing::Bitmap^ MyOpenCvWrapper::ApplyFilter(System::Drawing::Bitmap^ bitmap){
    Mat image = BitmapToMat(bitmap);//convert Bitmap to Mat
    if (!image.data){
        return nullptr;
    }

    Mat dstImage;//destination image

    //apply the Filter
    medianBlur(image, dstImage, 25);

    //convert Mat to Bitmap
    System::Drawing::Bitmap^ output = MatToBitmap(dstImage);

    return output;
}

Step 6 - Call from C#

Now you can call this function from C# by passing a Bitmap type object to the method and it will return the filter applied image back as a Bitmap. So you can use this returned Bitmap as a normal Bitmap in C#.

//open jpg file as Bitmap
Bitmap img = (Bitmap)Bitmap.FromFile(@"C:\Users\Public\Pictures\Sample Pictures\Tulips.jpg");

OpenCvDotNet.MyOpenCvWrapper obj = new OpenCvDotNet.MyOpenCvWrapper();
Bitmap output = obj.ApplyFilter(img);//call opencv functions and get filterred image

 output.Save("test.jpg");//save processed image

If you put this code in an event handler in C#, it will looks like this,

Step 7 - Use returned Bitmap from OpenCV in C#

you can use this returned Bitmap as a normal Bitmap in C# such as setting the image for PictureBox. Following code will open a Bitmap from a file, process in opencv to apply filter and show the results in a C# PictureBox control.

private void btnOpen_Click(object sender, EventArgs e)
{
    //allow user to open jpg file
    OpenFileDialog dlogOpen = new OpenFileDialog();
    dlogOpen.Filter = "Jpg Files|*.jpg";
    if (dlogOpen.ShowDialog() != System.Windows.Forms.DialogResult.OK)
        return;

    //open jpg file as Bitmap
    Bitmap img = (Bitmap)Bitmap.FromFile(dlogOpen.FileName);

    pbSrcImg.Image = img;//set picture box image to UI

    OpenCvDotNet.MyOpenCvWrapper processor = new OpenCvDotNet.MyOpenCvWrapper();
    Bitmap processedImg = processor.ApplyFilter(img);//call opencv functions and get filterred image

    pbDstImage.Image = processedImg;//set processed image to picture box
}

Where pbSrcImg and pbDstImage are PictureBox UI controls in C#. Once you open a jpg image it will show the UI as follow,



Download complete Visual Studio project.


From Previous Article...
So now we are going to create this wrapper for our application. Since we are dealing with .net framework, we can use CLR (Common Language Runtime) technique to create this wrapper. First you have to create a CLR project in Visual Studio. This post will describe, how to call Opencv functions from winfrom/C# and apply an Opencv filter to an image and show the Opencv window from winform.

Continue Reading...

Call OpenCV functions from C#.net

OpenCV is a C++ library, .net is different platform. Therefore, how to call Opencv functions from .net or how to call Opencv functions from C# or vb.net. This is possible because remember we are using Visual C++ for our Opencv projects and VC++ also a part of .net platform. This will allow you to wrap our specific opencv functions to be able to call from C#. A wrapper means an intermediate party which act as a communicator between 2 different sides. Simply you can use this technique to keep your front end GUI in C#, vb.net or WPF while keeping opencv as the back end to do image processing.

So now we are going to create this wrapper for our application. Since we are dealing with .net framework, we can use CLR (Common Language Runtime) technique to create this wrapper. First you have to create a CLR project in Visual Studio.

This post will describe, how to call Opencv functions from winfrom/C# and apply an Opencv filter to an image and show the Opencv window from winform.

Step 1 – Create a CLR project

Click on File-->New Project and select CLR under Visual C++ then select Class Library.

Then you will get a sample header file and cpp file for your wrapper class where you have to put all the functions' headers in class definition (in .h file) to be able to call from C#.

Step 2 -Apply OpenCV Configurations to the project.

 You can set all the project properties for Building and Linking a normal Opencv C++ project or you can import all the properties at once from an existing Property sheet as explained in this article.

Step 3 - Create CLR functions to call from C#


Now create a method in CLR class to call from C#. Here we need to keep something in mind, that is, C# deal with pointers (unless you specify as not a pointer/reference). Therefor all the parameters for this method should be pass as a pointer. But for now we will create a method without parameters.

Type following code in your class
    public:
        void ApplyFilter();


Now ApplyFilter is the method that we are going to call from C# using an object created from MyOpenCvWrapper class.

Step 4 - Include Opencv headers

Add all the header files of opencv that you need for this class, Here we just need cv.h and highgui.h. Remember to add include derivatives to your header file

#include <opencv\cv.h>
#include <opencv\highgui.h>


Step 5 - Code for the method

Open your cpp file and enter following code. This is the function that we are going to call from opencv and this is the code to do image processing. you can follow normal way to implement a class in C++.
#include "OpenCvDotNet.h"

using namespace OpenCvDotNet;
using namespace cv;

void MyOpenCvWrapper::ApplyFilter(){
    Mat image = imread("C:\\Pictures\\Hydrangeas.jpg");
    Mat dstImage;

    if (!image.data){
        return;
    }

    //Apply the Filter
    GaussianBlur(image, dstImage, Size(31, 31), 0);

    namedWindow("Image Window", CV_WINDOW_KEEPRATIO);
    imshow("Image Window", dstImage);
}



Code Explanation -
This code will open the image named "Hydrangeas.jpg" from "C:\Pictures" directory and check whether image opened successfully or not. Then it will apply a filter and show the result in an opencv window.

Step 6 - Add reference to C# project

Create and add a new project to your solution by Right click on your solution.

Now you can create either "Windows Form Application" or "WPF Application" or any other project type to use your opencv code. For this article I am using "Windows Form Application". After adding Winform project to your solution, your solution explorer should has 2 projects (CLR project and winform project).


Now you can Right click on Reference on WinFormProject and add reference to CLI project. You can find your project under Solutions/Project.

Step 6 - Call OpenCV from Winform project

Now you have made the link between winform and cli project which includes opencv calls. Note that the architecture (32bit or 64bit) of both projects should be same and the .net framework versions should be same on both projects to build your whole solution.

Use the class that we created from CLI project to create an object in C# and call the method that we created in CLI project from C#. then you should see the success !

I just added a button to my winform and call the method from there.

private void button1_Click(object sender, EventArgs e)
{
   OpenCvDotNet.MyOpenCvWrapper obj = new OpenCvDotNet.MyOpenCvWrapper();
   obj.ApplyFilter();
}

Step 7 - Run and test

Now you can run your winform application. Remember to set the winform project as the "Startup Project"
Then once you clicked on the button, you should see an opencv window showing a blur image.



This is a very simple way to call and get an output from opencv using winform/C#. My next post will describe how to pass a C# System.Drawing.Bitmap to CLI project and get the output image as a System.Drawing.Bitmap from the Opencv CLI.
Continue Reading...

Create OpenCV property sheets in Visual Studio to store properties



OpenCV is a very famous open source image processing library. Most of the Windows users are using OpenCV on Visual Studio IDE which provides C/C++ coding. Anyway You have to set some properties in each and every Visual Studio project, if you need to use OpenCV. This is very hard to me :). Because we have to set number of properties each and every times when you are creating a new project.

The solution for this is "Create a property sheet which can hold all the OpenCV properties". Following steps will describes How to create an OpenCV property sheet in Visual Studio 2013.


Step 1 - Create a new C++ project

You can follow normal steps to create a new C++ project. File --> New --> Project

Step 2 - Create new property sheet

First you have to open "Property Manager" window to manage all the property sheets for this project. Click on View --> Other Window --> Property Manager.

Then you will see a window similar to following window.
Here we have to create 2 property sheets for Debug and Release. Also If your project is 64 bit then you will have another 2 items as "Debug | x64" and "Release | x64", then you have to create two 64 bit property sheets separately. Anyway this article will guide you to create 32 bit property sheets.

Now Right click on "Debug | Win32" and select "Add New Project Property Sheet..." and give a name for your property sheet (Here I am using OpenCVDebug as the name).
Then you will see your new property sheet on Property Manager.

Step 3 - Set OpenCV properties (Debug)

Now we are going to set all the opencv properties for Debug mode. Right click on your new Property sheet and select Properties. There are 3 things that you have to set in here.
  1. OpenCV include directory
  2. OpenCV library directory
  3. OpenCV libraries

1. Setting OpenCV include directories

Go to General tab under C/C++ and click on Edit  under Additional Include Directories. Now set the directory path of your opencv include directory. (You can find this directory under build folder in opencv folder)

2. Setting OpenCV library directory

Go to General tab under Linker and add a new directory path for the Additional Library Directories as the library directory which includes 32bit lib files for your compiler version. Here I am using Visual Studio 2013 therefore I have to select vc12 and x86 since I am crating 32bit application.

3. Setting OpenCV libraries

Go to Input tab under Linker to set libraries for the opencv. Click Edit on Additional Dependencies and enter all the lib files' names which are ended with d (for an example opencv_core2410d.lib) inside your lib directory that you mentioned on "Setting Opencv library directory" step. Here stands for Debug.
Now all the properties have set to build an opencv project in Debug mode. If you need to build in Release mode you have to create a new separate property sheet under Release | Win32  and follow same steps as I mentioned above to set properties except 3rd step. Here you have to enter all the lib file names which are not ended with d (for an example opencv_core2410.lib)

Step 4 - Save and reuse Property sheets

Now click on Save button on the Property Manager to save your property sheets. Then you can find your property sheets where you saved (Here project folder).
Now assume that you have created a new Visual Studio OpenCV project, then you don't need to set all these properties again and again. You just needed to import these properties from these saved property sheets by clicking on "Add Existing Property Sheet..". So all the properties will be automatically set for your project. This is too easier than just setting these properties repeatably.
Thanks for reading this article and hope this will increase your developing speed !


Continue Reading...

How to create bootable USB pen drive

Bootable pen drive is a nice way to install operating systems to your machine. Only you need to do is just create bootable pen drive as describe here and change boot drive order to boot from USB, then you are ready to install any operating system from USB!

Here, I am going to describe How to create bootable USB pen drive to instal Ubuntu 13.04 64bit using one of nice software in Windows. This software supports large number of Linux operating system distributions and non-Linux operating systems such as Windows 7.

Note that you will need to have Operating System as an iso image file to make this happen.

Step 1
Download Universal USB Installer which enable you to create bootable pen drive.

Step 2
Plug your USB pen drive to your machine and start the downloaded exe file.

Step 3
Follow every steps as described,
  • Select operating system to be installed (Here, Ubuntu 13.04 64bit).
  • Select Pen Drive to extract selected operating system.

Step 4

You will get a window like this before you continue when you press on Create button on step3. Notice that I have selected the option Format to format my pen drive, you can uncheck this option anyway.

Press yes to continue.


Step 5
You will see several processes running from the software to perform the task. You will need to wait few minutes to complete these processes.


Step 6
Finally, you should see the success! then you can complete creating bootable pen drive and you can instal your operating system using pen drive.




Happy installing !


Continue Reading...

Rendering Unicode characters on your Android device

This post is about a problem which I face and the solution for it...So the problem is How to render or enable Unicode characters on my android device. I got this problem when I browse a web site which include Sinhala Unicode characters.

I am using Opera mini browser to browse internet. So here is the way to render Unicode characters on Opera mini.

Step 1
Type config: or opera:config in address bar and tap on Go, then it will display a configuration page.

Step 2
Scroll down and find out the title of "Use bitmap fonts for complex scripts" and select Yes from the combo box. Now Save the configurations by tap on Save button.

Now close the config page and now you are welcome to Unicode pages...!

Continue Reading...

Interface DS1307 RTC with PIC16f877 through I2C

Previous post introduce you the I2C communication protocol in briefly and present a complete set of functions to perform I2C communication as a 'master' (from hi tech C compiler). This post will be dedicated to introduce a RTC(Real Time Clock) IC DS1307 and a simple code to perform read/write operations on RTC through I2C. This code also implemented using Hi tech C compiler and PIC16f877 as the MCU.

What is RTC? yaaa RTC stands for Real Time Clock. Most of the devices those maintaining a clock for their functions use these kind of ICs.Also DS1307 has its own capability to use backup battery(3V coin) power when main power source shutdown.
 
You can download  Datasheet for DS1307.

The features can be listed bellow,

  • Real-time clock (RTC) counts seconds, minutes, hours, date of the month, month, day of the week, and year with leap-year compensation valid up to 2100
  • 56-byte, battery-backed, nonvolatile (NV) RAM for data storage
  • Two-wire serial interface (I2C)
  • Programmable square wave output signal
  • Automatic power-fail detects and switches circuitry
  • Consumes less than 500nA in battery backup mode with oscillator running
  • Optional industrial temperature range: -40°C to +85°C
  • Available in 8-pin DIP or SOIC

Most significant thing in the IC is the battery backup power because if it a real time clock it should be run always to get the real time from it. Therefore you do non need to worry about clock it will run independently from your MCU.


Source code for DS1307 RTC

So let's move to source code. This code uses the functions implemented on previous post. Therefore it is better to download those codes and create a header file for those functions so you can re-use them latter. Don't worry a complete source code can be found at the end of this post. Here DS1307 act as a slave on the bus and our MCU act as the master on the bus.

Data Write - Slave Receiver Mode
First let's try to write a function to write a byte to DS1307's EEPROM. I refer following diagram which I grab from DS1307's datasheet to code this function.


So lets implement this diagram into code!

void ds1307Write(unsigned char address, unsigned char data){
    i2CStart();//start the i2c bus
    i2CSend(208);//send DS1307 write command, DS1307 address + direction bit(R/nW) 1101000 + 0 = 11010000 = 208
    i2CSend(address);//send the address that you need to write data on.
    i2CSend(data);//write data on the address
    i2CStop();//close the I2C bus
}

Code explained

i2CStart();//start the i2c bus

This is the function call which we create in previous post to send start condition through I2C bus. So this code is to send the start condition to slave device.


i2CSend(208);//send DS1307 write command, DS1307 address + direction bit(R/nW) 1101000 + 0 = 11010000 = 208

If you can remember, I2C communication can be done by sending 7 bits wide address and R/W bit immediately following start condition on the bus. So this is the sending of address and the direction bit to the DS1307. The address for DS1307 is 1101000 (in binary) then the direction bit R/W (1 for read 0 for write). Then whole byte is the concatenated bits of address and R/W bit,

1101000 + 0 = 11010000 = 208 (in decimal)


i2CSend(address);//send the address that you need to write data on.

Then you need to send address which you want to write data on EEPROM of DS1307.


i2CSend(data);//write data on the address

Then the data to be written on above specified address.


i2CStop();//close the I2C bus

Then stop the I2C communication.


Data Read—Slave Transmitter Mode
Now the read function. This will be some what complicated, because you need to point the address where you want to read from before initiate the read operation. I refer following diagram from datasheet to implement the function. This function will return a byte at once which is located on address specified.



unsigned char ds1307Read(unsigned char address){
    unsigned char data;

    i2CStart();//send start signal on I2C bus
    i2CSend(208);//send DS1307 dummy write command to get the pointer to our location
                //DS1307 address + direction bit(R/nW) 1101000 + 0 = 11010000 = 208
    i2CSend(address);//set the address pointer
    i2CRestart();//send stop+start signal to bus
    i2CSend(209);//send read command to DS1307
    data = i2CRead();//read the buffer for received data
    i2CStop();//close the i2c bus
    return data;
}

Code explained
Initially send the start condition to the bus as explained in previous function to initiate the I2C bus for communication.


i2CSend(208);//send DS1307 dummy write command to get the pointer to our location
             //DS1307 address + direction bit(R/nW) 1101000 + 0 = 11010000 = 208

Then we send a dummy write command to get the EEPROM's pointer to the location that we need to read from. Therefore this byte will send to the DS3107 with the write operation direction bit (same as previous function). But we do not write anything on it.


i2CSend(address);//set the address pointer

Then send the address that we need to point on to set the EEPROM's pointer to the location.


i2CRestart();//send stop+start signal to bus

As I explained earlier we are not going to write anything, only purpose to issue write operation is set the pointer to the location.Therefore send the 'repeated start' condition to the bus.


i2CSend(209);//send read command to DS1307

Now we can send the read operation to DS1307. The value can be calculated as follows,
DS1307 address (1101000) + direction bit (1 for read) = 1101001 = 209 (in decimal


data = i2CRead();//read the buffer for received data

Now you can read data from DS1307 and hold these data on "data" variable. If you have any doubt about these function which are calling in this post please refer previous post. Because, none of the function that we calling here are inbuilt functions.


i2CStop();//close the i2c bus
return data;


Then close the I2C bus by sending stop condition and return the data which read from DS1307's EEPROM

DS1307's memory
Now you have functions for read and write operations. Now let's try to extract data from DS1307. We will need its memory map to do that. Memory map is,


Now let's try to read seconds from the memory as you can see here the Seconds data contain in 3 parts. Those are CH bit, 10 Seconds and Seconds. If you read the datasheet, you can find the functionality of CH bit. It is the clock enable bit. If you set CH = 0 the clock is enabled and running, if you set CH = 1 the clock will be disable. I will explain 10 Seconds and Second thing from a example,

Let's say the seconds on the clock is 36, then,
10 Seconds  = 3 (011)
Seconds = 6 (110)
and let CH = 0

Now the byte on the 0th position is 0 + 011 + 110 = 0011110 (plus mean concatenate). So I think you got the trick or still complicated...! Don't worry I have created functions to get and set Seconds, Minutes, Hours (in 12 hour or 24 hour mode), and Day. You can code your own functions for other things (Date, month and year). Only thing is to understand the memory map of the IC and code it.

You can download a complete source code and test program at the end of this tutorial.
The complete code is,

/**
 * Function will read a byte from the @address location of the memory
 * @param address - address that you need to read
 * @return received data byte from DS1307
 */
unsigned char ds1307Read(unsigned char address){
    unsigned char data;

    i2CStart();//send start signal on I2C bus
    i2CSend(208);//send DS1307 dummy write command to get the pointer to our location
                //DS1307 address + direction bit(R/nW) 1101000 + 0 = 11010000 = 208
    i2CSend(address);//set the address pointer
    i2CRestart();//send stop+start signal to bus
    i2CSend(209);//send read commad to DS1307
    data = i2CRead();//read the buffer for received data
    i2CStop();//close the i2c bus
    return data;
}

/**
 * Write to DS1307's eeprom
 * @param address - address to write data (0 - 7)
 * @param data - 8bit data to be written
 */
void ds1307Write(unsigned char address, unsigned char data){
    i2CStart();//start the i2c bus
    i2CSend(208);//send DS1307 write command, DS1307 address + direction bit(R/nW) 1101000 + 0 = 11010000 = 208
    i2CSend(address);//send the address that you need to write data on.
    i2CSend(data);//write data on the address
    i2CStop();//close the I2C bus
}

/**
 * get seconds from DS1307's eeprom
 * @return seconds on DS1307
 */
unsigned char ds1307GetSec(){
    unsigned char sec10;
    unsigned char sec1;

    unsigned char bcdSec = ds1307Read(0);//get the 0th location data(CH + 10Sec + 1Sec)[in BCD format]

    bcdSec = bcdSec & 0b1111111;//ignore CH bit (ignore MSB)
    sec10 = bcdSec >> 4;//shift 4 bits right to ignore 1sec position(ignore 4 LSB)
    sec1 = bcdSec & 0b1111;//ignore 4 MSBs

    return sec10 * 10 + sec1;//return the seconds
}

/**
 * get minutes from DS1307's eeprom
 * @return minutes on DS1307
 */
unsigned char ds1307GetMin(){
    unsigned char min10;
    unsigned char min1;

    unsigned char bcdMin = ds1307Read(1);//get the 1st loaction's data(0 + 10Min + 1Min)[in BCD format]

    bcdMin = bcdMin & 0b1111111;//ignore MSB
    min10 = bcdMin >> 4;//shift 4 bit right to ignore 1min position(ignore 4 LSB)
    min1 = bcdMin &0b1111;

    return min10 * 10 + min1;//return the minnutes
}

/**
 * This function will return the hours in 24 hour format by reading from DS1307
 * @return hours from DS1307 in 24 hours format
 */
unsigned char ds1307GetHours(){
    unsigned char hour10;
    unsigned char hour1;
    unsigned char retHour;

    unsigned char bcdHour = ds1307Read(2);//get the 1st loaction's data(0 + 12 or 24 hour mode + 10hour(first bit) or PM/AM + 10hour(second bit) + 1hour)[in BCD format]
    bcdHour = bcdHour & 0b1111111;//ignore MSB(7th bit)
    if(bcdHour > 63){//is in 12 hours mode?
        bcdHour = bcdHour & 0b111111;//ignore MSB(6th bit)

        hour10 = (bcdHour & 0b11111) >> 4;//get the hour10 position by ignoring MSB(5th bit) and shift 4 bits to right
        hour1 = bcdHour & 0b1111;//get hour1 position by getting only 4 LSBs
        retHour = hour10 * 10 + hour1;//calculate the hours using hour10 and hour1

        if(bcdHour > 31){//is PM?
            if(retHour != 12)
                retHour = retHour + 12;
        }
    }else{
        bcdHour = bcdHour & 0b111111;//ignore MSB(6th bit)
        hour10 = bcdHour >> 4;//shift 4 bit to right to get 5th and 4th bits
        hour1 = bcdHour & 0b1111;//get 4 LSBs

        retHour = hour10 * 10 + hour1;//calculate the hours using hour10 and hour1
    }

    return retHour;
}

/**
 * reads the Day value from DS1307 memory
 * @return the number for the specific day (1=SUN, 2=MON,... 7=SAT)
 */
unsigned char ds1307GetDay(){
    return ds1307Read(3);//read value on 3rd location of DS1307 and return it
}

/**
 * set seconds to DS1307 with CH bit
 * @param newSec - seconds to be set (0 - 79)
 * @param chBit - Clock enable bit (0 = clock enable, 1 = clock disable)
 */
void ds1307SetSecond(unsigned char newSec, unsigned char chBit){
    unsigned char bcdNum;
    if(newSec > 79)
        return;//to avoid writing to CH when writing to second feild

    ds1307separateDigits(newSec);
    bcdNum = digitPlaceVal[1] << 4;//shift 4 bits left
    bcdNum = bcdNum | digitPlaceVal[0];//ORing with placeValue1

    //add CH bit
    if(chBit == 1)
        bcdNum = bcdNum | 0b10000000;

    ds1307Write(0, bcdNum);//write to sec
}

/**
 * Set DS1307 minutes
 * @param newMin - minutes to be set(0 - 127)
 */
void ds1307SetMinutes(unsigned char newMin){
    unsigned char bcdNum;
    if(newMin > 127)
        return;//to avoid overlimit

    ds1307separateDigits(newMin);
    bcdNum = digitPlaceVal[1] << 4;//shift 4 bits left
    bcdNum = bcdNum | digitPlaceVal[0];//ORing with placeValue1

    ds1307Write(1, bcdNum);//write to min
}

/**
 * Set DS1307 hours in 12 hour mode
 * @param newHour - hours in 12 hour mode (0 - 19)
 * @param pm_nAm - AM/PM bit (1 for PM, 0 for AM)
 */
void ds1307SetHours12(unsigned char newHour, unsigned char pm_nAm){
    unsigned char bcdNum;
    if(newHour > 19)
        return;//avoid overlimit

    ds1307separateDigits(newHour);
    bcdNum = digitPlaceVal[1] << 4;//place hour's placeValue10
    bcdNum = bcdNum | digitPlaceVal[0];//ORing with placeValue1

    bcdNum = bcdNum | 0b1000000;//set 12 hour mode
    if(pm_nAm)//PM?
        bcdNum = bcdNum | 0b100000;//set PM

    ds1307Write(2, bcdNum);//write to hours
}

/**
 * Set DS1307 hours in 24 hour mode
 * @param newHour - hours in 24 hour mode (0 - 29)
 */
void ds1307SetHour24(unsigned char newHour){
    unsigned char bcdNum;
    if(newHour > 29)
        return;//avoid overlimit

    ds1307separateDigits(newHour);
    bcdNum = digitPlaceVal[1] << 4;//place hour's placeValue10
    bcdNum = bcdNum | digitPlaceVal[0];//ORing with placeValue1

    ds1307Write(2, bcdNum);//write to hours
}

/**
 * Set day to DS1307 (range 0-7)
 * @param newDay - day to be set (1 = SUN, 2 = MON,...., 7 = SAT)
 */
void ds1307SetDay(unsigned char newDay){
    if(newDay > 7)
        return;//avoid overlimit

    ds1307Write(3, newDay);//write to day
}

/**
 * This function accept 2 digit number and separate it into 10 and 1 place value and assign to digit array
 *
digit[0] = place value 1
 *
digit[1] = place value 10
 * @param num2 - 2 digit number(0 - 99)
 */
void ds1307separateDigits(unsigned char num2){
    digitPlaceVal[1] = num2 / 10;
    digitPlaceVal[0] = num2 - (digitPlaceVal[1] * 10);
}


Download
A complete source code and test program.

Continue Reading...

Hi-tech C code for I2C communication

Before move to code I think it is better to write a brief introduction to I2C communication.

I²C (pronounced I-squared-C) created by Philips Semiconductors and commonly written as 'I2C' stands for Inter-Integrated Circuit and allows communication of data between I2C devices over two wires. It sends information serially using one line for data (SDA) and one for clock (SCL).There needs to be a third wire which is just the ground or 0 volts. There may also be a 5volt wire is power is being distributed to the devices. Both SCL and SDA lines are "open drain" drivers. What this means is that the chip can drive its output low, but it cannot drive it high. For the line to be able to go high you must provide pull-up resistors to the 5v supply. I2C bus can have one or more I2C devices connected.



Master and Slave
The devices on the I2C bus are either masters or slaves. The master is always the device that drives the SCL clock line. Slaves are the devices that respond to the master. A slave cannot initiate a transfer over the I2C bus, only a master can do that. There can be multiple slaves on the I2C bus. Also both slaves and masters can receive/send data over I2C. But always master initiate the data transfer over I2C bus.

Always I2C communication start with the start condition and terminate with the stop condition. Between these conditions you can transfer data between master and slave(s).

Addressing
Each slave device on the I2C bus should have a unique address. Then master can address these salve device individually for better communication between master and slave device. Always master initiate data transfer with the address to select a slave device on the bus for further communication.
Address byte contain 7bit address and data direction bit (R/W). This will initiate the specific slave device to be read or write operations.


START (S) and STOP (P) bits
START (S) and STOP (P) bits are unique signals that can be generated on the bus but only by a bus master.
This can be done at any time so you can force a restart if anything goes wrong even in the middle of communication.
START and STOP bits are defined as rising or falling edges on the data line while the clock line is kept high.
 And this is the only occurrence of falling or rising SDA line while SCL line in high. Therefore this can be captured by slave devices as start/stop condition.
Also microchip device support restart condition, which is simply send stop(P) condition and start(S) condition sequentially by the master.



Data
All data blocks are composed of 8 bits (1 Byte). The initial block has 7 address bits followed by a direction bit (Read or Write). Acknowledge bits are squeezed in between each block. Each data byte is transmitted MSB first including the address byte.

Acknowledge
The acknowledge bit (generated by the receiving device) indicates to the transmitter that the the data transfer was ok. Note that the clock pulse for the acknowledge bit is always created by the bus master.
The acknowledge data bit is generated by either the master or slave depending on the data direction. For the master writing to a slave the acknowledge is generated by the slave. For the master receiving data from a slave the master generates the acknowledge bit.

This acknowledge bit can  acknowledge(ACK) or not-acknowledge(NACK) depending on operations on the I2C bus.
  • To acknowledge the receiving device must put the SDA line into low with the next clock pulse.
  • To not-acknowledge  the receiving device must high SDA line with the next clock pulse (simply do nothing on the SDA line, then it will remain high) 
 When data receiving by the master not-acknowledge bit must generate by the master to indicate to slave, "there no need to send more data to me(master)". Then master must stop the I2C bus by sending STOP(P) condition.


Data transfer from master to slave



Data transfer from slave to master




I think now you have a brief idea about I2C communication. So let's move to code. This code was written in Hitech C for most popular PIC16f877 microcontroller which is developed by microchip Inc using its inbuilt MSSP(Master Synchronous Serial Port) unit.


Source code for complete I2C (Master) communication with PIC16f877


/**
 * Initialize the I2C module as a master mode

 * SCL - RC3
 * SDA - RC4
 * 100kHz I2C click at 4Mhz crystal
 */
void initializeI2C(){
    TRISC3 = 1;//make SCL as input
    TRISC4 = 1;//make SDA as intput

    //----------configs for SSPSTAT register------------
    SSPSTAT = 0b10000000;//SMP = 1 [Slew rate control disabled for standard speed mode (100 kHz and 1 MHz)]
    //--------------------------------------------------

    //---------- configs for SSPCON register------------
    //Synchronous Serial Port Mode Select bits
    //configs as I2C Master mode, clock = FOSC / (4 * (SSPADD+1))
    SSPM0 = 0;
    SSPM1 = 0;
    SSPM2 = 0;
    SSPM3 = 1;

    SSPEN = 1;//Enables the serial port and configures the SDA and SCL pins as the source of the serial port pins

    SSPOV = 0;//No overflow

    WCOL = 0;//No collision
    //--------------------------------------------------

    //---------- configs for SSPCON2 register-----------
    SSPCON2 = 0;//initially no operations on the bus
    //--------------------------------------------------

    SSPADD = 40;// 100kHz clock speed at 4Mhz cystal

    //----------PIR1-----------
    SSPIF = 0;//clear  Master Synchronous Serial Port (MSSP) Interrupt Flag bit

    //-----------PIR2-----------
    BCLIF = 0;//clear Bus Collision Interrupt Flag bit
}

/**
 * wait until I2C bus become idel
 */
void waitUntilIdel(){
    while(R_W || SEN || RSEN || PEN || RCEN || ACKEN){
        __delay_ms(1);
    }
}

/**
 * Send 8 bit(1 byte) through I2C bus
 * @param data - 8 bit data t be sent
 */
void i2CSend(unsigned char data){
    waitUntilIdel();
    SSPBUF = data;
    //while(BF) continue;// wait until complete this bit transmision
    waitUntilIdel();//wait until any pending transaction
}

/**
 * Send start condition to I2C bus
 */
void i2CStart(){
    waitUntilIdel();
    SEN = 1;//Initiate Start condition on SDA and SCL pins. Automatically cleared by hardware
    waitUntilIdel();
}

/**
 * Send stop condition to I2C bus
 */
void i2CStop(){
    waitUntilIdel();
    PEN = 1;//Initiate Stop condition on SDA and SCL pins. Automatically cleared by hardware.
    waitUntilIdel();
}

/**
 * Send restart condition to I2C bus
 */
void i2CRestart(){
    waitUntilIdel();
    RSEN = 1;//Initiate Repeated Start condition on SDA and SCL pins. Automatically cleared by hardware.
    waitUntilIdel();
}

/**
 * read the I2C bus
 * @return Read data from I2C slave
 */
unsigned char i2CRead(){
    unsigned char temp;

    waitUntilIdel();

    //configure MSSP for data recieving
    RCEN = 1;//Enables Receive mode for I2C

    waitUntilIdel();

    while(!BF);//wait for buffer full
    temp = SSPBUF;//read the buffer
    waitUntilIdel();//wait for any transaction
    return temp;//return the bufferd byte
}

/**
 * send acknowledge condition
 */
void i2CAck(){
    waitUntilIdel();
    ACKDT = 0;//Acknowledge Data bit(0 = Acknowledge)
    ACKEN = 1;//Initiate Acknowledge sequence on SDA and SCL pins, and transmit ACKDT data bit.Automatically cleared by hardware.
    waitUntilIdel();
}

/**
 * send not acknowledge condition
 */
void i2CNAck(){
    waitUntilIdel();
    ACKDT = 1;//Acknowledge data bit(1 = Not Acknowledge)
    ACKEN = 1;//Initiate Acknowledge sequence on SDA and SCL pins, and transmit ACKDT data bit.Automatically cleared by hardware.
    waitUntilIdel();
}


I think comments on each code line is better enough to understand these functions.
The MSSP module has three associated registers. These include a status register (SSPSTAT) and two control registers (SSPCON and SSPCON2). Above functions will configure these registers to use I2C communication in Master mode.

You can download complete source code with test program.

Next post will be a practical usage of I2C communication which use the RTC (Real Time Clock) IC DS1307.
Continue Reading...

Proteus VSM viewer to debug MPLABX projects

Hi all, Before few months earlier I heard Microchip released a new IDE to implement firmwares for their products. That is MPLABX. MPlabX has very nice features and tools to assist while you are coding, compiling, debugging, etc... If you familiar with Netbeans, it will be the great advantage to use MPLABX. Because MPLAB X is based on the Oracle Sponsored open-source NetBeans platform. Therefore supports many third-party tools, and is compatible with many NetBeans plug-ins. So try out it your self to feel the difference from MpLab8.

If you prefer to download new version of MPLABX click here

This post is not to present features of the MPLABX. So this article will introduce the way to work MPLABX with Proteus VSM viewer for debugging purposes. MPLAB8 has its inbuilt ability to work with Proteus VSM viewer. But there are a few more steps to integrate it with the MPLABX IDE. When you are working with Proteus VSM viewer in MPLABX, you will see how much easier to debug your code.

Step 1 - The first step is to install VSM viewer in MPLABX IDE.Open Plugins window by selecting Tools->Plugins from the menu bar. Then click on Available plugins, then you will see the Proteus VSM viewer from the list under 'MPLAB DBCore' category. (If proteus vsm viewer not listed on the list just press on Reload Catalog button to reload the list).

Then click on Install to install the plugin to IDE, the install wizard will guide you to install it successfully. Also you can update or remove you existing plugins from this window.

Step 2 - Create your project from MPLABX and select Proteus VSM viewer as debugging tool when it asked.As showed in following figure.

OR you can set Proteus VSM as the debugging tool for you existing project. Just goto project Properties by right click on your project. Then click on 'Config: [default]' category. Then select Proteus VSM from the list box positioned at right side of the window as shown below.

Step 3 - Now you can code your project and compile your project in debug mode. To do that right click on your project from the Projects window then select Debug from the drop down menu. Now this will lead to create files and folder hierarchy for debugging by IDE, if your code compile successfully. (This will terminate debugging session until we still do not create a ISIS design for our project. Just don't care those!)

Step 4 - Create the design file file for your project from the Proteus ISIS and select the .cof file which is located at \dist\default\debug folder as the program file for your micocontroller(in ISIS design). Then save the design in your project folder.

Step 5 - Now we should add our design file to MPLabX project. Go to project properties window and click on Proteus VSM Viewer category under Config: [default] category then select your design file from Design File Name and press on apply (you can let other configurations as it is under default environment).

Now you can set break points in your source code and Debug your project. Then MPLabX will automatically run the ISIS design simultaneously with your code.

Oh I forgot to say, it is better to only open your ISIS design from the Proteus ISIS while you debug and enable Remote Debug Monitor from Proteus ISIS (Debug->Use Remote Debug Monitor)

Then you can open both windows simultaneously and debug you project as you can see...




Continue Reading...