Kalibrasi MPU6050

MPU6050 adalah sensor sudut dan percepatan atau biasa disebut gyrometer. Sama seperti kebanyakan sensor lain, MPU6050 perlu dikalibrasi sebelum digunakan. Kinerja gyrometer ini sangat terpengaruh terhadap suhu, jadi pastikan suhu stabil terlebih dahulu sebelum melakukan proses kalibrasi. Beri jarak waktu antara 3 sampai dengan 5 menit setelah power on agar suhu stabil. Hasil proses kalibrasi pun akan berbeda saat dilakukan di suhu ruangan dibandingkan dengan kalibrasi di luar ruangan. Pastikan dikalibrasi pada suhu ruangan yang sama, atau setidaknya hampir sama, dengan dengan suhu dimana alat akan digunakan.

Berikut kode kalibrasi dari Luis Ródenas denga sedikit modifikasi:

Pastikan sudah melakuan instalasi librari I2Cdev dan MPU6050 dan hubungkan MPU6050 dengan skema berikut

 

Taru MPU6050 pada bidang datar, semisal meja dan jangan digerakkan sampai proses kalibrasi selesai.  Selanjutnya, untuk proses kalibrasi, buka dengan serial monitor dengan baud rate 115200 dan kirimkan satu huruf (bebas) lalu tekan send atau klik tombol enter. Tunggi hingga daftar offset muncul dengan ditandai tulisan ‘Your offsets:’

USBASP, Arduino IDE, Linux

Make sure USBasb plugged into USB Port and check whenever its conected or not by typing lsusb in terminal.

stufi1983@latif-laptop:~$ lsusb
Bus 001 Device 003: ID 0db0:6877 Micro Star International RT2573
Bus 001 Device 004: ID 058f:6387 Alcor Micro Corp. Flash Drive
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 003: ID 16c0:05dc Van Ooijen Technische Informatica shared ID for use with libusb
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub

USBasp should detected with VendorID 16c0 and ProductID 05dc. Look at bus address. It says Bus 002 Device 003. See permission on Bus 002 Device 001 by typing ls -l /dev/bus/usb/002/003 in terminal.

stufi1983@latif-laptop:~$ ls -l /dev/bus/usb/002/003
crw-rw-r-- 1 root root 189, 130 Sep  6 10:05 /dev/bus/usb/002/003

Current permission is crw-rw-r– mean user dont have permission to write at the bus. So, change permission to 777 with chmod command.

stufi1983@latif-laptop:~$ sudo chmod 777 /dev/bus/usb/002/003
[sudo] password for stufi1983:

OK, Done. USBasp now already to use with Arduino. Select in Arduino  menu Tool>Progammer>USBasp. Make new file New>Examples>Basic>Blink. Then upload by using menu: File>Upload using Programer

Timer Interrupt on ATMega8535

I have writeATMEga8535 & Arduino IDE, its time to try how will it works. This time I’d like to try using Timer Interrupt. Will be usefull for time critical (e.g dot matrix scanning) and PWM generation.

