Friday, December 5, 2014

Reading MMA7660 Accelerometer Data With nRF51822 BLE SOC

In this post I will describe you how to connect MMA7660 accelerometer to nRF51822 ,how to use the interrupts and gestures and how to read accelerometer data via UART module in  NRF51822.

I'm using nRF Development Kit which includes two nRF51822 chips which are mounted on PCB with a matched antenna.The kit also includes a compact nRF51822 USB dongle that can act as a Bluetooth low energy master emulator when developing Bluetooth low energy applications. Unfortunately we couldn't buy a nRFgo Starter kit. So I will describe how to program the nRF chip without a nRFgo Starter Kit.

Connect pin2 and pin3 together and connect them to 3.3V in K1 header. Then find a ground signal from the board  (see P5 header) and connect it to the GND of the power source.

Initializing UART

To read the data via UART module I used USB to Serial converter module which can be easily buy from ebay. But in my next post, I will describe how to read data via BLE module in the chip. Before using the uart module you must configure the baud rate , hardware flow control and pins.

I did not use any hardware flow control. So I set HDFC to false.

     #define RX_PIN_NUMBER  11
     #define TX_PIN_NUMBER  9
     #define CTS_PIN_NUMBER 10
     #define RTS_PIN_NUMBER 8
     #define HWFC           false

Set the baud rate to 9600.

Initializing I2C


MMA7660 accelerometer module is specially designed for identify gestures. It communicates with the micro controller via I2C interface. So we must activate the I2C interface of nrf51822 SoC. In order to do so, twi_hw_master.c file should be imported to your workspace. By calling "twi_master_init(void)" function in the above C file,we can initialize hardware I2C interface of the chip. By default SDA and SCK pins are 24U and 25U. By reading the data sheet of accelerometer module you would get better understand how it communicates with external micro controller via I2C interface. In the data sheet they have mentioned the I2C frequency the accelerometer works with.

There are some issues with I2C interface. Most of the time devices will not communicate properly if the connections between them are loos. Connecting wires should shorter as possible. 

SDA and SCL pins should be connected to pull up resisters. In the breakout board I bought from ebay got those pull up resisters. So I was able to connect the breakout board's SCL and SDA pins directly to the chip.

So we have done the basic setup to communicate with accelerometer and get data out from it.

MMA7660 Register Settings 


XbotiX- 2014 University of Ruhuna Robotics Competition Winners




XbotiX robotics Competition was held by University of Ruhuna. Among more than 40 competitors, We were able to win the Trophy...

Wednesday, April 30, 2014

Reading Serial Port Data & Plotting Gray Image in Matlab

I my previous post I connected a OV7660 camera module to STM32F4 Discovery board. Then I was needed to display the data transmitted by micro controller to PC as an Gray scale image. To do that I use Matlab. Using Matlab reading a serial port is easy, but you must get familiar with matlab.

My micro controller sends data as bytes (0-255) to PC which indicating intensity values of each pixel in image. I pick those bytes by reading the relevant serial port in matlab. I suggest you to get familiar with matlab Serial functions before looking at the below code .

This code containes only few lines, but it does much work to read and display bytes as an image.


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
clear all
s = serial('COM1');
s.BaudRate = 921600;


%get(s);

image = zeros(70,80);
row =1;
col = 1;

fopen(s);
pix = fgetl(s);

while str2double(pix)~= 256
    pix = fgetl(s);
    
end
while str2double(pix)~= 300
    pix = fgetl(s);
    
end

for i = 1:70*80
 pix = fgetl(s);
 
 %class(str2double(pix));
 image(row,col) =str2double(pix);
%image(row,col) = 255;
 if  mod(i,80) == 0
     row=row+1;
     col = 0;
 end
 col=col+1;
end
fclose(s);
%figure()
colormap(gray);
imagesc(image);


    

Following video presents how this code works. Each and every time pressing the Run icon, It will show the image captured by camera.


Comments are warmly welcome!

Monday, April 28, 2014

STM32F4 Discovery Board + OV7660 or OV7670 Working Code

Hello all,

