Saturday, January 17, 2015

Turn Arduino into a function generator

In our last post we introduced Project Xenon. In this quick post we will turn Arduino into a function generator. A function generator is a piece of electronic test equipment used to generate different types of electrical waveforms. Some of the most common waveforms produced by the function generator are the sine, square, triangular and sawtooth shapes. 

To create a function generator by using project Xenon all you have to do is to create the following diagram:


There are four blocks that are used to create waveforms: three ,,Extended Generators" and one ,,Extended Sin Source". Extended Sin Source doesn't need a set up. It just generates sine wave on it's output according data provided at it's inputs. We have to set up the three extended generators. As we want to create a square, triangular and sawtooth shapes we set up the generators as follows:




Now each generator generates on it's output a different shape. We have set the same x axis span on each generator. This way we need to create only one frequency generator that will be used to drive all extended generators. The frequency is "simulated" by the speed we provide data from 0 to "x axis span value" on extended generator's input. We simulate the frequency by a resetable integrator.

The integrator provides on it's output data according the data at it's input. If you provide number 1.0 on integrator's input, the integrator will continously increase the output value by 1.0 within 1 second. If you provide number 5.0 on integrator's input, the integrator will continously increase the output value by 5.0 within 1 second. So to "simulate" a frequency we need to reset the integrator if it reaches the number 1.0.

The output done of extended generator is activated if the input data at pos input reaches the end of the x axis span entered in the configuration dialog. So if we interconnect output done with reset input on resetable integrator we have successfully created our simulated frequency.

The previous sample block is used to discard the loop that was created by interconnecting extended generator with resetable integrator. If you discard the previous sample block from the diagram the compiler wouldn't by able to decide whether it should invoke first extended generator or resetable integrator.

Output of each extended generator is multiplied by digital input's state. So if the digital input is 0 the output of multiply block will be zero. If the digital input is 1 the output of multiply block will represent the value of particular extended generator.

Then we sum the outputs from generators together. This way will be able to mix the signals together. Then we multiply them by the value from analog input 15. So we will be able to set the amplitude of generated signal from 0 to 5. 

Then we just convert the signal from 0/5 span to 0/255 span and spread the generated signal among digital outputs.

Then we have connected to the Arduino a DAC R2R network, two potentiometers and four switches.



The DAC will be used to convert the digital value generated by generators into the analog value. The output of DAC is then connected to an oscilloscope. The two potentiometers are used to set the frequency and amplitude of generated waveforms. The four switches are used to select a waveform.

And the results are as follows. First the different shapes:




You can switch between generated waveforms:


You can change the frequency of generated waveforms:



And you are able to change the amplitude of generated waveforms:


And if you look at the diagram, you will notice that we are summing the outputs of the generators. So you are able to mix different shapes together:

Square, triangle and sawtooth waveform summed together
Conclusion

We created this function generator under two hours (including soldering of the hardware). As you can see, by using project Xenon, you are able to create powerful applications quickly without a need to type a single line of code. This way you are able to spend more time by playing with your application and enjoying more the creation process.  

Sunday, January 11, 2015

Introduction of Project Xenon

A new way of programming Arduino devices


How many times have you used a visual representation to explain your thoughts?

Drawing is often used at planning, it is natural for humans. Even by easy task like navigate someone to a certain place it is easier to use a visual explanation (e.g. map) than text description. When you want to design a software (or let someone to design it for you), you will probably explain your thoughts by drawing some boxes and arrows on a paper. And we want to keep it that way.

Just imagine you draw some boxes interconnect them with lines and after you click on the build button something generates the program for you. Yes, this is exactly what hides behind Project Xenon.

Users without background in computer science are often intimidated by the syntax and quirks of programming languages. It takes a long time and practice to learn how to express your imagination and thoughts by programming language. 

In Project Xenon the boxes are called blocks. These blocks represents some functionality like addition, multiplication, logical expression and so on. The functionality represented by the block is accessible through block inputs and the result is provided on the blocks outputs. As you can guess you are making
programs by interconnecting different blocks by dragging lines from one block's output to another one block's input. These lines are called wires and represents arrows from our boxes and arrows example mentioned above.

