Tag Archives: Uncategorized

Bank Holiday kind of blog

via Raspberry Pi

The blog today is that there is no blog today. It’s Spring bank holiday here in the UK which means that we are all too busy rolling cheeses down hills, re-enacting obscure battles against baddies and doing stuff like this:

StompMorrisdance

Also, the education team are all en route to York for Picademy North. See you Tuesday!

The post Bank Holiday kind of blog appeared first on Raspberry Pi.

Getting started with the Internet of Things

via Raspberry Pi

By 2020 there will be twelvety gigajillion of Internet Things all shouting at each other and sulking – Alice Bevel

The Internet of Things had been around for a while (since 1982 apparently) but it’s still a bit of a mystery to many. The concept of hooking up physical devices and letting them talk to each other is great, but how do you get started? How do you get useful data from them?

A Thing

A Thing

I’ve been playing around with IoT this week and came across this great starter IoT project for the Pi, a people counting project by Agustin Pelaez. It’s an oldie but goodie and worth a mention because it’s as simple as it gets in terms of IoT—a sensor sends data to a server, which then presents the data in a nice, human-friendly form.

PIR connected to Pi

A £2 PIR connected directly to the Pi with just three wires ( Photo: Agustin Pelaez)

It’s also as cheap as chips—apart from a Pi you only need a passive infra-red sensor (PIR) as used in several of our resources. We love PIRs: they cost a couple of quid, connect directly to the Pi GPIO pins and they can be used for all sorts of useful and/or mad projects. The basic Ubidots account that stores and analyses the data is free. So this is an ideal IoT beginners’ project— cheap, straightforward and can be adapted to other projects. (Note that there is a bug in the code, peopleev = 0 should read peoplecount = 0.)

node-red and thingbox

Node-RED on Thingbox, controlling LEDs on the Pi via the web (Photo: thethingbox.io)

If you want to dig further without too much pain, the ThingBox has an SD card image for the Pi that allows you to “Install Internet of Things technologies on a Raspberry Pi without any technical knowledge” and has a number of basic projects to get you started. It works with Ubidots out of the box and has a number of tutorials that will help you learn common IoT tools like Node-RED on the Pi (including a PIR counter project which is a nice compare-and-contrast  to the Python based one above.)

I like the ThingBox a lot. It lowers the activation energy needed to get started with IoT on the Pi (actually, it makes it easy) and it allows all Pi owners access to what appears at first glance to be an arcane … Thing. The Internet of Things is fun, useful and empowering, and a natural extension to physical computing using the GPIO pins on the Pi. Hook up some Things today and have play.

Another Thing

Another Thing

The post Getting started with the Internet of Things appeared first on Raspberry Pi.

Pocket PiGRRL: is that an emulator in your pocket?

via Raspberry Pi

PiGRRL and robot

Huge, city-destroying robot for scale (Photo: Adafruit/ Ruiz Brothers)

Last year we went into full retronerd dribble mode over the PiGRRL, a Pi-powered Gameboy. Our friends at Adafruit have brought the project up to date with the Pocket PiGRRL. It has a 3D printed case and uses the smaller Raspberry model A+ plus a 2.4″ PiTFT HAT to bring the size down. Emulation is handled by RetroPie.

Adafruit say that “it’s easier than our previous PiGRRL build” and the use of components such as the amplifier and charger means that it’s suitable for a confident beginner—at the very least you’ll earn your soldering skills badge.

The post Pocket PiGRRL: is that an emulator in your pocket? appeared first on Raspberry Pi.

The Inquirer Tech Hero Awards

via Raspberry Pi

inq-tech-hero-awards-logo-540x334

This is not a flashy post with lots of pictures and farkles. It’s a thank you note.

In February 2012 we started selling the Raspberry Pi  (you may not have heard the story—it went pretty well ;)). A year later we moved into a small office. There were six of us.

Our very first office. Aww, cute.

Our very first office. Aww, cute.

Less than a year later we stormed the penthouse floor of the same building and have been there ever since. (We even have our own fridge now so we don’t have to write our names on our pies with a Sharpie). And here we are three years later—things have gone swimmingly and we’ve picked up one or two awards (OK, lots) along the way. We love getting awards but not (just) in a big-headian way. They are a confirmation that we are doing something right and doing something good.

Last night I travelled on the InterCity to London to attend the first ever Inquirer Tech Hero Awards (Liz and Eben are away and I was sent because I most resemble him physically). We were nominated in three categories: The Raspberry Pi 2 won ‘Best Product’ and Eben won ‘Tech Champion’. We had a great night, chatted to a load of people and we brought back two impressive chunks of glass that say, “Keep doing your thing, it’s good.”

The best photo I could manage - these things are shiny AND transparent

The best photo I could manage – these things are shiny AND transparent

So thanks to the Inquirer and to everyone who voted. But the biggest thank you goes to our amazing, unique and lovely community. We wouldn’t be here without you. Cheers.

The post The Inquirer Tech Hero Awards appeared first on Raspberry Pi.

Two factor authentication safe deposit box

via Raspberry Pi

github 2fa dialog

A skilled hacker attempts to crack 2FA

Securing your digital life with two-factor authentication (2FA) is pretty common nowadays. A password alone just doesn’t hack it. (Or does it?) Typically, 2FA on the web requires a one-time code, sent to your phone, as well as your password to log in. Other systems may use factors such as biometrics (e.g. fingerprints) or hardware dongles. (My own bank requires me to use a silly little (very losable) card reader every time I want to transfer a fiver.)

Safety deposit box with two-factor authentication

Safety deposit box with two-factor authentication

Thinking inside the box

Pablo Carranza Vélez decided to apply the principle of 2FA to a physical object, a Raspberry Pi controlled safe deposit box. To get into the box you need both your personal entry code and the code sent to your phone. This then triggers a solenoid to unlock the box.

Lots of cool stuff going on

The box uses resin.io and Authy API. Full details, schematics and code can be found on  Pablo’s hackster.io page. It’s a simple concept but there’s lots going on in terms of hardware and software—Authy, resin.io, MongoDB, node.js, Bootstrap, breadboard circuits, solenoids—to make a great project and an interesting proof of concept. It’s also an excellent introduction to the contemporary technologies used and there’s even some computer science with a nod to state machines.

Opening the resin safebox from Pablo Carranza Vélez on Vimeo.

A pre-emptive note thing

“Just hold on this minute!” shouts a completely imaginary concerned reader. “You could (literally) brute force it with a sledge hammer/ hack it with a giant Wile E. Coyote magnet/ steal the building it was attached to / drill a hole in it and send in a tiny monkey to feebly tug at the solenoid.” Well yes, you could. To all of them.

Our advice is: do not make one of these to store your ultra-rare U2 Panini stickers in as they might get nicked (my brother swears to this day that it wasn’t him who drew NHS specs on Bono in red biro). The 2FA safe box is a thought provoking Raspberry Pi / IoT project, not the Old Lady of Threadneedle Street, lawks! (I’ve always wanted to write that, it’s a cracking name :))

Solenoid control breadboard prototype

Solenoid control breadboard prototype

The post Two factor authentication safe deposit box appeared first on Raspberry Pi.

Snapping Stills with the Red TED Letters

via Raspberry Pi

TED2015 Photo Booth

I’m a bit of a TED talk fiend, so I was delighted to find out that a few Raspberry Pis had a hand in letting TEDsters capture their photo along with the iconic red TED letters at TED2015 in Vancouver. To explain:

A funny thing happens at the end of every TED — after the show wraps, first one brave person, and then duos and groups, rush the stage to get their picture taken with the red TED letters. So at TED2015, our tech team thought, why not bring the letters out to the conference floor and let everyone play? In a nights-and-weekends project, our engineers built a self-service photobooth that encouraged all TEDsters — attendees, staff and more — to strike a pose. By the end of the conference, 1,296 photos had been taken with the booth.

“A lot of people wanted to get jump shots,” says software engineer Joshua Warchol, who helped conceptualize and build the booth. “Some groups would spend 10 minutes trying to get the perfect aerial moment. And I also had not anticipated that people would feel an unstoppable compulsion to climb the letters. We had to get them repainted mid-week because of nicks and scuffs.”

We see a lot of people implementing great photo booths with Raspberry Pi, but what I especially like about this one is that it’s activated when a group scans their NFC badges. After the photo is taken, it automatically emails it to the group that badged in. Of course, the whole operation comes together with a lot of different off-the-shelf technologies working in concert. Along with the Raspberry Pis, there’s a tablet interface, Adafruit NFC reader, a camera, and a BlinkStick LED module to indicate the camera’s position and status. Luckily for us, Joshua posted all the details about how he pulled this off, including the code. Thanks, Joshua!

Interface

Camera1

The post Snapping Stills with the Red TED Letters appeared first on Raspberry Pi.

Modulo: a simple, modular solution for building electronic devices

via Raspberry Pi

We came across the excellent Modulo on Kickstarter recently. Modulo is a “modular solution for building powerful electronic devices” and consists of a cluster of smart modules such as sensors, motor drivers and displays that slide into a solid base unit.

modulo

It abstracts all the jumbliness of building electronics from scratch and lets you concentrate on concepts, design and programming cool stuff.

Once you’ve built your project you can program it in Python on the Pi. It also plays nice with Arduino and Spark (now Particle).

Poseidon, a robotic landscape watering system. Using Modulo and a Pi it delivers just the right amount of water based on the weather forecast.

Poseidon, a robotic landscape watering system. Using Modulo and a Pi it delivers just the right amount of water based on the weather forecast.

The Raspberry Pi crew met the Modulo team at MakerCon this week and were, of course, challenged to Modulo Pong. Eben won 26-14 (I completely made that up based on a recurring dream in which the score is always the same and Eben is dressed as a giant ping pong ball.)

Modulo controlled Pong

Modulo controlled Pong

We like Modulo a lot. It’s smart, funky and a great way to get into digital creativity. Also, it has “Knob kit” and a tea brewing robot and it would be very un-British of us on both counts if we didn’t let that influence us.

Modulo has already reached its primary Kickstarter goal but is now aiming for sufficient funding to make Modulo a sustainable concern. Take a look if, like us, you’d like to see this happen.

The post Modulo: a simple, modular solution for building electronic devices appeared first on Raspberry Pi.

Price cut! Raspberry Pi Model B+ now only $25

via Raspberry Pi

When we announced the Raspberry Pi 2 back in February, we said that we’d continue to support its predecessor, the Raspberry Pi Model B+. Since then, we’ve sold well over a million units of Raspberry Pi 2, but Model B+ has also continued to sell very well, despite also costing $35.

$10 cheaper, but just as much fun

$10 cheaper, but just as much fun

A side effect of the production optimizations that allowed us to hit the $35 target price for Raspberry Pi 2 is that the Model B+ is now much cheaper to manufacture than it was when it was introduced. With this in mind, we’ve decided to drop its list price to $25. If you’re looking for a Raspberry Pi with networking and multiple USB ports, and don’t need the extra performance or memory that the Raspberry Pi 2 brings, you might want to check it out.

The Raspberry Pi product line now stretches from the Model A+ at $20, via the Model B+ at $25, to the top-of-the-range Raspberry Pi 2 at $35. The new pricing will take effect across our partners’ websites over the next few days, but right now RS Components in the UK and MCM Electronics in North America look like your best bet.

The post Price cut! Raspberry Pi Model B+ now only $25 appeared first on Raspberry Pi.

The Sense HAT: headgear for the terminally curious

via Raspberry Pi

Having looked at the chunky outside goodness of the Astro Pi case yesterday it seems only fair to take another look at the heart of the Astro Pi, the Sense HAT. (This is not a conical cap that you put on the really clever kid and stand him in the corner but our add-on board for the Pi bristling with sensors and other useful things.) It’s currently going out to schools and organisations who took part in our recent competition but we also plan to sell it.