First step, open BareMinimum on File>Example>Basic and place this code in the setup routines (just after void setup() {):
// initialize timer1
noInterrupts();           // disable all interrupts
TCCR1A = 0;
TCCR1B = 0;
TCNT1  = 0;

  OCR1A = 31250;            // compare match register 16MHz/256/2Hz
TCCR1B |= (1 << WGM12);   // CTC mode
//TCCR1B |= (1 << CS11);    // 8 prescaler
TCCR1B |= (1 << CS10);    // 64 prescaler (overide) or no prescaler (standalone)
TIMSK |= (1 << OCIE1A);  // enable timer compare interrupt /TIMSK1
interrupts();             // enable all interrupts

That was initialization for Timer1 which is 16bit timer. The important thing to do are disable interrupt vector and  re-enable interupt vector after timer initialization done.

TCCR1A (Timer/Counter1 Control Register A) is register consist of WGM (Wave Generation Mode) to decide what mode which we will use. There are 4 main modes, Normal, CTC, PWM and Fast PWM mode. This time i’d prefer CTC mode.

In CTC (Clear Timer on Compare) mode the counter will count until it hits the value specified in the OCR1 register.  When the TCNT1 passes the TOP value (Specified by the OCR1) it resets to 0 and at the same time sets the TOV1 flag.

Look at following table, what we need to set is only WGM12 to be 1 (mode 4).

But wait! What is the difference between mode 4 and mode 12? Yes, Register that used to manipulate the counter resolution is OCR1A for CTC mode 4, mean while CTC mode 12 use ICR1. The ICR1 Register is not double buffered. This means that if ICR1 is changed to a low value when the counter is running with a low or none prescaler value, there is a risk that the new ICR1 value written is lower than the current value of TCNT1. The result will then be that the counter willmiss the Compare Match at the TOP value. The OCR1A Register however, is double buffered. This feature allows the OCR1A I/O location to be written anytime.

When the OCR1A I/O location is written the value written will be put into the OCR1A Buffer Register. The OCR1A Compare Register will then be updated with the value in the Buffer Register at the next timer clock cycle the TCNT1 matches TOP. The update is done at the same timer clock cycle as the TCNT1 is cleared and the TOV1 Flag is set.

Using the ICR1 Register for defining TOP works well when using fixed TOP values. By using ICR1, the OCR1A Register is free to be used for generating a PWM output on OC1A. However, if the base PWM frequency is actively changed (by changing the TOP value), using the OCR1A as TOP is clearlya better choice due to its double buffer feature.

Next step is reseting Timer 1 Counter in TCNT1 Register. TCNT1 is the counter register which is increasing when timmer is running.

To make timer work, prescales have to be set either internal or external clock source. Prescaler can be set in TCCR1B Register at CS12;CS11;CS10.

To user timer1 as interrupt, TIMSK Register have to be set. When OCIE1A bit is written to one, and the I-flag in the Status Register is set (interrupts globally enabled), the Timer/Counter1 Output Compare A Match interrupt is enabled. The corresponding Interrupt Vector is executed when the OCF1A Flag, located in TIFR, is set.

Don’t forget to re-enable Interrupt to get TImer1 Interrupt work.

Be wise in using Microcontroller’s memory

Memory is a finite resource on these tiny processors and some applications are just plain too big for an microcontroler(uC). But most code has some room for optimization. So if program is just a little overweight, with a little diet and exercise, probably shed enough bytes to make it fit into uC. As we know, there are 3 types of memory in uC:

  • Flash or Program Memory
  • SRAM
  • EEPROM

Flash Memory

Flash memory is used to store your program image and any initialized data. You can execute program code from flash, but you can’t modify data in flash memory from your executing code. To modify the data, it must first be copied into SRAM

Flash memory is the same technology used for thumb-drives and SD cards. It is non-volatile, so your program will still be there when the system is powered off.

Flash memory has a finite lifetime of about 100,000 write cycles. So if you upload 10 programs a day, every day for the next 27 years, you might wear it out.

SRAM

SRAM or Static Random Access Memory, can be read and written from your executing program. SRAM memory is used for several purposes by a running program:

  • Static Data – This is a block of reserved space in SRAM for all the global and static variables from your program. For variables with initial values, the runtime system copies the initial value from Flash when the program starts.
  • Heap – The heap is for dynamically allocated data items. The heap grows from the top of the static data area up as data items are allocated.
  • Stack – The stack is for local variables and for maintaining a record of interrupts and function calls. The stack grows from the top of memory down towards the heap. Every interrupt, function call and/or local variable allocation causes the stack to grow. Returning from an interrupt or function call will reclaim all stack space used by that interrupt or function.

EEPROM

EEPROM is another form of non-volatile memory that can be read or written from your executing program. It can only be read byte-by-byte, so it can be a little awkward to use. It is also slower than SRAM and has a finite lifetime of about 100,000 write cycles (you can read it as many times as you want).

Several Memory Problems

Most memory problems occur when the stack and the heap collide. When this happens, one or both of these memory areas will be corrupted with unpredictable results. In some cases it will cause an immediate crash. In others, the effects of the corruption may not be noticed until much later. One way to diagnose memory problems is to measure how much memory is in use.

Optimize Flash memory

Flash memory usage is depend on code and compiler. If it have reached or exceeded the space available, some of these optimizations may help get you back under the limit.

Remove Dead Code

If project is a mash-up of code from several sources, chances are there are parts that are not getting used and can be eliminated to save space.

  • Unused Libraries – Are all the #include libraries actually used?
  • Unused Functions – Are all the functions acutally being called?
  • Unused Variables – Are all the variables actually being used?
  • Unreachable Code – Are there conditional expressions which will never be true?
 Hint: If  not sure about an #include, a function or a variable. Comment it out. If the program still compiles, that code is not being used. Just remove it!

Consolidate Repeated Code

If any sequence of code statements in two or more places, consider making a function out of them.

Eliminate the Bootloader

If space is really-really tight, you might consider eliminating the bootloader. This can save as much as 2K or 4K of Flash – depending on which bootloader you are currently using.

The downside of this is that you will need to load your code using an ISP programmer instead of via a standard USB cable.

 

Optimize SRAM

SRAM is the most precious memory commodity. SRAM shortages are probably the most common memory problems. They are also the hardest to diagnose. If program is failing in an otherwise inexplicable fashion, a crashed may happen in the stack due to a SRAM shortage. There are a number of things that can do to reduce SRAM usage.

Remove Unused Variables

If not sure whether a variable is being used or not, comment it out. If the sketch still compiles, get rid of it!

Use F() Macro

Literal strings are repeat memory offenders. First they take up space in the program image in Flash, then they are copied to SRAM at startup as static variables. This is a horrible waste of SRAM since we will never be writing to them.

Paul Stoffregen of PJRC and Teensyduino fame developed the F() macro as a super-simple solution to this problem. The F() macro tells the compiler to keep your strings in PROGMEM. All you have to do is to enclose the literal string in the F() macro.

For example, replacing this:

  Serial.println("This string will be stored in memory. Horible waste of SRAM!");

with this:

  Serial.println(F(" This string will be stored in memory. Horible waste of SRAM!"));

Will save bytes of wonderful SRAM!

Reserve() your strings

The string library allows to reserve buffer space for a string with the reserve() function. The idea is prevent String from fragmenting the heap by using reserve(num) to pre-allocate memory for a String that grows.

With the memory already allocated, String doesn’t need to call realloc() if the string grows in length. In most usages, lots of other little String objects are used temporarily as you perform these operations, forcing the new string allocation to a new area of the heap and leaving a big hole where the previous one was (memory fragmentation). Usually all you need to do is use reserve() on any long-lived String objects that you know will be increasing in length as you process text.

Move constant data to PROGMEM.

Data items declared as PROGMEM do not get copied to SRAM at startup. They are a little less convenient to work with, but they can save significant amounts of SRAM. The basic Arduino reference for PROGMEM is here. And there is a more detailed tutorial on the subject here.

#define FS(x) (__FlashStringHelper*)(x)
const char MyText[]  PROGMEM  = { “My flash based text” };

void setup() {
Serial.begin(57600):
Serial.println(FS(MyText));
}

Reduce Buffer Sizes

Buffer and Array Allocations: If you allocate a buffer, make sure it is no bigger than it needs to be.

Buffers in Libraries: Also be aware that some libraries allocate buffers behind the scenes that may be candidates for trimming as well.

System Buffers: Another buffer hidden deeply in the system is the 64 byte serial receive buffer. If your sketch is not receiving a lot of high-speed serial data, you can probably cut this buffer size in half – or maybe even less.

For an Example, if using Arduino library, Serial buffer can be reduced. The Serial buffer size is defined in HardwareSerial.cpp. This file can be found in your Arduino install directory: \Arduino-1.x.x\hardware\arduino\cores\arduino\HardwareSerial.cpp. Look for the line: #define SERIAL_BUFFER_SIZE 64 And change it to 32 or less.

Reduce Oversized Variables: Don’t use a float when an int will do. Don’t use an int when a byte will do. Try to use the smallest data type capable of holding the information. Use char instead of int if only need 255 bits data.

 

Think Globally. Allocate Locally.

Let’s have another look at how SRAM is used (and abused):

Global & Static Variables

Global and Static variables are the first things loaded into SRAM. They push the start of the heap upward toward the stack and they will occupy this space for all eternity.

Dynamic Allocations

Dynamicaly allocated objects and data cause the heap to grow toward the stack. Unlike Global and Static variables, these variables can be de-allocated to free up space. But this does not necessarily cause the heap to shrink! If there is other dynamic data above it in the heap, the top of the heap will not move. When the heap is full of holes like swiss cheese we call it a “fragmented heap”.

Local Variables

Every function call creates a stack frame that makes the stack grow toward the heap. Each stack frame will contain:

  • All parameters passed to the function
  • All local variables declared in the function.

This data is usable within the function, but the space is 100% reclaimed when the function exits!

The Takeaway

Avoid dynamic heap allocations – These can quickly fragment the limited heap-space.

Prefer local to global allocation – Stack variables only exist while they are being used. If you have variables that only are used in a small section of your code, consider making that code into a function and declaring the variables local to the function.

 

Optimize EEPROM

EEPROM is a handy, non-volatile storage space that works well for storing data such as calibration or tuning constants that are not practical to hard-code into Flash.

It is unusual to run out of EEPROM. And it is not often practical to use EEPROM to offload SRAM data. But we’ll mention it here for completeness. Using EEPROM requires that you include the EEPROM library.

#include <EEPROM.h>

The EEPROM library gives us 2 functions:

uint8_t read(int) //Read a byte from the specified EEPROM address

void write(int, uint8_t) //Write a byte to the specified EEPROM address

  • Note that while reads are unlimited, there are a finite number of write cycles limited (typically about 100,000).
  • Always check that EEPROM is ready before reading/writing to it (eeprom_is_ready function)
  • Always prefer the update functions rather than the write ones, as update checks first if the stored data is different than the data, so it erases / writes the new data only if it has changed. Check first if the value has changed before writing, or even use the EEPROMex alternative EEPROM Arduino library.
  • Read / write operations on EEPROM should never be interrupted : you should always disable/clear interrupts (cli()) before any operation and re-enable/set interrupts after (sei()).

Here is the example:

while (!eeprom_is_ready());

cli();

if(eeprom_read_word((uint16_t*)addr) != sensorValue) {

eeprom_write_word((uint16_t*)addr, sensorValue);

}

sei();

ATMega8535 & Arduino IDE

Arduino Uno, merupakan salah satu varian yang saat ini banyak digunakan untuk praktisi dan hobbi elektronika. Arduino adalah pengendali mikro dengan prosesor Atmel AVR dan softwarenya memiliki bahasa pemrograman sendiri. Memiliki library yang cukup komplit serta didukung forum yang ramai, sehingga memudahkan untuk membangun proyek sistem kendali. Library arduino memiliki hampir semua interface dan sesor.

Arduino Uno use widely by electronic practical and hobbyist. Arduino consist of microcontroller which have its own Integrated Development Environtment based on Processing. It has plenty of library supported by many discussion forum, so it will be make easier to build control system projects.

Mikrokontroler arduino bermacam macam, untuk lebih jelasnya dapat dilihat di wikipedia. Kebanyakan menggunakan mikrokontroler Atmel AVR, namun untuk Arduino Yun, Arduino Due dan Arduino Zero menggunakan arsitektur ARM.

Arduino has vary variant depending on its microcontroler, it can be seen through wikipedia. Most of it use AVR, and Atmel Microcontroler. There is also 3 variants using ARM mikrocontroller such as Arduino Yun, Arduino Due and Arduino Zero.

Arduino UNO memiliki pin I/O yang cukup memadai, sekitar 14. Jilka memerlukan pin yang lbih banyak, dapat menggunakan Arduino dengan mikrokontroler jenis ATMega2560 atau jenis ARM yang memiliki I/O 54 pin. Namun sayang sekali memerlukan biaya yang cukup mengusras kantong 😀

Arduino UNO has 14 pin can be used as I/O. If more pin needed, Arduino which employ ATMega2560 or ARM architechture are more elegible to use. Those variant have 54 I/O pin, of cource more expensive than Arduino UNO.

Pada komponen rack yang saya miliki hanya tersedia IC AVR jenis ATMega8, 328, 8535 dan ATTiny2313. Padahal project kali ini membutuhkan I/O sejumlah 20 pin. Mau tidak mau harus mengoprek ATMega8535 yang memiliki 40 pin (32 pin I/O) agar dapat diprogram menggunakan library Arduino. Oke, sekedar sharing ilmu, berikut langkah dan hasilnya :

In my component rack, there are only ATMega8, ATMega328, ATMega8535 and ATTiny2313. I’d tried to use ATMega8535 because I need 20 I/O pin. ATMega8535 has 32 I/O pin. To make ATMega8535 can be used with Arduino library, it can be done by add several line on arduino/hardware/arduino/board.txt as follow:

Tambahkan pada file arduino/hardware/arduino/board.txt dengan baris berikut:

##############################################################

arduino_atmega8535.name=Stumpino-Atmega8535-8Mhz arduino_atmega8535.upload.protocol=stk500v2 arduino_atmega8535.upload.maximum_size=7168 arduino_atmega8535.upload.speed=38400 arduino_atmega8535.bootloader.low_fuses=0xE4 arduino_atmega8535.bootloader.high_fuses=0x98 arduino_atmega8535.bootloader.extended_fuses=0xFD arduino_atmega8535.bootloader.path=atmega arduino_atmega8535.bootloader.file=stk500boot_v2_notdefined.hex #arduino_atmega8535.bootloader.unlock_bits=0x3F #arduino_atmega8535.bootloader.lock_bits=0x0F arduino_atmega8535.build.mcu=atmega8535 arduino_atmega8535.build.f_cpu=8000000L arduino_atmega8535.build.core=arduino arduino_atmega8535.build.variant=stumpinomega40 ##############################################################

arduino_atmega8535-16.name=Stumpino-Atmega8535-External 16Mhz arduino_atmega8535-16.upload.protocol=stk500v2 arduino_atmega8535-16.upload.maximum_size=7168 arduino_atmega8535-16.upload.speed=38400 arduino_atmega8535-16.bootloader.low_fuses=0xEF arduino_atmega8535-16.bootloader.high_fuses=0x98 arduino_atmega8535-16.bootloader.extended_fuses=0xFD arduino_atmega8535-16.bootloader.path=atmega arduino_atmega8535-16.bootloader.file=stk500boot_v2_notdefined.hex #arduino_atmega8535-16.bootloader.unlock_bits=0x3F #arduino_atmega8535-16.bootloader.lock_bits=0x0F arduino_atmega8535-16.build.mcu=atmega8535 arduino_atmega8535-16.build.f_cpu=16000000L arduino_atmega8535-16.build.core=arduino arduino_atmega8535-16.build.variant=stumpinomega40

Kemudian dengan bantuan header yang telah dibut oleh Eric Conner lalu taruh di folder arduino/hardware/arduino/variants/stumpinomega40

Using header that Eric Conner have made, place it in arduino/hardware/arduino/variants/stumpinomega40 folder

Ok, siap pakai. Buka Arduino lalu pilih menu Tool>Board>Stumpino ATMega8535-Eksternal16Mhz dan buka File>Example>01 Basic>Blink. Saya menggunakan downloader USBAsp, jadi saya pilih di bagian menu Tool>Programmer>USBAsp

OK, its ready to use. Open Arduino IDE than choose  Tool>Board>Stumpino ATMega8535-Eksternal16Mhz than open File>Example>01 Basic>Blink. I use USBAsp dwonloader, so I click on Tool>Programmer>USBAsp

Saatnya merangkai ATMega8535 dan ISP-pin di project board dan mencoba. Perlu diketahui, D13 pada ATMega8535 adalah di pin IC kaki 19, jadi tempatkan led di IC pin 19. Silahkan mencoba….

Its time to connect  ATMega8535 and ISP-pin in project board. Arduino D13 pin in ATMega8535 is pin 19. Happy trying…

AVR INT & PCINT

#include <avr/io.h>
#include <stdint.h>            // has to be added to use uint8_t

#include <avr/interrupt.h>    // Needed to use interrupts    

volatile uint8_t portbhistory = 0xFF;     // default is high because the pull-up

int main(void)
{
	//---- INT0
    DDRD &= ~(1 << DDD2);     // Clear the PD2 pin
    // PD2 (PCINT0 pin) is now an input

    PORTD |= (1 << PORTD2);    // turn On the Pull-up
    // PD2 is now an input with pull-up enabled

    EICRA |= (1 << ISC00);    // set INT0 to trigger on ANY logic change
    EIMSK |= (1 << INT0);     // Turns on INT0

	//---- PCINT
    DDRB &= ~((1 << DDB0) | (1 << DDB1) | (1 << DDB2)); // Clear the PB0, PB1, PB2 pin
    PORTB |= ((1 << PORTB0) | (1 << PORTB1) | (1 << PORTB2)); // turn On the Pull-up
    // PB0,PB1,PB2 (PCINT0, PCINT1, PCINT2 pin) are now inputs with pull-up enabled
    
    PCICR |= (1 << PCIE0);     // set PCIE0 to enable PCMSK0 scan
    PCMSK0 |= (1 << PCINT0);   // set PCINT0 to trigger an interrupt on state change 

    sei();                     // turn on interrupts

    while(1)
    {
        /*main program loop here */
    }
}

ISR (INT0_vect)
{
    /* interrupt 0 code here */
}

ISR (PCINT0_vect)
{
    uint8_t changedbits;

    changedbits = PINB ^ portbhistory;
    portbhistory = PINB;
    
    if(changedbits & (1 << PINB0))
    {
        /* PCINT0 changed code here */
    }
    
    if(changedbits & (1 << PINB1))
    {
        /* PCINT1 changed code here */
    }

    if(changedbits & (1 << PINB2))
    {
        /* PCINT2 changed code here */
    }

}