This post is about on how to get data from  OV7660/OV7670 camera module by connecting to STM32F4 Discovery Board. first of all I would like to introduce you following posts on connecting OV7670 camera module to a micro controller which were really helpful for me to become success.

  • http://embeddedprogrammer.blogspot.com/2012/07/hacking-ov7670-camera-module-sccb-cheat.html
  • http://embeddedprogrammer.blogspot.com/2013/01/demo-stm32f4-ov7670-qserialterm.html
  • forum
I must thankful for the above posts which were really useful. 

Okay, Lets get to work!

Before you are going to do this project I recommend you to read the data sheets and reference manuals of OV7660, STM32F4 data sheet and STM32F4 Reference manual. It is not a good practice for a person who is willing to gain his knowledge on embedded systems to copy and pasting a code in internet and make it to work. What is really valuable is practicing to read data sheets and understanding how they work.

Above mentioned blog posts have clearly mentioned how camera module works. The difference between OV7660 and OV7670 is ,OV7670 has more options like scaling the image as user wanted,register values  etc... . The behavior of working is same for both the camera modules.

I will explain how I became success step by step

STEP 1

Before doing all the things you must give a clock to camera module in order to work. Without giving a clock it is a dead module. I was able to connect this camera module to both STM32F103 and STM32f4 Discovery board. I shift to discovery board because it is unable to get data at higher speeds since no DCMI interface is available on that microcontroller.
STM32F4 is more powerful than F103. It is having DCMI and DMA modules. So the speed is really good.
To get a clock out from your microcontroller,just search on microcontroller's data sheet as MCO.
You can see what is the MCO pin in your microcontroller. In discovery board I used PA8 to get clock out. Get the data sheet of STM32F4 Discovery board and search on PA8. You will see what PA8 can do. We must set that pin as MCO in configuration code. Following is the code for it.


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
void MCO1_Init(void)
{
 GPIO_InitTypeDef GPIO_InitStructure;

 RCC_ClockSecuritySystemCmd(ENABLE);

 /* Enable GPIOs clocks */
 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
 
 GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_MCO);
  
 /* Configure MCO (PA8) */
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;  
 GPIO_Init(GPIOA, &GPIO_InitStructure);

 RCC_MCO1Config(RCC_MCO1Source_PLLCLK, RCC_MCO1Div_4);
}


You must check the clock is worming correctly using a logic analyzer or a oscilloscope before move to next step.

STEP 2

Next is to configure the camera as we want. Omnivision has used SCCB interface to configure the camera. Seems like SCCB is almost same as I2C. But I couldn't get to work with I2C. By changing some commands I was able to communicate with the camera. following is the code for SCCB interface, It is worked well for me. If anything need to be changed, please let me know. Your ideas are warmly welcome!


1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
#include "stm32f4xx.h"
#define ONE_BYTE_REG_ADDR 0x01
#define TWO_BYTE_REG_ADDR 0x02

void Hardware_InitI2C(void) 
{
 
 GPIO_InitTypeDef GPIO_InitStructure; // this is for the GPIO pins used as I2C1SDA and I2C1SCL
 I2C_InitTypeDef I2C_InitStructure; // this is for the I2C1 initilization
 
 /* enable APB1 peripheral clock for I2C1*/
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
 
 /* enable the peripheral clock for the pins used by
  PB6 for I2C SCL and PB9 for I2C1_SDL*/
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); 
 
 /* This sequence sets up the I2C1SDA and I2C1SCL pins
  * so they work correctly with the I2C1 peripheral
  */
 GPIO_StructInit(&GPIO_InitStructure);
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9; // Pins 10(I2C1_SCL) and 11(I2C1_SDA)
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // the pins are configured as alternate function so the USART peripheral has access to them
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;// this defines the IO speed and has nothing to do with the baudrate!
 GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;// this defines the output type as open drain
 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;// this activates the pullup resistors on the IO pins
 GPIO_Init(GPIOB, &GPIO_InitStructure);// now all the values are passed to the GPIO_Init() 
 
 
 /* The I2C1_SCL and I2C1_SDA pins are now connected to their AF
  * so that the I2C1 can take over control of the
  * pins
  */
 GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_I2C1); //
 GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_I2C1);
 
  /* Configure I2C1 */
  I2C_StructInit(&I2C_InitStructure);
  I2C_DeInit(I2C1);
 
  /* Enable the I2C peripheral */
  I2C_Cmd(I2C1, ENABLE);
 
  /* Set the I2C structure parameters */
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
  I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
  I2C_InitStructure.I2C_OwnAddress1 = 0x00;
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
  I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  I2C_InitStructure.I2C_ClockSpeed = 100000;
  /* I2C Peripheral Enable */