A Raspberry Pi wearing a Sense HAT

A Raspberry Pi wearing a Sense HAT

The full tech specs are here but basically it has:

  • 8×8 LED matrix display
  • accelerometer, gyroscope and magnetometer
  • air pressure sensor
  • temperature and humidity sensor
  • a teeny joystick
  • real time clock

The Astro Pi site explains what these all do and how they could be used.

I’m really excited about the Sense HAT. With all of those sensors on a single board it’s obviously a brilliant tool for making stuff (I have in mind a self-balancing attack robot that senses humans, aggressively hunts them down and then gently dispenses Wagon Wheels from its slot-like mouth). But it’s the potential for science that’s making me think. In particular I’d love to see it flourish in the science classroom.

A typical school science classroom

A typical school science classroom

Despite the teacher recruitment ads that inevitably show zany antics with Van der Graaff generators, explosions and dancing bonobos the reality is that much of high school science is about experimentation and observation (which is a good thing!). But lab kit such as sensors, controllers and data loggers don’t come cheap (I was once told by a class that their usual science teacher never let them use the data loggers because “they were too expensive). Nor is it easy to get bits of kit to talk to each other or the Internet of Things (with the potential benefits that come from that such as improved assessment, parental involvement, sharing and consolidating data).

data logger

A data logger I found in a school skip. The size of a cash register yet only logs temperature. On paper.

A Pi wearing a Sense HAT could do everything from monitoring plant growth to controlling and logging experimental variables. A series of experiments using the accelerometer/gyroscope to investigate forces and equations of motion is mandatory. Feel free to add your own ideas below and if any science teachers would like to get involved the please get in touch.

If you are lucky enough to already have a Sense HAT, Martin “When does that man sleep?” O’Hanlon has written an excellent getting started tutorial . If not then it’s worth taking a look anyway to get a sense (yeah, yeah :)) of what it can do.

Astro Pi sense HAT LED

Up above the streets and the houses, Astro Pi climbing high.

The final price is yet to be announced but we’re confident that there will be nothing else out there to rival it for value, potential, support and resources. Keep your eyes peeled for more news on the Sense HAT soon.

The post The Sense HAT: headgear for the terminally curious appeared first on Raspberry Pi.

Astro Pi flight case

via Raspberry Pi

You can’t just take a Raspberry Pi into space in your pocket or an old soft scoop ice cream tub. It’s too spiky for one thing. What you need is a block of aluminium the size of your head and some mad milling skills to make the best Pi case ever. Dave Honess explains:

The latest update to the Astro Pi project is the unveiling of the Astro Pi aluminium flight case that British ESA Astronaut Tim Peake will be using on the ISS. This will not be available to the public to buy because we’re only making a small number of them. We may however, in due course, release an object file so schools with a 3D printer can print one themselves.

The Astro Pi flight case.

The Astro Pi flight case.

The image below was taken from the CAD software that we used to model the case. The real thing will be made from 6061 grade aluminium which is standard for aerospace applications.

This is the top view showing the opening for the LED matrix and joystick along with a quad of buttons with an adjacent pair. The face on the right is used to attach it to a Bogen arm so that the Astro Pi can be held in place, for example if the crew want to aim the camera out of the cupola window.

The hole next to the joystick is to allow air to flow inside and reach the temperature, pressure and humidity sensors.

case_final_1

The design may surprise you because it’s so big a bulky. It satisfies the safety requirements that ESA and NASA have in place for small payloads aboard the ISS though!

This is not a 3d rendering.

This is not a 3d rendering!

The most important of these requirements is touch temperature. There is a rule that any surface, that the crew can touch, must not reach or exceed 45 degrees Celsius. Our Jonathan Bell and SSTL’s Nimal Navarathinam did extensive thermal simulations to work out this design. That’s really hard maths to you and me!

IMG_2033

In space the process of convection doesn’t happen. On Earth the air warmed by a Pi CPU will rise as colder air is pulled down by gravity. On the ISS the air warmed by a CPU just stays there and bakes it. So the case has been designed for thermal dissipation in mind. There is a guaranteed level of airflow within all of the ISS modules which makes this possible.

Those pillars on the base each dissipate about 0.1 Watts of heat. All of them combined with the surface area of the case ensure that the Astro Pi can never get anywhere near 45 degrees. Inside the case the Raspberry Pi is thermally joined to the aluminium by way of a heat conductive boss / slug.

case_final_3

The view above shows the hole for the camera module in the middle too. So make a mental note that the camera faces in the opposite direction to the LED matrix when in flight configuration. You can also see the corner bolts that hold the two halves of the case together.

IMG_2019

The ISS crew are also really fond of bungee cords, the kind you might use for camping or mountaineering with those hooks on the ends, and so the four corner columns have holes to allow bungee cords to be used to lash the Astro Pi to whatever they want.

IMG_2016


So that’s about it for now. The only thing left to tell you is that we’ve now reached and successfully passed phase two of the flight safety process with ESA. So it’s only phase three to go now before we get the mighty flight safety certificate that says we can go on the Soyuz rocket with Tim! Between now and the phase three review all the testing will occur at SSTL and Airbus Defence and Space.

Here is a brief summary of the tests we have to do:

RTC (real time clock) Battery

  • Measurement of open circuit voltage and loaded voltage
  • Vacuum exposure test (450 mmHg for 2 hours)
  • Post vacuum inspection for leaks and deformation
  • Post vacuum measurement of open circuit voltage and loaded voltage

Flight Hardware

  • Operation checks (verify it boots and functions correctly)
  • Power integration test (using an ISS mains AC inverter)
  • Thermal test (stress the Astro Pi to cause maximum current consumption / heat dissipation and measure touch temperature)
  • Vacuum thermal test (same as the above but in a vacuum)
  • Off gassing assessment (determination of off gassing products from materials and assembled articles to be used inside the ISS modules)
  • EMC test
  • Vibration test (Soyuz launch vehicle conditions)

Keep an eye on social media over the coming weeks for as it happens updates!

Thanks for reading.

The post Astro Pi flight case appeared first on Raspberry Pi.

Despicable Katie’s Minion Pi

via Raspberry Pi

When we were at SXSW Create in Austin back in March, teaching hundreds of kids about Bash script, we met a young lady called Katie.

Katie looks unassuming, but she’s actually an evil genius.

katieandeben

Some guy who needs a shave, and Katie.

Some of the other kids we talked to during the three-day event hadn’t done a huge amount with a Raspberry Pi before (although we saw a lot of young people who were using Pis at school or home for Scratch programming or Minecraft); but there were some real experts among the crowd – and Katie, who came along to tell us all about what she’s been doing with her Raspberry Pi, and to get some tips, was the sinister stand-out.

Katie’s been using a Pi at home for some years for a number of projects, but her most ambitious yet is her Minion Pi, which she was preparing for a school science exhibition when we met her. Minion Pi is a customised Kossel Mini 3D printer driven by a Raspberry Pi 2 – Katie’s built a custom power supply and a custom user interface on an iPad using her phone as a hotspot, so the whole thing is completely self-contained. Why? She’s building an army of Minions, her first step towards world domination.

MinionPi_RPi

(An important note: Katie is eleven years old. Like most of us here at Pi Towers, when I was eleven I was mostly making forts in shrubbery and chewing stuff that wasn’t meant to be chewed; not building complicated computing systems.)

Katie made this board to explain how her project works. Click to embiggen.

Katie made this board to explain how her project works. Click to embiggen.

Here’s Katie’s diabolical contraption in action. (Printing Minions, naturally.)

Gru Katie mailed me to say:

I’ve still got some work to do on the project (Pi Cam should arrive on Thursday), but it’s running really, really well, and the Raspberry Pi makes it easy to control from my iPad, and made it really easy to take to school and demo. People are already paying me to print stuff!

Katie’s made a very detailed build diary available (she warns me that the server it’s on is easily overloaded, so it’s possible you may have to come back later to look at it). Congratulations, Katie: this is an amazingly polished project. Please look upon us kindly when you are Despicable World Queen.

The post Despicable Katie’s Minion Pi appeared first on Raspberry Pi.

WaterFall Spectrogram.

via coolarduino

Playing with my new toy (TFT ILI9486) I discovered, that its support vertical scrolling feature.  And my fft library needs a demo-video, so idea for new project was born. Waterfall spectrogram, real-time, audio 20 – 20 000 range. Main board is arduino DUE.  Plus capacitors and couple resistors to bias analog input at V / 2. Rectangular pulse from my DSO forms wide harmonics content, excellent source to demonstrate aliasing -);.

 

vsc_setup() sends 0x33 command to tft, setting vertical scroll area. and vScroll() 0x37 rotates image.

#include <SplitRadixReal.h>
#include <UTFT.h>

#define   SMP_RATE          44100UL 
#define   CLK_MAIN       84000000UL
#define   TMR_CNTR       CLK_MAIN / (2 *SMP_RATE)

// FFT_SIZE IS DEFINED in Header file Radix4.h 
// #define   FFT_SIZE           2048

#define   MIRROR         FFT_SIZE / 2
#define   INP_BUFF       FFT_SIZE 
         
volatile   uint16_t   sptr              =   0 ;
volatile    int16_t   flag              =   0 ;

           uint16_t  inp[2][INP_BUFF]   = { 0};     // DMA likes ping-pongs buffer

            int         f_r[FFT_SIZE]   = { 0};
            int         magn[MIRROR]    = { 0};     // Magnitudes
const       int         dc_offset       = 2047; 

            uint8_t          print_inp  =    0;     // print switch

            SplitRadixReal     radix;


#define     SIZE_X                          320
#define     SIZE_Y                          480
#define     BORDER                            5
#define     BFA                          BORDER  // Bottom Fixed Area
#define     TFA                      (SIZE_Y /2)  // Top Fixed Area 240
#define     VSH             (SIZE_Y - BFA - TFA) // Vertical Scroll Area 235
#define     CSEL                             40
#define     WFALL           (SIZE_X -(2* BORDER) -2)// Width-306
#define     MAPCF                             3  // Re-map coefficient, 1024 : 320

            UTFT        myGLCD( ILI9486, 38, 39, CSEL, 41);
extern      uint8_t     BigFont[];

            int         vsp                 = 0;

            int         imag[WFALL]      = { 0};     // Image-1
            int         blgo[WFALL]      = { 0};     // Image-2
            uint16_t    color_map[1024]  = { 0}; 
            uint16_t    log10_map[1024]  = { 0}; 

void setup()
{
  Serial.begin (115200) ; 
  adc_setup ();         
  tmr_setup ();         
//
  myGLCD.InitLCD(PORTRAIT);
  myGLCD.clrScr();

  vsc_setup();
  set_lake();  
  gen_cmap();  // color wheel map (LUT)
  gen_lmap();  // log-10 map (LUT)
  
  pinMode( 44, OUTPUT); // fft
  pinMode( 45, OUTPUT); // re-map
  pinMode( 46, OUTPUT); // pour_water
  pinMode( 47, OUTPUT); // belagio

  pinMode( 55, OUTPUT); // MIC Power
  digitalWrite( 55, HIGH);
}

inline int mult_shft12( int a, int b)  
{
  return (( a  *  b )  >> 12);      
}