For better explanation consider the following diagram. There are four blocks. Two value blocks, one sum block and one display block. By interconnecting these block like in the next image you have created your first program.




Let's add some more math, multiply the output of block Sum by number 4.




Now add some logic: if the result is greater than 5, and less or equal to 9.


But we are running these examples on an Arduino device. The real advantage of Arduino is its ability to communicate with the real world. Let's measure a voltage. You can measure voltage on Arduino by using it's analog inputs. Don't worry, you do not have to code. We will just use a block called analog input. This block provides on it's output a voltage measured on a chosen pin.

To make this example more interesting we will use a part that converts a temperature to voltage: LM35. If you dig a little bit around this part you will learn that by multiplying the measured voltage by 100 you get the measured temperature. And to make a little bit more real world example let's turn Arduino into a cooling device that keeps temperature below certain level.



First we will measure the voltage on analog input number 5. Because there is an output from LM35 connected to this analog input we can convert the measured voltage to degrees by multiplying the value by 100. The display block shows that there is 21.9° Celsius inside the room we are working.

Then we add two conditions. We want to start a fan after the measured temperature goes above 30.0° Celsius and turn off the fan after the temperature goes below 25.0° Celsius. By connecting these conditions to a RS block (see RS Flip-flop) we are able to create required condition to drive the fan. 

But, Arduino can't turn on a fan by itself. It doesn't have enough power. So we have to use an Arduino Motor Shield to turn on a fan. We have connected the fan to the ,,A" output of the motor shield . This output can be turned on by setting logic one on Arduino's digital output 3 and turned off be clearing the same output. And at least we will add a Live Scope block to the diagram. We provide a measured temperature and logic condition that drives the fan to this block. This way we will be able to watch the regulation process in real-time.



As you can see, visual programming language lowers the entry barrier for non-programmers. Everything works in a simple drag&drop way. You can manipulate the block parameters in an user-­friendly way, with customized help and live previews. So you know what's going on. Now we would like to introduce some features of Project Xenon. The detailed description will be covered in following articles.

Remote development target
We didn't end up in the middle of the journey. The ability to generate a working program from a diagram is awesome. But that is only the half of the story. After we generated the first program we realized that we want to play a little bit with the result. We want to watch exactly what going on. Or we wanted to change some variables inside a running program. Therefore we developed three special blocks: value, display and live scope. 

The value block provides on its output value you have entered. If you connect to a running program you are able to change this value in real-time. So if you want to fine-tune some values you do not have to reprogram the device over and over again. Everything you have to do is just simply double click on a block you want to change a enter a new value.

The display block is a simple block with one input. It read the value four time a second and display it directly in the diagram. You saw these block in the first examples. Now you now that you are able to change the value any time you like and see the response directly in the diagram.


The ,,live scope" block is the most advanced block in remote target library. It can reads up to eight values at a time and send them as fast as possible back to diagram. You can connect to any ,,Live Scope" and see the data at real-time. You are able to have unlimited scopes in the diagram. The following images show the generation of eight sine waves and the data obtained through ,,Live Scope".



The ability to quickly connect a scope to a wire and see what's going on makes the development extremely fast. You can connect to any wire in the diagram. You are able to change the value of the value in the diagram in real-time. This way you can control, setup and test your solution without any need of reprogramming and running the device again.

Just imagine the last time you ware fine-tuning your solutions. How much time did you spend on it? Now you are able to fine-tune your solution and watch it's response in scope.

Generator and Sine Source
Many times we found ourselves in a situation where we had to produce a signal with a specific shape. It's hard to create and fine-tune signal shape by code. It requires a lot of math and it is a very time consuming process. We wanted to simplify this process as much as possible. So we simplified it to a drag&drop level.

But first to demonstrate this feature we will connect Arduino to a R2R DAC (Digital to Analog Converter) and watch the signal on an oscilloscope. In the following image is an Arduino connected to a R2R network. The output of the R2R network is then connected to an oscilloscope probe.



The individual bits of R2R network are connected to digital outputs 41,39,37,35,33 and 31. We have to spread the generated signal among these digital outputs. The whole diagram of generated signal spread among digital outputs is on the fallowing image.