I2C_ITConfig(I2C1, I2C_IT_ERR, ENABLE);
  /* Initialize the I2C peripheral w/ selected parameters */
  I2C_Init(I2C1,&I2C_InitStructure);
 I2C_Cmd(I2C1, ENABLE);
     

}

int i2c_send_data(u8 slave_addr, u16 reg_addr, u8* data, u8 addr_len)
{
  int timeout = 0x7FFFFF;
  int ret = 0;
         /* send i2c*/
  I2C_GenerateSTART(I2C1, ENABLE); 
  while( !I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));  
  {
   if ((timeout--) == 0) 
   {
    ret = 1;
    goto exit;
   }
  }
  I2C_Send7bitAddress(I2C1, slave_addr, I2C_Direction_Transmitter);  
  while(!(I2C1->SR1 & (1 << 1))); // check ADDR bit
  {
   if ((timeout--) == 0) 
   {
    ret = 2;
    goto exit;
   }
  }
  while(!(I2C1->SR2 & (1 << 2)));   // check TRA bit
  {
   if ((timeout--) == 0) 
   {
    ret = 3;
    goto exit;
   }
  }
 
  /* 2 byte reg address */
  if(addr_len == TWO_BYTE_REG_ADDR)
  {
   // MSB
   I2C_SendData(I2C1, (0xFF & (reg_addr >> 8)) );   
   while(!(I2C1->SR1 & (1 << 7))); 
   {
   if ((timeout--) == 0) 
   {
    ret = 4;
    goto exit;
   }
   }
     
   // LSB
   I2C_SendData(I2C1, (0xFF & reg_addr));   
   while(!(I2C1->SR1 & (1 << 7)));
   {
   if ((timeout--) == 0) 
   {
    ret = 5;
    goto exit;
   }
   }
  
  }
  /* 1 byte reg address */
  else 
  {
   I2C_SendData(I2C1, (0xFF & reg_addr));   
   while(!(I2C1->SR1 & (1 << 7)));
   {
   if ((timeout--) == 0) 
   {
    ret = 6;
    goto exit;
   }
   } 
  }

  I2C_SendData(I2C1, *data);  
  while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));
  {
//   if ((timeout--) == 0) 
   {
    ret = 7;
    goto exit;
   }
  } 

                exit:  
  I2C_GenerateSTOP(I2C1, ENABLE);  
  return ret;
}

int i2c_receive_data(u8 slave_addr, u16 reg_addr, u8* data, u8 addr_len)
{
  int timeout = 0x7FFFFF;
  int ret = 0;
   /* send i2c*/
  while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));
  I2C_GenerateSTART(I2C1, ENABLE); 
  while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));
  {
   if ((timeout--) == 0) 
   {
    ret = 1;
    goto exit;
   }
  }
  
  I2C_Send7bitAddress(I2C1, slave_addr, I2C_Direction_Transmitter);  
  while(!(I2C1->SR1 & (1 << 1))); // check ADDR bit
  {
   if ((timeout--) == 0)    
   {
    ret = 2;
    goto exit;
   }
  }
  
  while(!(I2C1->SR2 & (1 << 2)));   // check TRA bit
  {
   if ((timeout--) == 0) 
   {
    ret = 3;
    goto exit;
   }
  }
  
  /* 2 byte reg address */
  if(addr_len == TWO_BYTE_REG_ADDR)
  {
   // MSB
   I2C_SendData(I2C1, (0xFF & (reg_addr >> 8)) );   
   while(!(I2C1->SR1 & (1 << 7))); 
   {
   if ((timeout--) == 0) 
   {
    ret = 4;
    goto exit;
   }
  }
     
   // LSB
   I2C_SendData(I2C1, (0xFF & reg_addr));   
   while(!(I2C1->SR1 & (1 << 7)));
   {
   if ((timeout--) == 0) 
   {
    ret = 5;
    goto exit;
   }
  }
  }
  
  /* 1 byte reg address */
  else 
  {
   I2C_SendData(I2C1, (0xFF & reg_addr));
   while(!(I2C1->SR1 & (1 << 7)));
   {
   if ((timeout--) == 0) 
   {
    ret = 6;
    goto exit;
   }
  }
  }

  I2C_GenerateSTOP(I2C1, ENABLE);
  I2C_GenerateSTART(I2C1, ENABLE);
  while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));
  {
   if ((timeout--) == 0) 
   {
    ret = 7;
    goto exit;
   }
  }
  I2C_Send7bitAddress(I2C1, slave_addr, I2C_Direction_Receiver);
  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); // check ADDR bit
  {
   if ((timeout--) == 0) 
   {
    ret = 8;
    goto exit;
   }
  }
    
  
  I2C_AcknowledgeConfig(I2C1, DISABLE);
                /* Send STOP Condition */
                I2C_GenerateSTOP(I2C1, ENABLE);
  
  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));
  {
   if ((timeout--) == 0) 
   {
    ret = 10;
    goto exit;
   }
  }
  
  *data = I2C_ReceiveData(I2C1);
  I2C_AcknowledgeConfig(I2C1, ENABLE);
  return ret;
  