void loop() 
{
  if ( flag )
  {   
   uint16_t indx_a = flag -1;
   uint16_t indx_b = 0;

digitalWrite( 44, HIGH);
   for ( uint16_t i = 0, k = (NWAVE / FFT_SIZE); i < FFT_SIZE; i++ ) 
   {  
      uint16_t windw = Hamming[i * k];
      f_r[i] = mult_shft12((inp[indx_a][indx_b++] - dc_offset), windw);
   }

   if( print_inp ){
     Serial.print("ntBuffer: ");    
     Serial.print(indx_a, DEC);       
     prnt_out2( f_r, FFT_SIZE);
     print_inp =  0;
     }

   radix.rev_bin( f_r, FFT_SIZE);
   radix.fft_split_radix_real( f_r, LOG2_FFT);
   radix.gain_Reset( f_r, LOG2_FFT -1); 
   radix.get_Magnit2( f_r, magn);

digitalWrite( 44,  LOW);
digitalWrite( 45, HIGH);

   re_map();
digitalWrite( 45,  LOW);
digitalWrite( 46, HIGH);

   pour_water();
digitalWrite( 46,  LOW);
digitalWrite( 47, HIGH);

   belagio();
digitalWrite( 47,  LOW);

   while (Serial.available()) {
        uint8_t input = Serial.read();
        switch(input) {
        case 'r':
            break;           
        case 'x':
            print_inp = 1;
            break;
        case 'f':
            Serial.print("ntReal: ");    
            prnt_out2( f_r, MIRROR);
            break;
        case 'o':
            Serial.print("ntMagnitudes: ");    
            prnt_out2( magn, MIRROR);
            break;
        case '?':
        case 'h':
            cmd_print_help();
            break;
        default: // -------------------------------
            Serial.print("Unexpected: ");
            Serial.print((char)input);
            cmd_print_help();
        }
    Serial.print("> ");
    }
   flag = 0;
   }   
}

void prnt_out2( int *array, int dlina) 
{
  Serial.print("nt");      
     for ( uint32_t i = 0; i < dlina; i++)
     {
       Serial.print(array[i]);       
       Serial.print("t");    
       if ((i+1)%16 == 0) Serial.print("nt");
     }
  Serial.println("nt");
}

void cmd_print_help(void) 
{
    Serial.println("n  Listing of all available CLI Commandsn");
    Serial.println("t"?" or "h": print this menu");
    Serial.println("t"x": print out adc array");
    Serial.println("t"f": print out fft array");
    Serial.println("t"o": print out magnitude array");
}

 

void vsc_setup(void)
{
  digitalWrite( CSEL, LOW);
  myGLCD.LCD_Write_COM( 0x33 );

  char BH0 = (TFA  >>   8);
  char BL0 = (TFA  & 0xFF);
  myGLCD.LCD_Write_DATA( BH0);
  myGLCD.LCD_Write_DATA( BL0);

  char BH1 = (VSH  >>   8);
  char BL1 = (VSH  & 0xFF);
  myGLCD.LCD_Write_DATA( BH1);
  myGLCD.LCD_Write_DATA( BL1);

  char BH2 = (BFA  >>   8);
  char BL2 = (BFA  & 0xFF);
  myGLCD.LCD_Write_DATA( BH2);
  myGLCD.LCD_Write_DATA( BL2);

  digitalWrite( CSEL, HIGH);
}

void set_lake(void)
{
  myGLCD.setFont(BigFont);
  myGLCD.setBackColor( 255, 255, 255);
  myGLCD.setColor(255, 255, 255);
//    myGLCD.setColor( 150, 255, 255);
  int dispx = SIZE_X - 1;
  int dispy = SIZE_Y - 1;

  myGLCD.fillRect( 0, 0, dispx, dispy);
  
  myGLCD.setColor(   0,   0, 255);
  int x1 = BORDER -2;
  int x2 = SIZE_X - x1;//BORDER -2;
  int y1 = BORDER -2;
  int y2 = SIZE_Y - y1;//BORDER -2;
//  myGLCD.drawRect(x1, y1, x2, y2); 
  myGLCD.fillRect(    0,   0,    x1, dispy); // left
  myGLCD.fillRect(   x2,   0, dispx, dispy); // right
  myGLCD.fillRect(    0,   0, dispx,    y1); // top
  myGLCD.fillRect(    0,  y2, dispx, dispy); // bot
//
  int strip = VSH / 2;
  myGLCD.setColor( 255,   0, 255);
  myGLCD.fillRect(  0, (y2 -2),    x1, (y2 -2 -strip)); 
  myGLCD.fillRect( x2, (y2 -2), dispx, (y2 -2 -strip));  
// riski
  myGLCD.setColor( 0, 255, 255);

  myGLCD.drawRect( x1, 50, x2, 53);    // Draw a Scale

  for ( int i = x2, cont = 0; i > x1; i -= 15, cont++ ) {
  
    myGLCD.drawRect( i, 50, i+2, 45);
    if(((cont %5) ==0) && (cont != 0)) {
      myGLCD.fillRect( i, 50, i+2, 40);
    }
  }
  myGLCD.print("0   5   10   15   20", x2, 30, 180);
}


void pour_water(void)
{
  int y = vsp + TFA;      //y = bottom line of scroll area 
  myGLCD.vScroll( y );    //roll frame.

  int x = SIZE_X - BORDER;
  for( int i = 0; i <  WFALL; i++, x--) { 
      int temp = imag[i];
      myGLCD.setColor( temp ); 
      myGLCD.drawPixel( x, y );
    }
  if(++vsp >= VSH) vsp = 1;
}

void belagio(void)
{
  int st_x = SIZE_X - BORDER; 
  int st_y = BORDER + 50; //dlya risok

// 20 ms
  myGLCD.setColor( 255, 255, 255);
  myGLCD.fillRect( st_x, st_y, st_x - WFALL, VSH);
  //  myGLCD.setColor(VGA_SILVER);

  myGLCD.setColor(  0,  0, 255);

  for( int i = 0; i <  WFALL; i++, st_x--) { 
      int temp = ((blgo[i] * 3)/ 4); // kompensate 255 : 185 (235-50)
      int end_y = st_y + temp;
      
      if(end_y > VSH) end_y = VSH; 
      
      if(temp > 0) myGLCD.drawLine( st_x, st_y, st_x, end_y );
    }
}

 

void re_map(void)
{
  for( int i = 0; i <  WFALL; i++) {
    imag[i] = 0; 
    }
 
  for( int j = 1; j < MIRROR; j++) { // DC off
    int indx = j / MAPCF;
       if( indx >= WFALL ) break; 
       imag[indx] += magn[j]; 
     }

  for( int i = 0; i <  WFALL; i++) { 
     int temp = imag[i];
       if( temp > 1023 ) temp = 1023;       
       temp = log10_map[temp];

       blgo[i] = (temp >> 2);
       imag[i] = color_map[temp];
     }
}


uint16_t setnColor( int layer ) {
    int  center = 128; // DC offset
    int  width 	= 127; // Amplitude  	    

    float frequency = (float) (0.001 * 2 * M_PI * layer); //0.0014 -bardovui

    float phi090 = (float) (M_PI * 1 /2 ); // 4/3=240
    float phi240 = (float) (M_PI * 4 /3 ); // 4/3=240
    float phi360 = (float) (M_PI * 2 /1 ); // 4/3=240

      int red = (int) (sin(frequency + phi360) * width + center);
      int grn = (int) (sin(frequency + phi090) * width + center);
      int blu = (int) (sin(frequency + phi240) * width + center);
        
    //  uint16_t color = yrk << 24 | red << 16 | grn << 8 | blu;
    uint16_t  color = ((red & 248) << 8 | (grn & 252) << 3 | (blu & 248) >> 3);

  return color;
}

void gen_cmap(void) 
{
  for( int bc = 0; bc < 1024; bc++) { color_map[bc] = setnColor( bc ); }	
}

void gen_lmap(void) 
{
  for( int bc = 0; bc < 1024; bc++) {
     log10_map[bc] = (int)((373.0 * log10(bc + 1.0)) - 100.0);
   }
  log10_map[0] = 0; 	
}


 

void tmr_setup ()
{
  pmc_enable_periph_clk(TC_INTERFACE_ID + 0 *3 + 0); // clock the TC0 channel 0

  TcChannel * t = &(TC0->TC_CHANNEL)[0] ;            // pointer to TC0 registers for its channel 0
  t->TC_CCR = TC_CCR_CLKDIS ;                        // disable internal clocking while setup regs
  t->TC_IDR = 0xFFFFFFFF ;                           // disable interrupts
  t->TC_SR ;                                         // read int status reg to clear pending
  t->TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 |           // use TCLK1 (prescale by 2, = 42MHz)
              TC_CMR_WAVE |                          // waveform mode
              TC_CMR_WAVSEL_UP_RC |                  // count-up PWM using RC as threshold
              TC_CMR_EEVT_XC0 |     // Set external events from XC0 (this setup TIOB as output)
              TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_CLEAR |
              TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_CLEAR ;
  
  t->TC_RC = TMR_CNTR;              // counter resets on RC, so sets period in terms of 42MHz clock
  t->TC_RA = TMR_CNTR /2;           // roughly square wave
  t->TC_CMR = (t->TC_CMR & 0xFFF0FFFF) | TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET ;  // set clear and set from RA and RC compares
  t->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG ;  // re-enable local clocking and switch to hardware trigger source.  
}

void adc_setup ()
{
  pmc_enable_periph_clk(ID_ADC);
  adc_init(ADC, SystemCoreClock, ADC_FREQ_MAX, ADC_STARTUP_FAST);
  NVIC_EnableIRQ (ADC_IRQn);               // enable ADC interrupt vector

  adc_disable_all_channel(ADC);
  adc_enable_interrupt(ADC, ADC_IER_RXBUFF);

  ADC->ADC_RPR  =  (uint32_t)  inp[0];      // DMA buffer
  ADC->ADC_RCR  =  INP_BUFF;
  ADC->ADC_RNPR =  (uint32_t)  inp[1];      // next DMA buffer
  ADC->ADC_RNCR =  INP_BUFF;
  ADC->ADC_PTCR =  1;

  adc_set_bias_current(ADC, 0x01); 
  //  adc_enable_tag(ADC);
  adc_enable_channel(ADC, ADC_CHANNEL_7);  // AN0
  adc_configure_trigger(ADC, ADC_TRIG_TIO_CH_0, 0);
  adc_start(ADC); 
}

void ADC_Handler (void)
{
  if((adc_get_status(ADC) & ADC_ISR_RXBUFF) ==	ADC_ISR_RXBUFF) {
    flag = ++sptr; 
    sptr &=  0x01;
    ADC->ADC_RNPR  =  (uint32_t)  inp[sptr];
    ADC->ADC_RNCR  =  INP_BUFF;
    }
}

And last touch, you need to add this function, inside UTFT or in main sketch:


void UTFT::vScroll( int vsp )
{
cbi(P_CS, B_CS);
LCD_Write_COM( 0x37 );

char BH0 = ( vsp >> 8);
char BL0 = ( vsp & 0xFF);
// LCD_Write_DATA( BH0, BL0);
LCD_Write_DATA( BH0);
LCD_Write_DATA( BL0);
sbi(P_CS, B_CS);
}

 



Piezo Tester -2, Arduino DUE based DDS Synthesizer.

via coolarduino

If you wanted to build  “LCQ Meter / Piezo Tester” project but don’t have AD9850 board,  than here is solution. Arduino DUE has a DAC, and with some magic it may replace DDS synthesizer !. Well, of course not in the RF range, only up to 250 kHz in this implementation.  As a buffer for weak DAC, I decided to use audio power amplifier TDA7052. It may directly drive 4 ohm load, works starting from 3V ( important for 3.3V compatibility ) and has wide linear frequency response, even more than necessary.

Complete circuit DDS filter-buffer board Assembly pin-compatible Main board w/o AD9850 Snap-in replacement Overview 100 pF 0.01H test.

 DDS, how does it work.

 Here is the sketch, to generate < 400 kHz sinewave out of arduino dac:

#define    NWAVE               80