First we generate the signal by ,,Generator" block. Then we convert it by ,,Convertor" block to values from 0 to 255. Then we connect this data to a block that will split them into bits ,,UINT8 to Bits" and we connect the results to appropriate pins.

We have set up the ,,Generator" block as depicted on the following image. The data varies from 0.0 to 1.0 according the shape we entered by simply moving/adding points. And we set the period of generated signal to 1000 ms.



The result observed by the oscilloscope:


Now if we would like to change the shape all we have to do is to move/add/remove points of the shape. No math or programming required.

The next type of generator is especially suitable when you want to create a sine wave with variable frequency and offset. And even if you want a smooth transition from one frequency to another. Again, we connected Arduino to a DAC to see the signal on an oscilloscope.


The ,,Extended Sin source" generates on it's output data representing a sine wave according the input data. We connected a potentiometer to Arduino's analog input 1. So we can vary the voltage from 0.0 to 5.0 Volts.  By connecting an ,,Analog Input" block to ,,Extended Sin source" block frequency input we are able to generate sine wave from 0.0 to 5.0 hertz. The result is depicted on the following image.




Control library
By using Arduino to control some advanced control system e.g. quadcopter you will certainly end up by using this library. It contains all the blocks required to turn Arduino into an advanced control systems e.g. transfer function, integration and derivation.


We will describe this library in later articles.

Digital I/O, Analog In, Servo Out, PWM Output
The basic application of Arduino is to comunicate and interract with its environment, with the world. Arduino supports many ways to interact with the environment. So we wrap Arduino features into several easy to use blocks. You can turn on LEDs, react to button push, read analog values, control servo motor and drive PWM. Just by combining these blocks with simple logic you are able to create powerful applications.

I2C/SPI protocol generator
This feature belongs to one of our greatest inventions. By entering just a single line, the description of I2C protocol, the environment generates all the stuff needed to communicate with I2C device. You do not have to study all the functionality, registers, interrupts and bother with its implementation to suite your needs. All you have to do is to describe the communication protocol and provide required data you want to send.

You are even able to split complicated protocols into several easy to understand parts and run then sequentially. The following diagram shows initialization and communication with a MPU 6050 device.



The data obtained from MPU 6050 are shown on image below.



You are not limited to only one device, you are able to communicate with as many devices as you want. The one and only thing you need to learn is the protocol used to interact with I2C slave device. 

Conclusion

We put a great effort into this project. Now we want to hear your feedback. There is a lot of awesome functionality we don't mentioned in this article (e.g. creating groups, running groups on a pin interrupt, measuring impulse length and so on). We believe that thanks our project more and more people will be able to became a part of an awesome Arduino community.


Sunday, November 2, 2014

Conquer Arduino's ADC


We ware writing source code for Arduino in a pure C and we wanted to use analog to digital converter (ADC). Nobody likes writing the same source code again and again. So we wanted to make a simple template/library. Our requirements ware:

  • the ADC conversion have to be simply implemented
  • we would not control the ADC conversion, it must be completely automatic
  • the variables that hold analog values can not be randomly changed
  • the variables that hold analog values will be updated only after we let it to do so

First we need to initialize the Arduino's ADC. Datasheet for ATMega2560 stays that the ADC clock frequency should be between 50 - 250 kHz. Values around 50 kHz will give us more accurate measurements whereas values around 250 kHz will lead to less precise measurements but we get a higher conversion speed. So we have to negotiate between speed and precision. For a best compromise we are going to use value somewhere between.

Arduino Mega has a 16MHz crystal that drives processor frequency. This frequency is also used as clock for AD converter. The AD converter can't operate at such high frequency so we need to reduce the clock speed by setting the ADC prescaler. We want a clock frequency between 50 - 250kHz. The best prescaler for this purpose is 128.  This way we get ADC clock frequency at 125 kHz.

      ADCSRA = 0x8F;      //Enable the ADC and its interrupt feature  
                        //and set the ACD clock pre-scalar to clk/128  
                        //at 16Mhz/128 = 125 000 Hz: suggested frequency 50 - 250 Khz  