exit:
  I2C_GenerateSTOP(I2C1, ENABLE);
  return ret;
}

To write to and read from  the camera i defined two functions...

Monday, April 21, 2014

Accelerometer Data Filtering Using Low Pass filter


      While you are working with accelerometers you may have noticed that the data are too noisy. To use those values to your application, definitely you will have to filter out them. So what is a filter, you may heard of a water filter,which is used to remove germs and unwanted materials in water. Using a filter, we will be able to get more precise data from accelerometer. There are many filters which can do data filtering. In this post I'm going to show you how to filter out accelerometer data using a simple low pass filter.

Following figure (fig.1) shows you the data from  MPU6050 accelerometer without(blue line) and with(white line) filtering. The accelerometer was mounted on the middle of a quadcopter. So when the motors are rotating accelerometer data got more and more noisy(fig.2). I felt how could I deal with these kind of very very noisy data at the first time I saw accelerometer readings come out from the accelerometer.


                                                                             fig.1
                                                                   

                                                                            fig.2


As you can see from above images, blue line is very noisy while white line is low nisy because it has been filter out by a low pass filter.

I used STM32F103RBT6 microcontroller to configure and read data through I2C interface. you can connect two MPU6050 modules to same I2C line. I hope to add a new post on how to configure and read data from MPU6050 in near future.

Some accelerometr modules have its inbuilt low pass filters, They can be configured to out put only the filter out data. MPU6050 is an example for that. If you are combining accelerometer and gyro to get precise angle, you have to use kalman filter or Complimentary filter. But in MPU6050 there is an inbuilt Digital Motion Processor capable of processing 9 axis motion fusion algorithms.

But in this post I use a low pass filter to filter out accelerometer data from accelerometer.

Below is the code for a digital low pass filter.

void accelerometer_Smooth (void)
{
    
    static int element=0;
    static int n,k = 0;
    static double x_Low,y_Low,z_Low,x0,y0,z0,z02 = 0;
    
    /* Low pass filter */
    
  const float dt = (1.0 / 50.0);
  const float RC = 0.35;
  const float alpha = dt / (RC + dt);

    
   
  x_Low = ((alpha * axData[0]) + (1.0 - alpha) * x0);
  y_Low = ((alpha * axData[1]) + (1.0 - alpha) * y0);
  z_Low = ((alpha * axData[2]) + (1.0 - alpha) * z0);  
  x0 = x_Low;
  y0 = y_Low;
  z0 = z_Low;

}

You can change the values of dt and RC according to the rise time you want.

axData[0],axData[1],axData[2] are global variables which contains the new accelerometer data without filtering.

axData[0] = x axis acceleration come out from accelerometer without filtering.
axData[1] = y axis acceleration come out from accelerometer without filtering.
axData[2] = z axis acceleration come out from accelerometer without filtering.

By using a median filter and a kalman predictor we can obtain the same results as low pass filter. I will post about them in near future.

Serial chart was really helpful to examine data coming out from accelerometer. You can download the software from here.