uint16_t  Sinewave[4][2][NWAVE] = {
  {
    { // 0 - 10 kHz
     +4095,   +4093,   +4089,   +4081,   +4070,   +4056,   +4038,   +4018,   +3995,   +3968,   +3939,   +3907,   +3872,   +3834,   +3793,   +3750,
     +3704,   +3656,   +3605,   +3551,   +3495,   +3438,   +3377,   +3315,   +3251,   +3185,   +3118,   +3048,   +2977,   +2905,   +2831,   +2757,
     +2681,   +2604,   +2526,   +2447,   +2368,   +2289,   +2209,   +2128,   +2048,   +1968,   +1887,   +1807,   +1728,   +1649,   +1570,   +1492,
     +1415,   +1339,   +1265,   +1191,   +1119,   +1048,    +978,    +911,    +845,    +781,    +719,    +658,    +601,    +545,    +491,    +440,
      +392,    +346,    +303,    +262,    +224,    +189,    +157,    +128,    +101,     +78,     +58,     +40,     +26,     +15,      +7,      +3,
    },
    {
        +1,      +3,      +7,     +15,     +26,     +40,     +58,     +78,    +101,    +128,    +157,    +189,    +224,    +262,    +303,    +346,
      +392,    +440,    +491,    +545,    +601,    +658,    +719,    +781,    +845,    +911,    +978,   +1048,   +1119,   +1191,   +1265,   +1339,
     +1415,   +1492,   +1570,   +1649,   +1728,   +1807,   +1887,   +1968,   +2048,   +2128,   +2209,   +2289,   +2368,   +2447,   +2526,   +2604,
     +2681,   +2757,   +2831,   +2905,   +2977,   +3048,   +3118,   +3185,   +3251,   +3315,   +3377,   +3438,   +3495,   +3551,   +3605,   +3656,
     +3704,   +3750,   +3793,   +3834,   +3872,   +3907,   +3939,   +3968,   +3995,   +4018,   +4038,   +4056,   +4070,   +4081,   +4089,   +4093
    }
  },
  {
    {// 10 - 100 kHz
     +4095,   +3939,   +3495,   +2831,   +2048,   +1265,    +601,    +157,      +1,    +157,    +601,   +1265,   +2048,   +2831,   +3495,   +3939,
     +4095,   +3939,   +3495,   +2831,   +2048,   +1265,    +601,    +157,      +1,    +157,    +601,   +1265,   +2048,   +2831,   +3495,   +3939,
     +4095,   +3939,   +3495,   +2831,   +2048,   +1265,    +601,    +157,      +1,    +157,    +601,   +1265,   +2048,   +2831,   +3495,   +3939,
     +4095,   +3939,   +3495,   +2831,   +2048,   +1265,    +601,    +157,      +1,    +157,    +601,   +1265,   +2048,   +2831,   +3495,   +3939,
     +4095,   +3939,   +3495,   +2831,   +2048,   +1265,    +601,    +157,      +1,    +157,    +601,   +1265,   +2048,   +2831,   +3495,   +3939
    },
    {
     +4095,   +3939,   +3495,   +2831,   +2048,   +1265,    +601,    +157,      +1,    +157,    +601,   +1265,   +2048,   +2831,   +3495,   +3939,
     +4095,   +3939,   +3495,   +2831,   +2048,   +1265,    +601,    +157,      +1,    +157,    +601,   +1265,   +2048,   +2831,   +3495,   +3939,
     +4095,   +3939,   +3495,   +2831,   +2048,   +1265,    +601,    +157,      +1,    +157,    +601,   +1265,   +2048,   +2831,   +3495,   +3939,
     +4095,   +3939,   +3495,   +2831,   +2048,   +1265,    +601,    +157,      +1,    +157,    +601,   +1265,   +2048,   +2831,   +3495,   +3939,
     +4095,   +3939,   +3495,   +2831,   +2048,   +1265,    +601,    +157,      +1,    +157,    +601,   +1265,   +2048,   +2831,   +3495,   +3939
    }
  },
  {
    {// 100 - 200 kHz
     +4095,   +3495,   +2048,    +601,      +1,    +601,   +2048,   +3495,   +4095,   +3495,   +2048,    +601,      +1,    +601,   +2048,   +3495,
     +4095,   +3495,   +2048,    +601,      +1,    +601,   +2048,   +3495,   +4095,   +3495,   +2048,    +601,      +1,    +601,   +2048,   +3495,
     +4095,   +3495,   +2048,    +601,      +1,    +601,   +2048,   +3495,   +4095,   +3495,   +2048,    +601,      +1,    +601,   +2048,   +3495,
     +4095,   +3495,   +2048,    +601,      +1,    +601,   +2048,   +3495,   +4095,   +3495,   +2048,    +601,      +1,    +601,   +2048,   +3495,
     +4095,   +3495,   +2048,    +601,      +1,    +601,   +2048,   +3495,   +4095,   +3495,   +2048,    +601,      +1,    +601,   +2048,   +3495
    },
    {
     +4095,   +3495,   +2048,    +601,      +1,    +601,   +2048,   +3495,   +4095,   +3495,   +2048,    +601,      +1,    +601,   +2048,   +3495,
     +4095,   +3495,   +2048,    +601,      +1,    +601,   +2048,   +3495,   +4095,   +3495,   +2048,    +601,      +1,    +601,   +2048,   +3495,
     +4095,   +3495,   +2048,    +601,      +1,    +601,   +2048,   +3495,   +4095,   +3495,   +2048,    +601,      +1,    +601,   +2048,   +3495,
     +4095,   +3495,   +2048,    +601,      +1,    +601,   +2048,   +3495,   +4095,   +3495,   +2048,    +601,      +1,    +601,   +2048,   +3495,
     +4095,   +3495,   +2048,    +601,      +1,    +601,   +2048,   +3495,   +4095,   +3495,   +2048,    +601,      +1,    +601,   +2048,   +3495
    }
  },
  {
    {// 200 - 400 kHz
     +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,
     +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,
     +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,
     +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,
     +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048
    },
    {
     +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,
     +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,
     +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,
     +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,
     +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048,   +4095,   +2048,      +1,   +2048
    }
  }
};

            int       fast_mode  =    0;
            int       freq_inhz  = 1000; // Default Hz
            int       freq_intc  =    0;
            int       user_intf  =    0;

volatile   uint16_t   sptr       =    0;

void setup()
{
  Serial.begin (115200);
  dac_setup();        
  freq_intc = freqToTc(freq_inhz);
  TC_setup();        
  setup_pio_TIOA0(); 
}

int tcToFreq( int tc_cntr)
{
  int freq_hz;     

  if( tc_cntr == 0 ) return 1000;

  if( fast_mode ) freq_hz = (420000000UL / tc_cntr) / (2 * NWAVE);
  else            freq_hz = ( 42000000UL / tc_cntr) / (2 * NWAVE);

  if( fast_mode == 2 ) freq_hz *= 2;
  if( fast_mode == 3 ) freq_hz *= 4;

  return freq_hz;
}

int freqToTc( int freq_hz)
{
  int tc_cntr = 0;

  if( freq_hz == 0 ) return 25;

  if( fast_mode == 0 )  tc_cntr = (  42000000UL / freq_hz) / (2 * NWAVE);
  if( fast_mode == 1 )  tc_cntr = ( 420000000UL / freq_hz) / (2 * NWAVE);
  if( fast_mode == 2 )  tc_cntr = ( 840000000UL / freq_hz) / (2 * NWAVE);
  if( fast_mode == 3 )  tc_cntr = (1680000000UL / freq_hz) / (2 * NWAVE); 

  return tc_cntr;
}

void loop()
{
  char in_Byte;
  int     temp;
          
  if (Serial.available() > 0) {
    in_Byte = Serial.read();
    // Message Format To Set  Frequency:  1000f + "Enter".
    if((in_Byte >= '0') && (in_Byte <= '9'))
    {
      user_intf = (user_intf * 10) + (in_Byte - '0');
    }
    else
    { 
      if (in_Byte == 'f') // end delimiter
      {
        if ((user_intf > 20) && (user_intf < 400000))
        {
          freq_inhz = user_intf;
            fast_mode = 0;
            if( freq_inhz >  10000 ) fast_mode = 1;
            if( freq_inhz > 100000 ) fast_mode = 2;
            if( freq_inhz > 200000 ) fast_mode = 3;
          
          freq_intc = freqToTc(freq_inhz);
          TC_setup();        
          Serial.print("Fast Mode = ");
          Serial.println(fast_mode, DEC);
          Serial.print("freq_inhz = ");
          Serial.println(freq_inhz, DEC);
          Serial.print("freq_intc = ");
          Serial.println(freq_intc, DEC);
          Serial.print("approximation = ");
          temp = tcToFreq(freq_intc);
          Serial.println(temp, DEC);
        }
      user_intf = 0; // reset to 0 ready for the next sequence of digits
      }    
    }
  }
}

void DACC_Handler(void)
{
  if((dacc_get_interrupt_status(DACC) & DACC_ISR_ENDTX) == DACC_ISR_ENDTX) {
    ++sptr; 
    sptr &=  0x01;
      DACC->DACC_TNPR =  (uint32_t)  Sinewave[fast_mode][sptr];      // next DMA buffer
      DACC->DACC_TNCR =  NWAVE;
  }
}

void setup_pio_TIOA0()
{
  PIOB->PIO_PDR = PIO_PB25B_TIOA0;  
  PIOB->PIO_IDR = PIO_PB25B_TIOA0;  
  PIOB->PIO_ABSR |= PIO_PB25B_TIOA0;
}

void TC_setup ()
{
  pmc_enable_periph_clk(TC_INTERFACE_ID + 0 *3 + 0); 

  TcChannel * t = &(TC0->TC_CHANNEL)[0];            
  t->TC_CCR = TC_CCR_CLKDIS;                        
  t->TC_IDR = 0xFFFFFFFF;                           
  t->TC_SR;                                         
  t->TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 |          
              TC_CMR_WAVE |                         
              TC_CMR_WAVSEL_UP_RC |                 
              TC_CMR_EEVT_XC0 |     
              TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_CLEAR |
              TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_CLEAR;
  
  t->TC_RC = freq_intc;
  t->TC_RA = freq_intc /2;       
  t->TC_CMR = (t->TC_CMR & 0xFFF0FFFF) | TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET; 
  t->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG;    
}

void dac_setup ()
{
  pmc_enable_periph_clk (DACC_INTERFACE_ID) ; // start clocking DAC
  dacc_reset(DACC);
  dacc_set_transfer_mode(DACC, 0);
  dacc_set_power_save(DACC, 0, 1);            // sleep = 0, fastwkup = 1
  dacc_set_analog_control(DACC, DACC_ACR_IBCTLCH0(0x02) | DACC_ACR_IBCTLCH1(0x02) | DACC_ACR_IBCTLDACCORE(0x01));
  dacc_set_trigger(DACC, 1);
  
//  dacc_set_channel_selection(DACC, 1);
  //dacc_enable_channel(DACC, 1);
  dacc_set_channel_selection(DACC, 0);
  dacc_enable_channel(DACC, 0);

  NVIC_DisableIRQ(DACC_IRQn);
  NVIC_ClearPendingIRQ(DACC_IRQn);
  NVIC_EnableIRQ(DACC_IRQn);
  dacc_enable_interrupt(DACC, DACC_IER_ENDTX);

  DACC->DACC_TPR  =  (uint32_t)  Sinewave[fast_mode][0];      // DMA buffer
  DACC->DACC_TCR  =  NWAVE;
  DACC->DACC_TNPR =  (uint32_t)  Sinewave[fast_mode][1];      // next DMA buffer
  DACC->DACC_TNCR =  NWAVE;
  DACC->DACC_PTCR =  0x00000100;  //TXTEN - 8, RXTEN - 1.
}

The code runs continuously, but can’t generate exact frequency, there is always
some offset, that is increasing for higher frequencies.
In piezo-tester code I change algorithm, now generator outputs “bursts” of
frequencies, with high precision, phase correct and “clipped” at both ends.

Main sketch:

#include <UTFT.h>
#include <UTouch.h>
#include <SplitRadixReal.h>

#define  SMP_RATE         480000UL 
#define  CLK_MAIN       84000000UL
#define  TMR_CNTR       CLK_MAIN / (2 *SMP_RATE)

// FFT_SIZE IS DEFINED in Header file SplitRadixReal.h 
// #define  FFT_SIZE           2048

#define  MIRROR         FFT_SIZE / 2
#define  INP_BUFF       FFT_SIZE * 2

volatile int16_t        flag              =   0 ;
         uint16_t       adcIn[INP_BUFF]   = { 0};     
          int16_t       dacOut[FFT_SIZE]  = { 0};     

         int            fr[2][FFT_SIZE]   = { 0};

         int            temp[MIRROR]      = { 0};     // Magnitudes
         int            magn[MIRROR]      = { 0};     // Magnitudes
         int            phas[MIRROR]      = { 0};     // Phases
         float          resonance         =  1.0;     // Resonance based on phi = 0.

const    int            dc_offset         = 2047;
         int            debug_osm         =    0;     // degug over serial monitor 

const    float          fix_hardw         =  0.5;     // TDA7052 gain correction coefficient.
               
         SplitRadixReal radix;
         UTFT           myGLCD( ILI9486, 38, 39, 40, 41);
         UTouch         myTouch( 6, 5, 4, 3, 2);

extern   uint8_t        BigFont[];

         int            x, y, dispx, dispy, sizeX, sizeY;
         
         int            xyButtons[5][4]   = {{ 0}};
const    int            widthBtns         =     80;
const    int            spaceBtns         =      8;
         int            in_number         =      0;

         int32_t        startScan         =     10;
         int32_t        stop_Scan         =    100;

         int            activ_set         =      0;
         int            adjus_set         =      1;
 
void setup() {
  Serial.begin(115200);
// 
  adc_setup();         
  dac_setup();        
  tmr_setup();         
//
  myGLCD.InitLCD();
  myGLCD.clrScr();
  myGLCD.setFont(BigFont);
  myGLCD.setBackColor(0, 0, 255);

  myTouch.InitTouch();
  myTouch.setPrecision(PREC_MEDIUM);

  dispx = myGLCD.getDisplayXSize();
  dispy = myGLCD.getDisplayYSize();
  sizeX = dispx - widthBtns - (2 * spaceBtns); // ~400 pix.
  sizeY = dispy - 1;

  initButtons();  

  for( int i = 0; i < MIRROR; i++) { // DEMO
    int k = 10;
    magn[i]  =  (int) round(127 * cos ( k * 2 * 3.14 * i/ INP_BUFF));
    magn[i] += 127;   
    phas[i]  =  (int) round(127 * sin ( k * 2 * 3.14 * i/ INP_BUFF));
    }  
  Refresh();
//  sendFrequency(startScan * 1e3);
  sendFrequency(startScan * 1000.0);
}

void loop() {

   if (myTouch.dataAvailable()) {
      myTouch.read();
      x = myTouch.getX();
      y = myTouch.getY();
 
      int Btn =  whatButtons( x, y);
      int holdb = 0;
      
      if(Btn >= 0) {
        holdb = presButtons( Btn);
      }
      if( Btn == 0 ) {
        if( activ_set == 0 ) {
          startScan += adjus_set;
          int32_t limit = stop_Scan - 1;
          if( startScan > limit ) startScan = limit;
          }
        if( activ_set == 1 ) {
          stop_Scan += adjus_set;
          if( stop_Scan >  1000 ) stop_Scan =  1000;
          }
        }

      if( Btn == 1 ) {
        if( activ_set == 0 ) {
          startScan -= adjus_set;
          if( startScan <     1 ) startScan =     1;
          }
        if( activ_set == 1 ) {
          stop_Scan -= adjus_set;
          int32_t limit = startScan + 1;
          if( stop_Scan < limit ) stop_Scan = limit;
          }
        }

      if( Btn == 2 ) {
          scan_fft();
        }
      
      if(((Btn == 3) || (Btn == 4)) && (holdb == 0)) {
        if( ++activ_set > 1 ) activ_set = 0;
        }

      draw_Infor();   

      if(debug_osm) {
        Serial.print("n");
        Serial.print("tX = ");
        Serial.print( x, DEC);
        Serial.print("tY = ");
        Serial.print( y, DEC);
        Serial.print("tBtn = ");
        Serial.print( Btn, DEC);
        }   
      }

  while (Serial.available()) {
    uint8_t input = Serial.read();
      switch (input) {
        case 'r':
            break;
        case 'n':
            break;
        case 'x':
            debug_osm = 1 - debug_osm;
            if (debug_osm) Serial.println(F("ntDebug activated"));
            else           Serial.println(F("ntDebug de-activ."));
            break;
        case 'q':                            // Print dacOut. 0
            prnt_out2(dacOut, FFT_SIZE);
            break;
        case 'p':                            // Print Ch. 0
            prnt_out1(adcIn, MIRROR, 0);
            break;
        case 'o':                            // Print Ch. 1
            prnt_out1(adcIn, MIRROR, 1);
            break;
        case 'd':                            // Profiling-Scanner
          for (int i = 0; i < 5; i++){
            Serial.print("n");
            for (int j = 0; j < 4; j++){
              Serial.print("t");
              Serial.print(xyButtons[i][j], DEC);
              }
            }
          break;
        case '?':
        case 'h':
  //        cmd_print_help();
          break;
          default: // -------------------------------
          if ((input >= '0') && (input <= '9')) {
            in_number = (in_number * 10) + (input - '0');
            }
          else {
              if (input == '.') {
//                sendFrequency(in_number * 1e3);
                sendFrequency(in_number * 1000.0);
                  Serial.print("ntFreq. set to: ");
                  Serial.print(in_number, DEC);
                  Serial.print(" kHz.");
                in_number = 0;
                restart_sampl();
                }
               else {
                  Serial.print("Unexpected: ");
                  Serial.println((char)input);
                  } // else, esli ne ','
               } // else, esli ne cifra
          }
     }
}

void prnt_out2( int16_t *array, int dlina)
{
  Serial.print("nt");
  for ( uint32_t i = 0; i < dlina; i++) {
    Serial.print(array[i], DEC);
    Serial.print("t");
    if ((i+1) % 32 == 0) Serial.print("nt");
    }
}

void prnt_out1( uint16_t *array, int dlina, int channel)
{
  if ((channel < 0) || (channel > 1)) channel = 0;
  Serial.print("nt");
  for ( uint32_t i = channel; i < dlina; i += 2) {
    Serial.print(array[i] - dc_offset);
    Serial.print("t");
    if ((i + 2 - channel) % 64 == 0) Serial.print("nt");
    }
}

DDS:

void sendFrequency(float frequency) {

  const float  smp_real = CLK_MAIN / (2 * ((int)TMR_CNTR));
  const float  smp_koef = ((float) FFT_SIZE) / smp_real;

  float freq2 = frequency * smp_koef;

  for( int i = 0; i < MIRROR; i++) {
    int temp = (int) round( 2047 *sin( i *freq2 *2 *3.1415926535 /FFT_SIZE));
        // PHASE CORRECT SINEWAVE !!!
        uint16_t indx_a = MIRROR + i;
        uint16_t indx_b = MIRROR - i;
        dacOut[indx_a] =  temp;
        dacOut[indx_b] = -temp;
      }

   for ( uint16_t i = 0, k = (NWAVE / FFT_SIZE); i < FFT_SIZE; i++ ) {
     uint16_t windw = Hamming[i * k];
     int temp = dacOut[i];
         temp = mult_shft12(temp, windw);
         temp *= fix_hardw;
         dacOut[i] = temp + 2048; 
   } 
}

void dac_setup ()
{
  pmc_enable_periph_clk (DACC_INTERFACE_ID) ; // start clocking DAC
  dacc_reset(DACC);
  dacc_set_transfer_mode(DACC, 0);
  dacc_set_power_save(DACC, 0, 1);            // sleep = 0, fastwkup = 1
  dacc_set_analog_control(DACC, DACC_ACR_IBCTLCH0(0x02) | DACC_ACR_IBCTLCH1(0x02) | DACC_ACR_IBCTLDACCORE(0x01));

  DACC->DACC_MR = (DACC->DACC_MR & 0xFFFFFFFE);    //DISABLE
  dacc_set_trigger(DACC, 1); // TIMER = 1 (0,1,2).
  //  dacc_set_channel_selection(DACC, 1);
  //  dacc_enable_channel(DACC, 1);
  dacc_set_channel_selection(DACC, 0);
  dacc_enable_channel(DACC, 0);
  NVIC_DisableIRQ(DACC_IRQn);
  NVIC_ClearPendingIRQ(DACC_IRQn);
//  NVIC_EnableIRQ(DACC_IRQn);
//  dacc_enable_interrupt(DACC, DACC_IER_ENDTX);

  DACC->DACC_TPR  =  (uint32_t)  dacOut;      // DMA buffer
  DACC->DACC_TCR  =  FFT_SIZE;
//  DACC->DACC_TNPR =  (uint32_t)  Sinewave[fast_mode][1];      // next DMA buffer
//  DACC->DACC_TNCR =  NWAVE;
  DACC->DACC_PTCR =  0x00000100;  //TXTEN - 8, RXTEN - 1.
}

Display:

void   Refresh()
{
  myGLCD.clrScr();
  drawButtons();
  draw_Table();
  draw_Chart();   
  draw_Infor();   
}

void   draw_Infor()
{
    myGLCD.setColor( 0, 255, 255);
    myGLCD.print("Resonance:", 15, 288);
    myGLCD.printNumF( resonance, 3, 215, 288);
    myGLCD.print("kHz.", 330, 288);  

    myGLCD.setColor( 255, 255, 255);
    myGLCD.fillRect( 15, 12, 384, 34);

    myGLCD.setColor( 255,   0, 255);
    myGLCD.print("Start:",  15, 15);
    myGLCD.print("Stop :", 215, 15);  

    if( activ_set == 0 ) myGLCD.setBackColor( 150, 150, 150);
    else                 myGLCD.setBackColor( 255, 255, 255);
    myGLCD.printNumI( startScan, 120, 15);    

    if( activ_set == 1 ) myGLCD.setBackColor( 150, 150, 150);
    else                 myGLCD.setBackColor( 255, 255, 255);
    myGLCD.printNumI( stop_Scan, 320, 15);    

    myGLCD.setBackColor( 255, 255, 255);
    myGLCD.setColor(  0, 255, 255);
    
    if( activ_set == 0 ) {
      if(adjus_set ==  1) myGLCD.drawRect( 168, 12, 184, 34 );
      if(adjus_set == 10) myGLCD.drawRect( 152, 12, 168, 34 );
      if(adjus_set ==100) myGLCD.drawRect( 136, 12, 152, 34 );
      }
    if( activ_set == 1 ) {
      if(adjus_set ==  1) myGLCD.drawRect( 368, 12, 384, 34 );
      if(adjus_set == 10) myGLCD.drawRect( 352, 12, 368, 34 );
      if(adjus_set ==100) myGLCD.drawRect( 336, 12, 352, 34 );
      }
}  

void   draw_Chart()
{
  int offsetY1 = 36 + 15; //sizeY -15;
  int offsetY2 = sizeY / 2;
  
  myGLCD.setColor(  0,255,255);
  for (int i = 1, p = 0; i < sizeX; i++, p++)
  {
    myGLCD.drawLine( p, offsetY1 + magn[p], i, offsetY1 + magn[i]);
  }
  myGLCD.setColor(255,  0,255);
  for (int i = 1, p = 0; i < sizeX; i++, p++)
  {
    myGLCD.drawLine( p, offsetY2 + phas[p], i, offsetY2 + phas[i]);
  }
}