Next thing is to set up the Arduino's ADC according it's connection to real world. If we look at the Arduino Mega schematic we see that there is a 100n capacitor between AREF (pin 98) pin and GND. Then the AVCC pin is connected to +5V what is the voltage reference we want.



      ADMUX = 0x40; //Select AVCC with external capacitor at AREF pin  
                     //right aligned result and select ADC0 as input channel  

After the init set up we let the ADC to start conversion:

      ADCSRA |= (1<<ADSC); //Start first Conversion  

The whole init source code looks as follows:

 void initADC() {  
      ADCSRA = 0x8F; //Enable the ADC and its interrupt feature  
                      //and set the ACD clock pre-scalar to clk/128  
                      //at 16Mhz/128 = 125 000 Hz: suggested frequency 50 - 250 Khz  
      ADMUX = 0x40;  //Select AVCC with external capacitor at AREF pin  
      setADCChannel();   //and select ADC0 as input channel   
      ADCSRA |= (1<<ADSC); //Start first Conversion  
 }  

Now we have to process the conversion results. Each time a conversion finishes an interrupt service
routine ISR(ADC_vect) will takes place. In ISR we need to get data from ADC registers. We have selected the right adjusted result so we have to read data in following order first ADCL register and just then ADCH. Otherwise the next conversion after ISR will not start!

Note from AT-Mega 2560 Datasheet
When ADCL is read, the ADC Data Register is not updated until ADCH is read. Consequently, if the result is left adjusted and no more than 8-bit precision (7 bit + sign bit for differential input channels) is required, it is sufficient to read ADCH. Otherwise, ADCL must be read first, then ADCH.

      adc_result = ADCL;  
      adc_result |= (ADCH << 8);  

After invoking this few lines the adc_result variable holds the result of ADC conversion. We are not going to convert the result to measured voltage right now because we want the ISR to ends as soon as possible.

Another part of ISR is to prepare conversion on next ADC channel.

We are running conversion only on selected channels. We are selecting channels by inserting theirs number into the adc_channels array. Now after each conversion we run conversion on next ADC channel. To select next ADC channel we use a global variable adc_channels_index. The adc_channels_index is incremented in each ISR call and if we reach the end of adc_channels array we jump on index 0 and begin to process ADC channels from begin. To set the ADC channel we use
the following function.

 void setADCChannel() {  
      unsigned char selectedChannel = adc_channels[adc_channels_index];  
      /*  
       * if we are invoking conversion on ADC channel greater than 7  
       * we have to set bit MUX5 in ADCSRB register. Otherwise we clear  
       * that bit.  
       */  
      if(selectedChannel > 7) {  
           SET_BIT(ADCSRB, MUX5);  
      } else {  
           CLEAR_BIT(ADCSRB, MUX5);  
      }  
      selectedChannel &= 0b00000111;  
      ADMUX = (ADMUX & 0xF8) | selectedChannel;  
 }  

The whole ISR source code looks as follows:

 ISR(ADC_vect) {  
      unsigned int adc_result = 0;  
      adc_result = ADCL;  
      adc_result |= (ADCH << 8);  
      adc_value[adc_channels_index] = adc_result;  
      adc_channels_index++;  
      if(adc_channels_index >= channels_count) {  
           adc_channels_index = 0;  
      }  
      setADCChannel();  
      ADCSRA |= (1<<ADSC); //Start next Conversion  
 }  

Now we are ready to convert ADC values to measured voltage on Arnuino's inputs. The referenced voltage is 5V and we are using 10bit ADC converter so the ADC constant is given by equation:

 ADC_CONST = 5V/1024steps = 0.0048828125 V/step  

To convert ADC value stored in adc_value array we need only one line of code:

 double voltageOnADC0 = adc_value[0]*ADC_CONST;  

According to one of our requirements we want to update variables only after we let it to do so. So we insert the conversion equations into function called updateADC and if we want to update AD values we simply call this function and each variable that holds converted AD value will be updated to latest data available.

 void updateADC() {  
      voltageOnADC0 = adc_value[0]*ADC_CONST;  
 }  