void  draw_Table()
{
    myGLCD.setColor( 150, 150, 150);
   
    for ( int y = 10; y < sizeY; y += 36) {
      for ( int x = 10; x < (sizeX -12); x += 6) {
        myGLCD.drawPixel( x, y);
      }
    }
    for ( int x = 10; x < sizeX; x += 36) {
      for (int y = 10; y < (sizeY - 6); y +=  6) {
        myGLCD.drawPixel( x, y);
      }
    }
}

void initButtons()
{
  int cl, cr, cd, cu;

      cl = dispx - 1 - widthBtns;
      cr = dispx - 1 - spaceBtns;
  
  int heigE = (dispy - spaceBtns) /5;
  int heigB =  heigE - spaceBtns;
  
  for (int i = 0; i < 5; i++)
  {   
    cu = ( i * heigE) + spaceBtns;
    cd = cu + heigB;
    
    xyButtons[i][0] = cl;
    xyButtons[i][1] = cr;
    xyButtons[i][2] = cu;
    xyButtons[i][3] = cd;
  }
}

void drawButtons()
{
    int crx, cry;

        crx = dispx - 1;
        cry = dispy - 1;
    
        myGLCD.setColor(255, 255, 255);
        myGLCD.fillRect( 0, 0, crx, cry);
  
  for ( int i = 0; i < 5; i++) {
    myGLCD.setColor(0, 0, 255);
    myGLCD.fillRoundRect(xyButtons[i][0], xyButtons[i][2], xyButtons[i][1], xyButtons[i][3]);
    myGLCD.setColor(255, 255, 255);
    myGLCD.drawRoundRect(xyButtons[i][0], xyButtons[i][2], xyButtons[i][1], xyButtons[i][3]);
    }

    myGLCD.setColor( 0, 255, 0);
    myGLCD.setBackColor( 0,  0, 255);
    myGLCD.print("UP", xyButtons[0][0] +22, xyButtons[0][2] +22);
    myGLCD.print("DN", xyButtons[1][0] +22, xyButtons[1][2] +22);
    myGLCD.print("OK", xyButtons[2][0] +22, xyButtons[2][2] +22);
    myGLCD.print("LF", xyButtons[3][0] +22, xyButtons[3][2] +22);
    myGLCD.print("RH", xyButtons[4][0] +22, xyButtons[4][2] +22);
}

int presButtons(int n)
{
  int hold = 0;
  myGLCD.setColor(255, 0, 0);
  myGLCD.drawRoundRect(xyButtons[n][0], xyButtons[n][2], xyButtons[n][1], xyButtons[n][3]);

  long stampA = millis();
  while (myTouch.dataAvailable())
    myTouch.read();
  long stampB = millis();
  int time = stampB - stampA;
    if((time > 500) && (n == 3)) {
      adjus_set *= 10;
      if( adjus_set > 100 ) adjus_set = 100;
      hold = 1;
    }
    if((time > 500) && (n == 4)) {
      adjus_set /= 10;
      if( adjus_set < 1 ) adjus_set = 1;
      hold = 1;
    }
  myGLCD.setColor(255, 255, 255);
  myGLCD.drawRoundRect(xyButtons[n][0], xyButtons[n][2], xyButtons[n][1], xyButtons[n][3]);

  return hold;
}

int whatButtons( int cx, int cy)
{
  int Btn = -1;

  for (int i = 0; i < 5; i++)
  {   
    if((cx > xyButtons[i][0]) && 
       (cx < xyButtons[i][1]) && 
       (cy > xyButtons[i][2]) && 
       (cy < xyButtons[i][3]))
       Btn = i; 
  }
  return Btn;
}

Scan_FFT:

void scan_fft()
{
  const  float  phas_corr = -0.850059032;
  double resol  = ((double)(stop_Scan - startScan)) / sizeX;
  double setfr  = startScan * 1000UL;
         resol *= 1000; // in kHz

  int    minim  =  900;  

  for (int i = 0; i < sizeX; i++) {
    setfr += resol;
    sendFrequency(setfr);
    //    delay(2);
    restart_sampl();

    int   prom_magn[2] = {   0};
    float prom_phas[2] = { 0.0};
    int   peak = 1;

    if(debug_osm) {
      Serial.print("ntFr: ");
      Serial.print( setfr, 1);
      }
    
    for( uint32_t y = 0; y < 2; y++) {
         radix.rev_bin( fr[y], FFT_SIZE);
         radix.fft_split_radix_real( fr[y], LOG2_FFT);
         //    radix.gain_Reset( fr[y], LOG2_FFT -1);
         radix.gain_Reset( fr[y], 5);
         } 

         radix.get_Magnit1( fr[0], temp);
         peak = peak_search( temp, MIRROR);
         prom_magn[0] = temp[peak];
         prom_phas[0] = get_Phase( fr[0], peak);         

         radix.get_Magnit1( fr[1], temp);
         prom_magn[1] = temp[peak];
         prom_phas[1] = get_Phase( fr[1], peak);         

    magn[i] = (prom_magn[1] - prom_magn[0]) / 4; // ~1200 amplit to 300 - screen size
    magn[i] /= 32;  // Compenstaion for less gain_reset
    magn[i] *= -1;  // Inverse for display, Attenuation.
    
    prom_phas[1] += (peak * phas_corr);    
    int tmpr = prom_phas[1] - prom_phas[0];
    if( tmpr >  1800 ) tmpr -= 3600;
    if( tmpr < -1800 ) tmpr += 3600; 

        if(debug_osm) {
           Serial.print("tCh0: ");
           Serial.print("tPk: ");
           Serial.print( peak, DEC);
           Serial.print("tM: ");
           Serial.print( prom_magn[0], DEC);
           Serial.print("tP: ");
           Serial.print( prom_phas[0], 3);
           Serial.print("tCh1: ");
           Serial.print("tM: ");
           Serial.print( prom_magn[1], DEC);
           Serial.print("tP: ");
           Serial.print( prom_phas[1], 3);
           delay(20);
           Serial.print("tD: ");
           Serial.print( tmpr, DEC);
          }
    int tmp2 = abs(tmpr);
    if(abs(tmp2) < minim) {
      resonance =  setfr / 1000;             // in kHz
      minim = tmp2;
      }
    tmpr /= 6;                               // ~1800 phase to 300 - screen size
    phas[i] = tmpr;
  }    
  Refresh();
}

inline int mult_shft12( int a, int b)
{
  return (( a  *  b )  >> 12);
}

void restart_sampl( void )
{
   flag = 1;
   adc_configure_trigger(ADC, ADC_TRIG_TIO_CH_0, 0);

   dacc_set_trigger(DACC, 1); // TIMER = 1 (0,1,2).
   DACC->DACC_TPR  =  (uint32_t)  dacOut;      // DMA buffer
   DACC->DACC_TCR  =  FFT_SIZE;
   DACC->DACC_PTCR =  0x00000100;  // Must BE
    
   while ( flag ) {} // wait new pull

   uint16_t indx_b = 0;
   for( uint16_t i = 0; i < FFT_SIZE; i++ ) {
       fr[0][i] = adcIn[indx_b++] - dc_offset;
       fr[1][i] = adcIn[indx_b++] - dc_offset;
     }
}

void tmr_setup ()
{
  pmc_enable_periph_clk(TC_INTERFACE_ID + 0 *3 + 0); // clock the TC0 channel 0

  TcChannel * t = &(TC0->TC_CHANNEL)[0] ;            // pointer to TC0 registers for its channel 0
  t->TC_CCR = TC_CCR_CLKDIS ;                        // disable internal clocking while setup regs
  t->TC_IDR = 0xFFFFFFFF ;                           // disable interrupts
  t->TC_SR ;                                         // read int status reg to clear pending
  t->TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 |           // use TCLK1 (prescale by 2, = 42MHz)
              TC_CMR_WAVE |                          // waveform mode
              TC_CMR_WAVSEL_UP_RC |                  // count-up PWM using RC as threshold
              TC_CMR_EEVT_XC0 |     // Set external events from XC0 (this setup TIOB as output)
              TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_CLEAR |
              TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_CLEAR ;
  
  t->TC_RC = TMR_CNTR;              // counter resets on RC, so sets period in terms of 42MHz clock
  t->TC_RA = TMR_CNTR /2;           // roughly square wave
  t->TC_CMR = (t->TC_CMR & 0xFFF0FFFF) | TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET ;  // set clear and set from RA and RC compares
  t->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG ;  // re-enable local clocking and switch to hardware trigger source.
}

void adc_setup ()
{
  pmc_enable_periph_clk(ID_ADC);
  //  adc_init(ADC, SystemCoreClock, ADC_FREQ_MAX, ADC_STARTUP_FAST);
  adc_init(ADC, SystemCoreClock, 21000000UL, ADC_STARTUP_FAST);
  NVIC_EnableIRQ (ADC_IRQn);                   // enable ADC interrupt vector
  adc_disable_all_channel(ADC);
  adc_enable_interrupt(ADC, ADC_IER_RXBUFF);
//  ADC->ADC_RPR  =  (uint32_t)  inp;            // DMA buffer
  //ADC->ADC_RCR  =  INP_BUFF;
  ADC->ADC_RPR  =  (uint32_t)  adcIn;      // DMA buffer
  ADC->ADC_RCR  =  INP_BUFF;

  ADC->ADC_PTCR =  1;
  adc_set_bias_current(ADC, 0x01);
  //  adc_enable_tag(ADC);
  adc_enable_channel(ADC, ADC_CHANNEL_7);        // AN0
  adc_enable_channel(ADC, ADC_CHANNEL_6);        // AN1
//  adc_enable_channel(ADC, ADC_CHANNEL_5);      // AN2
  //adc_enable_channel(ADC, ADC_CHANNEL_4);      // AN3
  ADC->ADC_MR = (ADC->ADC_MR & 0xFFFFFFFE);      //DISABLE
  adc_configure_trigger(ADC, ADC_TRIG_TIO_CH_0, 0);
  //  adc_start(ADC);
}

void ADC_Handler (void)
{
  if ((adc_get_status(ADC) & ADC_ISR_RXBUFF) ==	ADC_ISR_RXBUFF) {
    ADC->ADC_MR = (ADC->ADC_MR & 0xFFFFFFFE);    // TRGEN - stop trigger
    ADC->ADC_RPR  =  (uint32_t)  adcIn;          // DMA buffer
    ADC->ADC_RCR  =  INP_BUFF;

    flag = 0;

    DACC->DACC_MR   = (DACC->DACC_MR & 0xFFFFFFFE);  // DISABLE
    }
}

Supplementary:

int peak_search( int *inTemp, int16_t inSize)
{
  int maxim = 0;
  int index = 1;
  
  for( uint16_t i = 1; i < inSize; i++ ) { // DC-off
    int temp = inTemp[i];
    if( temp > maxim ) {
      maxim = temp;
      index = i;
    }
  }
  return index;
}

float get_Phase(int *fr, int n_bin)
{ // Shkala x10, t.e. +-180 = +- 1800, resolution 0.1 degree
        int real = fr[n_bin];
        int imag = fr[FFT_SIZE -n_bin];
        int Phase = 10  * RAD_CONV * atan2((float) imag, (float) real);
        return Phase;
} 

Finished…


LCQ Meter / Amplitude-Phase Characterizer / Piezo tester.

via coolarduino

After completing “Ultrasonic Radar” project, I was wander how common ultrasonic sensor reacts to “out of band” signals, that are not equal to default 40 kHz. Thinking for awhile, I came up to idea create a project, which could measure a frequency response of the sensor in wide frequency range, and plot two charts, amplitude and phase. In short – “Piezo tester”. Of course, device could measure amplitude – phase characteristic of anything, in particular, audio filters, speakers, RF circuits etc. And connecting inductor or capacitor with known “calibrated” values in series with DUT, you can easily calculate LCQ parameters. Sure, mr. Arduino can to do it for you,  so this part of the code isn’t written yet.

piezo Tester Project 30-60 kHz band Normal, 50-60 kHz band DDS & Buffer modules Abnormal, 50-60 kHz band piezoSensor

 Discoveries I’ve made so far:

1. Regular piezo sensor (extracted from cheap ultrasonic range meter) has  one more resonance about 54-58 kHz, it’s x1.3 times higher than “default” 38-42 kHz commonly used frequency. Test was run from 30 to 70 kHz.

2. One out of 4 sensors I tested, has “abnormal” two-peaks shape resonance at that frequency. Look at the pictures.

 Part list, main components:

  1. Arduino DUE
  2. AD9850 DDS module
  3. 3.5′  TFT shield
  4. MCP6022
  5. Resistors, capacitors according to drawings.

Piezo_tester

I don’t think schematic requires any comments, OPA is just a buffer, both outputs preset to 1.65V and about x3 gain. MCP6022 has 10 MHz cut-off, and rail-to-rail capability, this has to be taken into account in case of replacement.

Software.

 New FFT library  calculates data,  and keeps track on all data flow starting from ADC.  UTFT and UTouch libraries serve as hardware interface to the TFT display module, plus HMI for operator / user.  I also borrow small piece of code to communicate with AD9850.  Here is main sketch:

#include <UTFT.h>
#include <UTouch.h>
#include <SplitRadixReal.h>

#define  W_CLK  8       // Pin  8 - connect to AD9850 module word load clock pin (CLK)
#define  FQ_UD  9       // Pin  9 - connect to freq update pin (FQ)
#define  DATAS 10       // Pin 10 - connect to serial DATAS load pin (DATAS)
#define  RESET 11       // Pin 11 - connect to reset pin (RST).

#define  pulseHigh(pin) {digitalWrite(pin, HIGH); digitalWrite(pin, LOW); }

#define  SMP_RATE         480000UL 
#define  CLK_MAIN       84000000UL
#define  TMR_CNTR       CLK_MAIN / (2 *SMP_RATE)

// FFT_SIZE IS DEFINED in Header file SplitRadixReal.h 
// #define  FFT_SIZE           2048

#define  MIRROR         FFT_SIZE / 2
#define  INP_BUFF       FFT_SIZE * 2

volatile int16_t        flag              =   0 ;
         uint16_t       adcIn[INP_BUFF]   = { 0};     

         int            fr[2][FFT_SIZE]   = { 0};

         int            temp[MIRROR]      = { 0};     // Magnitudes
         int            magn[MIRROR]      = { 0};     // Magnitudes
         int            phas[MIRROR]      = { 0};     // Phases
         float          resonance         =  1.0;     // Resonance based on phi = 0.

const    int            dc_offset         = 2047; 

         int            debug_osm         =    0;     // degug over serial monitor
               
         SplitRadixReal radix;

UTFT     myGLCD( ILI9486, 38, 39, 40, 41);
UTouch   myTouch( 6, 5, 4, 3, 2);

extern   uint8_t  BigFont[];

         int      x, y, dispx, dispy, sizeX, sizeY;
         
         int            xyButtons[5][4]   = {{ 0}};
const    int            widthBtns         =     80;
const    int            spaceBtns         =      8;
         int            in_number         =      0;

         int32_t        startScan         =     10;
         int32_t        stop_Scan         =    100;

         int            activ_set         =      0;
         int            adjus_set         =      1;
 
void setup() {
  Serial.begin(115200);
// 
  adc_setup ();         
  tmr_setup ();         
//  pinMode( 2, INPUT); 
//
  pinMode(FQ_UD, OUTPUT);
  pinMode(W_CLK, OUTPUT);
  pinMode(DATAS, OUTPUT);
  pinMode(RESET, OUTPUT);
   
  pulseHigh(RESET);
  pulseHigh(W_CLK);
  pulseHigh(FQ_UD);  // this pulse enables serial mode - DATASsheet page 12 figure 10

  sendFrequency(startScan * 1e3);
//  
  myGLCD.InitLCD();
  myGLCD.clrScr();
  myGLCD.setFont(BigFont);
  myGLCD.setBackColor(0, 0, 255);

  myTouch.InitTouch();
  myTouch.setPrecision(PREC_MEDIUM);

  dispx = myGLCD.getDisplayXSize();
  dispy = myGLCD.getDisplayYSize();
  sizeX = dispx - widthBtns - (2 * spaceBtns); // ~400 pix.
  sizeY = dispy - 1;

  initButtons();  

  for( int i = 0; i < MIRROR; i++) { // DEMO
    int k = 10;
    magn[i]  =  (int) round(127 * cos ( k * 2 * 3.14 * i/ INP_BUFF));
    magn[i] += 127;   
    phas[i]  =  (int) round(127 * sin ( k * 2 * 3.14 * i/ INP_BUFF));
    }  
  Refresh();
}

void loop() {

   if (myTouch.dataAvailable()) {
      myTouch.read();
      x = myTouch.getX();
      y = myTouch.getY();
 
      int Btn =  whatButtons( x, y);
      int holdb = 0;
      
      if(Btn >= 0) {
        holdb = presButtons( Btn);
      }
      if( Btn == 0 ) {
        if( activ_set == 0 ) {
          startScan += adjus_set;
          int32_t limit = stop_Scan - 1;
          if( startScan > limit ) startScan = limit;
          }
        if( activ_set == 1 ) {
          stop_Scan += adjus_set;
          if( stop_Scan >  1000 ) stop_Scan =  1000;
          }
        }

      if( Btn == 1 ) {
        if( activ_set == 0 ) {
          startScan -= adjus_set;
          if( startScan <     1 ) startScan =     1;
          }
        if( activ_set == 1 ) {
          stop_Scan -= adjus_set;
          int32_t limit = startScan + 1;
          if( stop_Scan < limit ) stop_Scan = limit;
          }
        }

      if( Btn == 2 ) {
          scan_fft();
        }
      
      if(((Btn == 3) || (Btn == 4)) && (holdb == 0)) {
        if( ++activ_set > 1 ) activ_set = 0;
        }
      //  Refresh();  - vspuxivaet ekran
      draw_Infor();   

      if(debug_osm) {
        Serial.print("n");
        Serial.print("tX = ");
        Serial.print( x, DEC);
        Serial.print("tY = ");
        Serial.print( y, DEC);
        Serial.print("tBtn = ");
        Serial.print( Btn, DEC);
        }   
      }

  while (Serial.available()) {
    uint8_t input = Serial.read();
      switch (input) {
        case 'r':
            break;
        case 'n':
            break;
        case 'x':
            debug_osm = 1 - debug_osm;
            if (debug_osm) Serial.println(F("ntDebug activated"));
            else           Serial.println(F("ntDebug de-activ."));
            break;
        case 'p':                            // Print Ch. 0
            prnt_out1(adcIn, MIRROR, 0);
            break;
        case 'o':                            // Print Ch. 1
            prnt_out1(adcIn, MIRROR, 1);
            break;
        case 'd':                            // Profiling-Scanner
          for (int i = 0; i < 5; i++){
            Serial.print("n");
            for (int j = 0; j < 4; j++){
              Serial.print("t");
              Serial.print(xyButtons[i][j], DEC);
              }
            }
          break;
        case '?':
        case 'h':
  //        cmd_print_help();
          break;
          default: // -------------------------------
          if ((input >= '0') && (input <= '9')) {
            in_number = (in_number * 10) + (input - '0');
            }
          else {
              if (input == '.') {
                sendFrequency(in_number * 1e3);
                  Serial.print("ntFreq. set to: ");
                  Serial.print(in_number, DEC);
                  Serial.print(" kHz.");
                in_number = 0;
                }
               else {
                  Serial.print("Unexpected: ");
                  Serial.println((char)input);
                  } // else, esli ne ','
               } // else, esli ne cifra
          }
     }
}

void prnt_out1( uint16_t *array, int dlina, int channel)
{
  if ((channel < 0) || (channel > 1)) channel = 0;
  Serial.print("nt");
  for ( uint32_t i = channel; i < dlina; i += 2) {
    Serial.print(array[i] - dc_offset);
    Serial.print("t");
    if ((i + 2 - channel) % 64 == 0) Serial.print("nt");
    }
}

DDS Module AD9850:

void tfr_byte(byte inpd)
{
  for( int i = 0; i < 8; i++, inpd >>= 1) {
    digitalWrite(DATAS, inpd & 0x01);
    pulseHigh(W_CLK);   //after each bit sent, CLK is pulsed high
  }
}
// frequency calc from DATASsheet page 8 = <sys clock> * <frequency tuning word>/2^32
void sendFrequency(double frequency) {
  int32_t freq = frequency * 4294967295 /125000000;  // note 125 MHz clock on 9850
  for( int b = 0; b < 4; b++, freq >>= 8) {
    tfr_byte(freq & 0xFF);
    }
  tfr_byte(0x000);   // Final control byte, all 0 for 9850 chip
  pulseHigh(FQ_UD);  // Done!  Should see output
}

TFT display part:

void   Refresh()
{
  myGLCD.clrScr();
  drawButtons();
  draw_Table();
  draw_Chart();   
  draw_Infor();   
}

void   draw_Infor()
{
    myGLCD.setColor( 0, 255, 255);
    myGLCD.print("Resonance:", 15, 288);
    myGLCD.printNumF( resonance, 3, 215, 288);
    myGLCD.print("kHz.", 330, 288);  

    myGLCD.setColor( 255, 255, 255);
    myGLCD.fillRect( 15, 12, 384, 34);

    myGLCD.setColor( 255,   0, 255);
    myGLCD.print("Start:",  15, 15);
    myGLCD.print("Stop :", 215, 15);  

    if( activ_set == 0 ) myGLCD.setBackColor( 150, 150, 150);
    else                 myGLCD.setBackColor( 255, 255, 255);
    myGLCD.printNumI( startScan, 120, 15);    

//    myGLCD.setColor( 255,   0, 255);
    if( activ_set == 1 ) myGLCD.setBackColor( 150, 150, 150);
    else                 myGLCD.setBackColor( 255, 255, 255);
    myGLCD.printNumI( stop_Scan, 320, 15);    

    myGLCD.setBackColor( 255, 255, 255);
    myGLCD.setColor(  0, 255, 255);
    
    if( activ_set == 0 ) {
      if(adjus_set ==  1) myGLCD.drawRect( 168, 12, 184, 34 );
      if(adjus_set == 10) myGLCD.drawRect( 152, 12, 168, 34 );
      if(adjus_set ==100) myGLCD.drawRect( 136, 12, 152, 34 );
      }
    if( activ_set == 1 ) {
      if(adjus_set ==  1) myGLCD.drawRect( 368, 12, 384, 34 );
      if(adjus_set == 10) myGLCD.drawRect( 352, 12, 368, 34 );
      if(adjus_set ==100) myGLCD.drawRect( 336, 12, 352, 34 );
      }
}  

void   draw_Chart()
{
  int offsetY1 = 36 + 15; //sizeY -15;
  int offsetY2 = sizeY / 2;
  
  myGLCD.setColor(  0,255,255);
  for (int i = 1, p = 0; i < sizeX; i++, p++)
  {
//    myGLCD.drawPixel( i, offsetY + inputData[0][i]);
//    myGLCD.drawLine( p, offsetY1 - magn[p], i, offsetY1 - magn[i]);
    myGLCD.drawLine( p, offsetY1 + magn[p], i, offsetY1 + magn[i]);
  }
  myGLCD.setColor(255,  0,255);
  for (int i = 1, p = 0; i < sizeX; i++, p++)
  {
 //   myGLCD.drawPixel( i, offsetY + inputData[1][i]);
    myGLCD.drawLine( p, offsetY2 + phas[p], i, offsetY2 + phas[i]);
  }
}