The following source contains complete source code for AT Mega 2560 microprocesors. It measure regulary data on AD channels 1,2,8 and 15. Then the conversion from ADC to voltage takes place by calling updateADC function.

 #define F_CPU 16000000UL  
 #include <avr/io.h>  
 #include <util/delay.h>  
 #include <avr/interrupt.h>  
 #define ADC_CONST 0.0048828125  
 #define SET_BIT(Port, Bit) Port |= (1 << Bit)  
 #define CLEAR_BIT(Port, Bit) Port &= ~(1 << Bit)  
 volatile unsigned char adc_channels[] = {1, 2, 8, 15};  
 volatile unsigned char channels_count = 4;  
 volatile unsigned int adc_value[] = {0, 0, 0, 0};  
 volatile unsigned char adc_channels_index = 0;  
 double voltageOnADC1 = 0.0;   
 double voltageOnADC2 = 0.0;   
 double voltageOnADC8 = 0.0;   
 double voltageOnADC15 = 0.0;   
 void setADCChannel() {  
      unsigned char selectedChannel = adc_channels[adc_channels_index];  
      if(selectedChannel > 7) {  
           SET_BIT(ADCSRB, MUX5);  
      } else {  
           CLEAR_BIT(ADCSRB, MUX5);  
      }  
      selectedChannel &= 0b00000111;  
      ADMUX = (ADMUX & 0xF8) | selectedChannel;  
 }  
 ISR(ADC_vect) {  
      unsigned int adc_result = 0;  
      adc_result = ADCL;  
      adc_result |= (ADCH << 8);  
      adc_value[adc_channels_index] = adc_result;  
      adc_channels_index++;  
      if(adc_channels_index >= channels_count) {  
           adc_channels_index = 0;  
      }  
      setADCChannel();  
      ADCSRA |= (1<<ADSC); //Start next Conversion  
 }  
 void initADC() {  
      ADCSRA = 0x8F;      //Enable the ADC and its interrupt feature  
                //and set the ACD clock pre-scalar to clk/128  
                //at 16Mhz/128 = 125 000 Hz: suggested frequency 50 - 250 Khz  
      ADMUX = 0x40;     //Select AVCC with external capacitor at AREF pin  
                //and select ADC0 as input channel   
      setADCChannel();  
      ADCSRA |= (1<<ADSC); //Start first Conversion  
 }  
 void updateADC() {  
      voltageOnADC1 = adc_value[0]*ADC_CONST;  
      voltageOnADC2 = adc_value[1]*ADC_CONST;  
      voltageOnADC8 = adc_value[2]*ADC_CONST;  
      voltageOnADC15 = adc_value[3]*ADC_CONST;  
 }  
 int main() {  
      SET_BIT(DDRL, 7);  
      SET_BIT(DDRL, 6);  
      SET_BIT(DDRL, 5);  
      SET_BIT(DDRL, 4);  
      initADC();  
      while(1) {  
           cli(); //Disable Global Interrupts  
           updateADC();  
           sei(); //Enable Global Interrupts  
           if(voltageOnADC1 >= 2.5) {  
                SET_BIT(PORTL, 7);  
           } else {  
                CLEAR_BIT(PORTL, 7);  
           }  
           if(voltageOnADC15 >= 2.5) {  
                SET_BIT(PORTL, 6);  
           } else {  
                CLEAR_BIT(PORTL, 6);  
           }  
           if(voltageOnADC8 >= 2.5) {  
                SET_BIT(PORTL, 5);  
           } else {  
                CLEAR_BIT(PORTL, 5);  
           }  
           if(voltageOnADC2 >= 2.5) {  
                SET_BIT(PORTL, 4);  
           } else {  
                CLEAR_BIT(PORTL, 4);  
           }  
           _delay_ms(500);  
      }  
 }  

Hope you enjoy this blog post and if you want to learn more follow us on Twitter.

Sunday, October 26, 2014

Let's do it by Java

Today we wanted to compile native C source code for atmega2560 and run it on arduino. But we didn't want to use Arduino IDE. We wanted to make the compilation process complete automatic by using Java. To make the long story short:

First we had to find out how to compile source code for Arduino Mega2560 from command line. Second use Java to run commands.