void  draw_Table()
{
    myGLCD.setColor( 150, 150, 150);
   
    for ( int y = 10; y < sizeY; y += 36) {
      for ( int x = 10; x < (sizeX -12); x += 6) {
        myGLCD.drawPixel( x, y);
      }
    }
    for ( int x = 10; x < sizeX; x += 36) {
      for (int y = 10; y < (sizeY - 6); y +=  6) {
        myGLCD.drawPixel( x, y);
      }
    }
}

void initButtons()
{
  int cl, cr, cd, cu;

      cl = dispx - 1 - widthBtns;
      cr = dispx - 1 - spaceBtns;
  
  int heigE = (dispy - spaceBtns) /5;
  int heigB =  heigE - spaceBtns;
  
  for (int i = 0; i < 5; i++)
  {   
    cu = ( i * heigE) + spaceBtns;
    cd = cu + heigB;
    
    xyButtons[i][0] = cl;
    xyButtons[i][1] = cr;
    xyButtons[i][2] = cu;
    xyButtons[i][3] = cd;
  }
}

void drawButtons()
{
    int crx, cry;

        crx = dispx - 1;
        cry = dispy - 1;
    
        myGLCD.setColor(255, 255, 255);
        myGLCD.fillRect( 0, 0, crx, cry);
  
  for ( int i = 0; i < 5; i++) {
    myGLCD.setColor(0, 0, 255);
    myGLCD.fillRoundRect(xyButtons[i][0], xyButtons[i][2], xyButtons[i][1], xyButtons[i][3]);
    myGLCD.setColor(255, 255, 255);
    myGLCD.drawRoundRect(xyButtons[i][0], xyButtons[i][2], xyButtons[i][1], xyButtons[i][3]);
    }

    myGLCD.setColor( 0, 255, 0);
    myGLCD.setBackColor( 0,  0, 255);
    myGLCD.print("UP", xyButtons[0][0] +22, xyButtons[0][2] +22);
    myGLCD.print("DN", xyButtons[1][0] +22, xyButtons[1][2] +22);
    myGLCD.print("OK", xyButtons[2][0] +22, xyButtons[2][2] +22);
    myGLCD.print("LF", xyButtons[3][0] +22, xyButtons[3][2] +22);
    myGLCD.print("RH", xyButtons[4][0] +22, xyButtons[4][2] +22);
}

int presButtons(int n)
{
  int hold = 0;
  myGLCD.setColor(255, 0, 0);
  myGLCD.drawRoundRect(xyButtons[n][0], xyButtons[n][2], xyButtons[n][1], xyButtons[n][3]);

  long stampA = millis();
  while (myTouch.dataAvailable())
    myTouch.read();
  long stampB = millis();
  int time = stampB - stampA;
//  if( time > 2000 ) {
//    adjus_set /= 10;
//    if( adjus_set < 1 ) adjus_set = 1;
//    }
//  else {  
    if((time > 500) && (n == 3)) {
      adjus_set *= 10;
      if( adjus_set > 100 ) adjus_set = 100;
      hold = 1;
    }
    if((time > 500) && (n == 4)) {
      adjus_set /= 10;
      if( adjus_set < 1 ) adjus_set = 1;
      hold = 1;
    }
//  }
  myGLCD.setColor(255, 255, 255);
  myGLCD.drawRoundRect(xyButtons[n][0], xyButtons[n][2], xyButtons[n][1], xyButtons[n][3]);

  return hold;
}

int whatButtons( int cx, int cy)
{
  int Btn = -1;

  for (int i = 0; i < 5; i++)
  {   
    if((cx > xyButtons[i][0]) && 
       (cx < xyButtons[i][1]) && 
       (cy > xyButtons[i][2]) && 
       (cy < xyButtons[i][3]))
       Btn = i; 
  }
  return Btn;
}

Sampling & FFT:

void scan_fft()
{
  const  float  phas_corr = -0.803571429;
  double resol  = ((double)(stop_Scan - startScan)) / sizeX;
  double setfr  = startScan * 1000UL;
         resol *= 1000; // in kHz

  int    minim  =  900;  

  for (int i = 0; i < sizeX; i++) {
    setfr += resol;
    sendFrequency(setfr);
    //    delay(2);
    restart_sampl();

    int   prom_magn[2] = {   0};
    float prom_phas[2] = { 0.0};
    int   peak = 1;

    if(debug_osm) {
      Serial.print("ntFr: ");
      Serial.print( setfr, 1);
      }
    
    for( uint32_t y = 0; y < 2; y++){
         radix.rev_bin( fr[y], FFT_SIZE);
         radix.fft_split_radix_real( fr[y], LOG2_FFT);
         //    radix.gain_Reset( fr[y], LOG2_FFT -1);
         radix.gain_Reset( fr[y], 5);
         } 

         radix.get_Magnit1( fr[0], temp);
         peak = peak_search( temp, MIRROR);
         prom_magn[0] = temp[peak];
         prom_phas[0] = get_Phase( fr[0], peak);         

         radix.get_Magnit1( fr[1], temp);
//         peak = peak_search( temp, MIRROR);         
         prom_magn[1] = temp[peak];
         prom_phas[1] = get_Phase( fr[1], peak);         

         if(debug_osm) {
           Serial.print("tY: ");
           Serial.print( y, DEC);
           Serial.print("tPk: ");
           Serial.print( peak, DEC);
           Serial.print("tM: ");
           Serial.print( prom_magn[y], DEC);
           Serial.print("tP: ");
           Serial.print( prom_phas[y], 1);
           delay(20);
           }
    //     }
    magn[i] = (prom_magn[1] - prom_magn[0]) / 4; // ~1200 amplit to 300 - screen size
    magn[i] /= 32;  // Compenstaion for less gain_reset
    magn[i] *= -1;  // Inverse for display, Attenuation.
    
    prom_phas[1] += (peak * phas_corr);    
    int tmpr = prom_phas[1] - prom_phas[0];
    if( tmpr >  1800 ) tmpr -= 3600;
    if( tmpr < -1800 ) tmpr += 3600;
        if(debug_osm) {
           Serial.print("tD: ");
           Serial.print( tmpr, DEC);
          }
    int tmp2 = abs(tmpr);
    if(abs(tmp2) < minim) {
      resonance =  setfr / 1000;             // in kHz
      minim = tmp2;
      }
    tmpr /= 6;                               // ~1800 phase to 300 - screen size
    phas[i] = tmpr;
  }    
  Refresh();
}

inline int mult_shft12( int a, int b)
{
  return (( a  *  b )  >> 12);
}

void restart_sampl( void )
{
   flag = 1;
   adc_configure_trigger(ADC, ADC_TRIG_TIO_CH_0, 0);
    
   while ( flag ) {} // wait new pull

   uint16_t indx_b = 0;
   for ( uint16_t i = 0, k = (NWAVE / FFT_SIZE); i < FFT_SIZE; i++ )
   {  
     uint16_t windw = Hamming[i * k];
       fr[0][i] = mult_shft12((adcIn[indx_b++] - dc_offset), windw);
       fr[1][i] = mult_shft12((adcIn[indx_b++] - dc_offset), windw);
   }
}

void tmr_setup ()
{
  pmc_enable_periph_clk(TC_INTERFACE_ID + 0 *3 + 0); // clock the TC0 channel 0

  TcChannel * t = &(TC0->TC_CHANNEL)[0] ;            // pointer to TC0 registers for its channel 0
  t->TC_CCR = TC_CCR_CLKDIS ;                        // disable internal clocking while setup regs
  t->TC_IDR = 0xFFFFFFFF ;                           // disable interrupts
  t->TC_SR ;                                         // read int status reg to clear pending
  t->TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 |           // use TCLK1 (prescale by 2, = 42MHz)
              TC_CMR_WAVE |                          // waveform mode
              TC_CMR_WAVSEL_UP_RC |                  // count-up PWM using RC as threshold
              TC_CMR_EEVT_XC0 |     // Set external events from XC0 (this setup TIOB as output)
              TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_CLEAR |
              TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_CLEAR ;
  
  t->TC_RC = TMR_CNTR;              // counter resets on RC, so sets period in terms of 42MHz clock
  t->TC_RA = TMR_CNTR /2;           // roughly square wave
  t->TC_CMR = (t->TC_CMR & 0xFFF0FFFF) | TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET ;  // set clear and set from RA and RC compares
  t->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG ;  // re-enable local clocking and switch to hardware trigger source.
}

void adc_setup ()
{
  pmc_enable_periph_clk(ID_ADC);
  //  adc_init(ADC, SystemCoreClock, ADC_FREQ_MAX, ADC_STARTUP_FAST);
  adc_init(ADC, SystemCoreClock, 21000000UL, ADC_STARTUP_FAST);
  NVIC_EnableIRQ (ADC_IRQn);                   // enable ADC interrupt vector
  adc_disable_all_channel(ADC);
  adc_enable_interrupt(ADC, ADC_IER_RXBUFF);
//  ADC->ADC_RPR  =  (uint32_t)  inp;            // DMA buffer
  //ADC->ADC_RCR  =  INP_BUFF;
  ADC->ADC_RPR  =  (uint32_t)  adcIn;      // DMA buffer
  ADC->ADC_RCR  =  INP_BUFF;

  ADC->ADC_PTCR =  1;
  adc_set_bias_current(ADC, 0x01);
  //  adc_enable_tag(ADC);
  adc_enable_channel(ADC, ADC_CHANNEL_7);      // AN0
  adc_enable_channel(ADC, ADC_CHANNEL_6);      // AN1
//  adc_enable_channel(ADC, ADC_CHANNEL_5);      // AN2
  //adc_enable_channel(ADC, ADC_CHANNEL_4);      // AN3
  ADC->ADC_MR = (ADC->ADC_MR & 0xFFFFFFFE);    //DISABLE
  adc_configure_trigger(ADC, ADC_TRIG_TIO_CH_0, 0);
  //  adc_start(ADC);
}

void ADC_Handler (void)
{
  if ((adc_get_status(ADC) & ADC_ISR_RXBUFF) ==	ADC_ISR_RXBUFF) {
    ADC->ADC_MR = (ADC->ADC_MR & 0xFFFFFFFE);  // TRGEN - stop trigger
    ADC->ADC_RPR  =  (uint32_t)  adcIn;          // DMA buffer
    ADC->ADC_RCR  =  INP_BUFF;

    flag = 0;
    }
}

Plus couple supplementary functions:

int peak_search( int *inTemp, int16_t inSize)
{
  int maxim = 0;
  int index = 1;
  
  for( uint16_t i = 1; i < inSize; i++ ) { // DC-off
    int temp = inTemp[i];
    if( temp > maxim ) {
      maxim = temp;
      index = i;
    }
  }
  return index;
}

float get_Phase(int *fr, int n_bin)
{ // Shkala x10, t.e. +-180 = +- 1800, resolution 0.1 degree
        int real = fr[n_bin];
        int imag = fr[FFT_SIZE -n_bin];
        int Phase = 10  * RAD_CONV * atan2((float) imag, (float) real);
        return Phase;
}

   // Time Offset Correction (1 ADC, xx usec delay).
   // 21 MHz, 24 clock = 1.143 usec / 512 (fft.512) x 360.0 = 0.803571429 

  To be continue...

 


OSHWA is having a Membership Drive!

via Open Source Hardware Association

We are launching a Membership Campaign to double our members of like-minded individuals and companies between now and January 15, 2015. Help us reach our goal by spreading the word: www.oshwa.org/membership/. We will keep you all updated on our membership drive as things progress. If you have innovative ideas on how we can attract more members, please get in touch with Aileen at info@oshwa.org. We welcome your ideas.

Tweet: OSHWA is building a future for open source hardware - become a member! http://www.oshwa.org/membership/ #joinoshwa Help us spread the word about OSHWA’s Membership Drive with a Tweet!