Our sample program will be simple blinking application. The LED located at Arduino Mega2560 board (pin PB7 represented as pin 13 on Arduino) will blink at 1Hz frequency.

 #define F_CPU 16000000UL  
 #include <avr/io.h>  
 #include <util/delay.h>  
 #define SET_BIT(Port, Bit) Port |= (1 << Bit)  
 #define CLEAR_BIT(Port, Bit) Port &= ~(1 << Bit)  
 int main() {  
      SET_BIT(DDRB, 7);  
      while(1) {  
           SET_BIT(PORTB, 7);  
           _delay_ms(500);  
           CLEAR_BIT(PORTB, 7);  
           _delay_ms(500);  
      }  
      return 0;       
 }  

Save this source code into the file called source.c. Now compile it by invoking this command:

 avr-gcc -mmcu=atmega2560 -Os -Wall -osource.elf source.c  

This will compile the source.c file and store the result of compilation in file called source.elf. The next step is to extract portions of the binary (source.elf) and save the information into .hex files. The GNU utility that does this is called avr-objcopy.

 avr-objcopy -j.text -j.data -Oihex source.elf source.hex  

And now just upload it by using avrdude to Arduino:

 avrdude -CC:\tools\avr\etc\avrdude.conf -cwiring -patmega2560 -PCOM6 -b115200 -F -Uflash:w:source.hex -D  

Note: the -D option means disable chip erasing before programming. But the bootloader erases the flash by default and the erase is probably not implemented by the bootloader because the avrdude fail to upload the hex file without the -D option. It will print the following error.

 avrdude: NOTE: FLASH memory has been specified, an erase cycle will be performed  
      To disable this feature, specify the -D option.  
 avrdude: erasing chip  
 avrdude: stk500v2_command(): command failed  

If the upload process was successful the LED should start to blink.

Now we are ready to implement the compilation and upload process in Java. The preferred way of invoking commands in Java is to use ProcessBuilder. One thing to mention is, always read the process output. Otherwise the output buffer may become full and the process will stop until the buffer becomes empty.

 package com.mechatronichacks.arduino;  
 import java.io.File;  
 import java.io.IOException;  
 import java.util.Scanner;  
 public class ArduinoUpload {  
      public ArduinoUpload() {  
           try {  
                int result = this.execute(new String[]{"C:\\tools\\avr\\bin\\avr-gcc", "-mmcu=atmega2560", "-Os", "-Wall", "-osource.elf", "source.c"});  
                if(result == 0) {  
                     System.out.println("Compilation successful");  
                }  
                if(result == 0)     {  
                     result = this.execute(new String[]{"C:\\tools\\avr\\bin\\avr-objcopy", "-j.text", "-j.data", "-Oihex", "source.elf", "source.hex"});  
                }  
                if(result == 0) {  
                     System.out.println("Linking successful");  
                }  
                if(result == 0) {  
                     result = this.execute(new String[]{"C:\\tools\\avr\\bin\\avrdude", "-CC:\\tools\\avr\\etc\\avrdude.conf", "-cwiring", "-patmega2560", "-PCOM6", "-b115200", "-F", "-Uflash:w:source.hex", "-D"});       
                }  
                if(result == 0) {  
                     System.out.println("Upload successful");  
                }  
           } catch (IOException e) {  
                e.printStackTrace();  
           } catch (InterruptedException e) {  
                e.printStackTrace();  
           }  
      }  
      private int execute(String[] command) throws IOException, InterruptedException {  
           ProcessBuilder builder = new ProcessBuilder(command);  
           //now set the working directory for process execution  
           builder.directory( new File("C:\\MechatronicHacks\\").getAbsoluteFile() );   
           builder.redirectErrorStream(true);  
            Process process = builder.start();  
           Scanner s = new Scanner(process.getInputStream());  
           StringBuilder text = new StringBuilder();  
           while (s.hasNextLine()) {  
                text.append(s.nextLine());  
                text.append("\n");  
           }  
           s.close();  
           System.out.println(text);  
           return process.waitFor();  
      }  
      public static void main(String[] args) {  
           new ArduinoUpload();  
      }  
 }