Measurement and control of temperature and relative humidity finds applications in numerous areas. These days devices are available which have both temperature and humidity sensors with signal conditioning, ADC, calibration and communication interface all built inside them. The use of such smart sensors greatly simplify the design and reduces the overall cost. We discussed in past about Humidity and temperature measurements with Sensirion’s SHT1x/SHT7x sensors. These sensors are capable of measuring both temperature and relative humidity and provide fully calibrated digital outputs. WhileSHT1x/SHT7x are very accurate sensors, they are still expensive for hobbyists use. This articles discusses the DHT11 sensor which also provides calibrated digital outputs for temperature and humidity but is relatively lot cheaper than the Sensirion sensors. The DHT11 sensor uses a proprietary 1-wire protocol which we will be exploring here and implementing with the PIC16F628A microcontroller that will receive the temperature and humidity values from the sensor and display them on a 16×2 character LCD.

Interfacing DHT11 sensor with PIC16F628A

About DHT11 sensor

The DHT11 sensor comes in a single row 4-pin package and operates from 3.5 to 5.5V power supply. It can measure temperature from 0-50 °C with an accuracy of ±2°C and relative humidity ranging from 20-95% with an accuracy of±5%. The sensor provides fully calibrated digital outputs for the two measurements. It has got its own proprietary 1-wire protocol, and therefore, the communication between the sensor and a microcontroller is not possible through a direct interface with any of its peripherals. The protocol must be implemented in the firmware of the MCU with precise timing required by the sensor.

DHT11 sensor comes in a single row 4-pin package

The following timing diagrams describe the data transfer protocol between a MCU and the DHT11 sensor. The MCU initiates data transmission by issuing a “Start” signal. The MCU pin must be configured as output for this purpose. The MCU first pulls the data line low for at least 18 ms and then pulls it high for next 20-40 μs before it releases it. Next, the sensor responds to the MCU “Start“signal by pulling the line low for 80 μs followed by a logic high signal that also lasts for 80 μs. Remember that the MCU pin must be configured to input after finishing the “Start“ signal. Once detecting the response signal from the sensor, the MCU should be ready to receive data from the sensor. The sensor then sends 40 bits (5 bytes) of data continuously in the data line. Note that while transmitting bytes, the sensor sends the most significant bit first.

“Start” and “Response” signals

The 40-bit data from the sensor has the following structure.

Data (40-bit) = Integer Byte of RH + Decimal Byte of RH + Integer Byte of Temp. + Decimal Byte of Temp. + Checksum Byte

For DHT11 sensor, the decimal bytes of temperature and humidity measurements are always zero. Therefore, the first and third bytes of received data actually give the numeric values of the measured relative humidity (%) and temperature (°C). The last byte is the checksum byte which is used to make sure that the data transfer has happened without any error. If all the five bytes are transferred successfully then the checksum byte must be equal to the last 8 bits of the sum of the first four bytes, i.e.,

Checksum = Last 8 bits of (Integer Byte of RH + Decimal Byte of RH + Integer Byte of Temp. + Decimal Byte of Temp.) 

Now lets talk about the most important thing, which is signalling for transmitting “0″ and “1″. In order to send a bit of data, the sensor first pulls the line low for 50 μs. Then it raises the line to high for 26-28 μs if it has to send “0″, or for 70 μs if the bit to be transmitted is “1″. So it is the width of the positive pulse that carries information about 1 and 0.

Timing difference for transmitting “1s” and “0s”

Start, Response and Data signals in sequence

At the end of the last transmitted bit, the sensor pulls the data line low for 50 μs and then releases it. The DHT11 sensor requires an external pull-up resistor to be connected between its Vcc and the data line so that under idle condition, the data line is always pulled high. After finishing the data transmission and releasing the data line, the DHT11 sensor goes to the low-power consumption mode until a new “Start” signal arrives from the MCU.

Circuit diagram

Here is the circuit diagram showing the DHT11 sensor and a HD44780-based character LCD interfaced to the PIC16F628A microcontroller. The microcontroller runs at 4.0 MHz clock using an external resonator connected between OSC1 (16) and OSC2 (15) pins. The use of 4.0 MHz clock makes the timing calculation easier as 1 machine cycle becomes 1 μs. The timing information will be used to calculate the width of the received data pulse from the sensor so that we could identify if it is carrying a 1 or 0.

Circuit connections for PIC16F628A and DHT11 sensor

The following pictures show the circuit setup on a breadboard. Don’t get confused with the four LEDs and tact switches shown on the perforated board. They have nothing to do with this project. They are there because I am using my DIY Experimenter’s I/O board for the LCD part of this project. Similarly, I am using my 18-pin PIC16F board for easy prototyping with the PIC16F628A microcontroller.

Complete setup of the circuit

PIC16F628A module and the DHT11 sensor are plugged into the breadboard

Software

Writing a software for DHT11 sensor is little more challenging than the hardware part because of the timing conditions for 1s and 0s. I have written sub-routines in mikroC Pro for PIC for initializing the DHT11 sensor and reading the 40-bit of data in sequence. I have used Timer2 module to keep track of the width of the received data pulse, which is required to identify if the received bit is 1 or 0. When a low-to-high pulse is detected at the beginning of any data bit, TMR2 is cleared and turned ON. Since the clock frequency used here is 4.0 MHz, the TMR2 increments by 1 in every 1 μs. The TMR2 is stopped whenever the data pulse is low again. The value of the TMR2 register gives you the the width of the data pulse in μs. I am using 40 μs as the threshold for identifying 0 and 1. If the TMR2 is greater than 40, it means the received bit is 1, else it is 0. Here is the complete source code written in mikroC Pro for PIC. It can be easily adapted to any other platform, but remember that if you are using a different clock frequency you should have to modify the timer operation accordingly.

// LCD module connectionssbit LCD_RS at RB2_bit;sbit LCD_EN at RB3_bit;sbit LCD_D4 at RB4_bit;sbit LCD_D5 at RB5_bit;sbit LCD_D6 at RB6_bit;sbit LCD_D7 at RB7_bit;sbit LCD_RS_Direction at TRISB2_bit;sbit LCD_EN_Direction at TRISB3_bit;sbit LCD_D4_Direction at TRISB4_bit;sbit LCD_D5_Direction at TRISB5_bit;sbit LCD_D6_Direction at TRISB6_bit;sbit LCD_D7_Direction at TRISB7_bit;sbit Data at RA0_bit;sbit DataDir at TRISA0_bit;char message1[] = "Temp = 00.0 C";char message2[] = "RH= 00.0 %";unsigned short TOUT = 0, CheckSum, i;unsigned short T_Byte1, T_Byte2, RH_Byte1, RH_Byte2;void StartSignal(){DataDir = 0;// Data port is outputData= 0;Delay_ms(25);// Low for at least 18usData= 1;Delay_us(30);// High for 20-40 usDataDir = 1;// Data port is input}unsigned short CheckResponse(){TOUT = 0;TMR2 = 0;T2CON.TMR2ON = 1;// Start TMR2 while waiting for sensor responsewhile(!Data && !TOUT); // If there's no response within 256us, the Timer2 overflowsif (TOUT) return 0;// and exitelse {TMR2 = 0;while(Data && !TOUT);if (TOUT) return 0;else {T2CON.TMR2ON = 0;return 1;}}}unsigned short ReadByte(){unsigned short num = 0, t;DataDir = 1;for (i=0; i<8; i++){while(!Data);TMR2 = 0;T2CON.TMR2ON = 1;// Start TMR2 from 0 when a low to high data pulsewhile(Data);// is detected, and wait until it falls low again.T2CON.TMR2ON = 0;// Stop the TMR2 when the data pulse falls low.if(TMR2 > 40) num |= 1<<(7-i);// If time > 40us, Data is 1}return num;}void interrupt(){if(PIR1.TMR2IF){TOUT = 1;T2CON.TMR2ON = 0; // stop timerPIR1.TMR2IF= 0; // Clear TMR0 interrupt flag}}void main() {unsigned short check;TRISB = 0b00000000;PORTB = 0;TRISA = 0b00100001;CMCON = 7;INTCON.GIE = 1;//Enable global interruptINTCON.PEIE = 1;//Enable peripheral interrupt// Configure Timer2 modulePIE1.TMR2IE = 1;// Enable Timer2 interruptT2CON = 0;// Prescaler 1:1, and Timer2 is off initiallyPIR1.TMR2IF =0;// Clear TMR INT Flag bitTMR2 = 0;Lcd_Init();Lcd_Cmd(_Lcd_Clear);Lcd_Cmd(_LCD_CURSOR_OFF);do {Delay_ms(1000);StartSignal();check = CheckResponse();if (!check) {Lcd_Cmd(_Lcd_Clear);Lcd_Out(1, 1, "No response");Lcd_Out(2, 1, "from the sensor");}else{RH_Byte1 = ReadByte();RH_Byte2 = ReadByte();T_Byte1 = ReadByte();T_Byte2 = ReadByte();CheckSum = ReadByte();// Check for error in Data receptionif (CheckSum == ((RH_Byte1 + RH_Byte2 + T_Byte1 + T_Byte2) & 0xFF)){message1[7]= T_Byte1/10 + 48;message1[8]= T_Byte1%10 + 48;message1[10] = T_Byte2/10 + 48;message2[7]= RH_Byte1/10 + 48;message2[8]= RH_Byte1%10 + 48;message2[10] = RH_Byte2/10 + 48;message1[11] = 223;// Degree symbolLcd_Cmd(_Lcd_Clear);Lcd_Out(1, 1, message1);Lcd_Out(2, 1, message2);}else{Lcd_Cmd(_Lcd_Clear);Lcd_Out(1, 1, "Checksum Error!");Lcd_Out(2, 1, "Trying Again ...");}}}while(1);}

You can also simplify the ReadByte subroutine without using the Timer2 module. The following version of ReadByte subroutine works equally well. Once the data pin is detected high, wait for 40 μs and check the data line again. If it is still high, it is 1, else 0.

unsigned short ReadByte(){unsigned short num = 0, t;DataDir = 1;for (i=0; i<8; i++){while(!Data);Delay_us(40);if(Data) num |= 1<<(7-i);while(Data);}return num;}

Download complete source code and HEX files

Output

The accuracy of DHT11 is not as good as Sensirion’s SHT1X/7X series sensors, but it provides an easy and cheap solution to hobbyists for measuring relative humidity and temperature in parallel using a single device, which is sometime required in certain applications such as calculating the dew point.

Temperature and relative humidity measured by DHT11 sensor

[via: http://embedded-lab.com/]

 

Kevin Murphy from New Boston, Michigan is a regular reader of Embedded Lab. He adapted our digital timer project to fulfill his need of a programmable timer switch for his darkroom. Here are some pictures that he has sent us showing how his assembled PCB of this project look like.

Dark room timer

Dark room timer assembled board

He also received a PCB for the 18-pin PIC16F board from us last week. The following pictures shows his assembled breadboard module for PIC16F628.

PIC16F628 breadboard module

The board with an external power supply

Thank you, Kevin for sharing these pictures with us.

[via: http://embedded-lab.com/]

 

In the first part of this tutorial, we discussed about Winstar’s WDG0151-TMI GLCD module, which is a 128×64 pixel monochromatic display built with KS0108B and KS0107B compatible display controllers. The module was interfaced to a PIC16F887 microcontroller and a test program was written in C to demonstrate how to implement the KS0108 instruction set in the firmware of PIC to activate display pixels on the screen. We wrote our subroutine programs that would turn the GLCD on, move the display location to a specified row and column, and draw a pixel at a given coordinates. You might have realized it by now that how much of effort is required to write the firmware for just plotting a point on a GLCD screen. Today’s discussion will focus more on using the built-in GLCD library routines of mikroC Pro for PIC compiler, which will make your life a lot easier if you are using a graphical LCD in your project.

Using MikroC Pro for PIC GLCD library

GLCD Library of mikroC Pro for PIC

The mikroC Pro for PIC provides GLCD library for a 128×64 pixel graphical LCD with Samsung KS0108/KS0107 controller chips. The library routines are categorized into two types: basic and advanced. Before using any library routine, following pin definitions is required. For our case (see the circuit diagram in thefirst part) where PORTD is used for data and PORTB pins for control signals, the GLCD pin settings should be defined as follows. Remember that the data lines must be on a single port.

// glcd pinout settingschar GLCD_DataPort at PORTD;sbit GLCD_CS1 at RB0_bit;sbit GLCD_CS2 at RB1_bit;sbit GLCD_RS at RB2_bit;sbit GLCD_RW at RB3_bit;sbit GLCD_EN at RB5_bit;sbit GLCD_RST at RB4_bit;
 sbit GLCD_CS1_Direction at TRISB0_bit;sbit GLCD_CS2_Direction at TRISB1_bit;sbit GLCD_RS_Direction at TRISB2_bit;sbit GLCD_RW_Direction at TRISB3_bit;sbit GLCD_EN_Direction at TRISB5_bit;sbit GLCD_RST_Direction at TRISB4_bit;

Basic routines:

  • Glcd_Init : Initializes the Glcd module
  • Glcd_Set_Side: Selects the Glcd side. Example, Glcd_Set_Side(0) and Glcd_Set_Side(62) both select the left side of the GLCD. Values from 64-127, such as Glcd_Set_Side(67), selects the right side of the GLCD.
  • Glcd_Set_X : Sets x-axis position from the left border of Glcd within the selected side. Example, Glcd_Set_X(25).
  • Glcd_Set_Page : Selects page (0-7) of the Glcd.
  • Glcd_Read_Data : Reads one byte of data from the current location of Glcd memory and moves to the next location.
  • Glcd_Write_Data : Writes one byte of data from the current location of Glcd memory and moves to the next location.

Advanced routines:

  • Glcd_Fill : Fills GLCD display RAM with a byte pattern. If the byte is 0, it will clear the display. If it is 0xFF, then it will fill the entire display with 1.
  • Glcd_Dot : Draws a dot on Glcd at given coordinates with a specified color. It is used as Glcd_Dot(x, y, color), where x = 0-127, and y=0-63, and color = 0-2. The parameter color determines a dot state: 0 clears dot, 1 puts a dot, and 2 inverts the dot state.
  • Glcd_Line : Draws a line joining two specified point coordinates and a given color value (0-2).
  • Glcd_V_Line : Draws a vertical line passing through two points with the same x-coordinate. It also accepts color parameter.
  • Glcd_H_Line : Draws a horizontal line passing through two points with the same y-coordinate. It also accepts color parameter.
  • Glcd_Rectangle : Draws a rectangle with specified top left and bottom right corner coordinates. It also accepts color parameter.
  • Glcd_Box : Draws a box with specified top left and bottom right corner coordinates. Unlike in Glcd_Rectangle, the color parameter here is the fill color of the box.
  • Glcd_Circle : Draws a circle with specified center coordinates and radius. It also accepts color parameter.
  • Glcd_Set_Font: As it was mentioned earlier in Part1, KS0108 controller does not have a built-in character generator and therefore fonts must be written in the firmware of the external microcontroller. This is a time consuming task as you need to determine data values for each letter to display. For simplicity, mikroElektronika provides the following demo fonts with mikroC Pro for PIC compiler.
    • Font_Glcd_System3x5
    • Font_Glcd_System5x7
    • Font_Glcd_5x7
    • Font_Glcd_Character8x7

    These fonts are used by Glcd_Write_Char and Glcd_Write_Text functions (defined below) to display character and text. The syntax for defining font is,

Glcd_Set_Font (const char *activeFont, unsigned short aFontWidth, unsignedshort aFontHeight, unsigned int aFontOffs);

where parameters are:

  • activeFont: font to be set. Needs to be formatted as an array of char
  • aFontWidth: width of the font characters in dots.
  • aFontHeight: height of the font characters in dots.
  • aFontOffs: number that represents difference between the mikroC PRO for PIC character set and regular ASCII set. Demo fonts supplied with the library have an offset of 32.

So, if you want to use the Font_Glcd_5x7, you can define the font as,

Glcd_Set_Font(Font_Glcd_5x7, 5, 7, 32);
  • Glcd_Write_Char : Writes a character at a defined x-position (0-127) and page number (0-7) on GLCD.
  • Glcd_Write_Text : For printing a text at a given x-position (0-127) and page number (0-7) on GLCD
  • Glcd_Image: Displays bitmap image on Glcd. The bitmap array of the image must be provided in the firmware. I have described this part in more detail in this post:

Circuit diagram

It is the same as described in

Part 1

of this tutorial and I am using UNI-DS6 development board for demonstration.

Software

The following program is written in mikroC Pro for PIC compiler for demonstrating the GLCD library routines described above. The first part of the program draws horizontal and vertical lines, rectangle, circle, and a filled box on the GLCD. The second part writes texts in all eight pages (0-7) using Font_Glcd_5x7. At the end, bigger font size (Font_Glcd_Characters_8x7) is used to write “Embedded Lab” at the center of the screen.

/** Project name: Testing GLCD with PIC16F887Embedded-Lab.com, Jan 02, 2012.* Description:This routine demonstrates how to use MikroC Pro for PIC GLCD libraryroutines for displaying shapes and texts of various font size.* Test configuration:MCU:PIC16F887Dev.Board:UNI-DS6Oscillator:HS, 10.0000 MHzExt. Modules:GLCD 128x64, KS108/107 controller*/// Glcd module connectionschar GLCD_DataPort at PORTD;sbit GLCD_CS1 at RB0_bit;sbit GLCD_CS2 at RB1_bit;sbit GLCD_RSat RB2_bit;sbit GLCD_RWat RB3_bit;sbit GLCD_RSTat RB4_bit;sbit GLCD_EN at RB5_bit;sbit GLCD_CS1_Direction at TRISB0_bit;sbit GLCD_CS2_Direction at TRISB1_bit;sbit GLCD_RS_Directionat TRISB2_bit;sbit GLCD_RW_Directionat TRISB3_bit;sbit GLCD_RST_Directionat TRISB4_bit;sbit GLCD_EN_Direction at TRISB5_bit;// End Glcd module connectionsvoid Delay2S(){// 2 seconds delay functionDelay_ms(2000);}void main() {ANSEL= 0;// Configure AN pins as digitalANSELH = 0;C1ON_bit = 0;// Disable comparatorsC2ON_bit = 0;TRISD = 0x00;TRISB = 0x00;Glcd_Init();// Initialize GLCDGlcd_Fill(0x00);// Clear GLCDdo {Glcd_V_Line(0, 63, 64, 1);Glcd_H_Line(0, 127, 32, 1);Delay2s();Glcd_Rectangle(10,5,117,57,1);// Draw rectangle, color is 1Delay2s();Glcd_Circle(64,32, 15, 1);Delay2s();Glcd_Fill(0xFF);// Fill Glcd with all 1sDelay2s();Glcd_V_Line(0, 63, 64, 0);Glcd_H_Line(0, 127, 32, 0);Delay2s();Glcd_Rectangle(10,5,117,57,0);// Draw rectangle, color is 0 nowDelay2s();Glcd_Circle(64,32, 15, 0);Delay2s();Glcd_Fill(0x00);// Clear GLCDDelay2s();Glcd_Box(10,5,117,57,1);Delay2s();Glcd_Fill(0x00);// Clear GLCDDelay2s();Glcd_Set_Font(Font_Glcd_5x7, 5, 7, 32);Glcd_Write_Text("This is Page 0", 10, 0, 1);Glcd_Write_Text("This is Page 1", 10, 1, 1);Glcd_Write_Text("This is Page 2", 10, 2, 1);Glcd_Write_Text("This is Page 3", 10, 3, 1);Glcd_Write_Text("This is Page 4", 10, 4, 1);Glcd_Write_Text("This is Page 5", 10, 5, 1);Glcd_Write_Text("This is Page 6", 10, 6, 1);Glcd_Write_Text("This is Page 7", 10, 7, 1);Delay2s();Glcd_Fill(0x00);// Clear GLCDGlcd_Set_Font(Font_Glcd_Character8x7, 8, 7, 32);Glcd_Write_Text("Embedded Lab", 8, 3, 2);// Write stringDelay2s();Glcd_Fill(0x00);// Clear GLCD} while(1);}

The pictures below show some of the outputs of this program. This has been tested on UNI-DS6 development board with PIC16F887 microcontroller.

Drawing lines, rectangle, and circle using GLCD library

Same thing with inverted color

Filled box

Displaying GLCD page numbers with 5×7 font size

Text display with 8×7 font

You can also display a 128×64 pixels monochromatic bitmap image on the GLCD. For this you need to first convert the bitmap image file into a data array. The following link takes you to a tutorial page which describes how to do that.

Displaying Microchip logo using GLCD bitmap editor tool

[via: http://embedded-lab.com/]

 

Because of their compact size, ease of use and many good features that are just enough to do most of the medium range projects, the 18-pin PIC16F series processors (PIC16F628A, PIC16F88, and now PIC16F1827/47) have always been my favorite microcontrollers. Many of my projects and tutorials written in this blog also use PIC16F628A and PIC16F1827 microcontrollers. As I will be using them more in the future too, I thought of making some PCB versions of my breadboard module for PIC16F628A with some modifications. I used Iteadstudio’s PCB prototyping service for this, and I would say the PCBs turned out really well for the price I paid. I used their 2 layer 5cm x 5cm service and got 10 PCBs for less than $15, including shipping to the United States.

Mini breadboard module for 18-pin PIC microcontrollers

Component layout and circuit diagram

The PCB dimension is approximately 5.0 cm x 3.5 cm and has got the following components:

  • 18-pin DIP socket to hold an 18-pin PIC microcontroller
  • 3-pin Ceramic resonator (4.0 MHz)
  • Two tact switches (6 mm x 6mm, 2-pin type like this)
  • 2-pin dip switch
  • 4 resistors (two 10 KΩ, two 470 Ω)
  • 1 capacitor (100 nF)
  • 2 LEDs
  • 5-pin ICSP header
  • Header pins for Vcc, Gnd, PORTA, and PORTB pins

The layout of these components on the PCB is shown below. There are two tact switches on the board: one for reset and other for user input. Similarly, there are two LEDs: one for power-on indicator and the other for user output. The input tact switch and the output LED can be connected to RB0 and RB1 pins respectively through a 2-pin dip switch. The switch and the LED are useful for a quick test of the board. The microcontroller clock is derived using an external ceramic resonator having 3 pins with built-in load capacitors. PORTA (RA0-RA4) and PORTB (RB0-RB7) pins of the microcontroller are accessible for experimenting through header pins (male, female, or right-angled). A 5-pin header is also available for ICSP programming through a PICkit2 or PICkit3.

Layout of the components on the board

The circuit diagram of this breadboard module is provided below. It can also be constructed on a general purpose prototyping perforated circuit board just like my breadboard module for PIC16F628A.

Circuit diagram of the breadboard module

PCB design

I designed a two layer PCB for this project using the DesignSpark PCB design software, which is free and easy to use. It also lets you create Gerber files of your designed PCB. The top and bottom layer of the PCB are shown below. I have used a copper pour on the bottom layer to create a ground plane. It could be done for the top layer too.

 

Top view of PCB

Bottom view of PCB

The assembled module of the project is shown below.

PCB with all components soldered

Module can be easily plugged into a breadboard

Right-angled male header pins can also be used for accessing I/O port pins so that it can be plugged straight on one side of the breadboard (shown below).

Board can also have right angled headers

Running a test program (flashing LED) on the board

At last, I have got a few of these PCBs as extra. If you are interested to get one, contact me at admin (at) embedded-lab.com. It will be $4.50 (through Paypal) for one PCB and that will include shipping within the United States through USPS. Sorry, I won’t be able to ship it outside the US.

Download Gerber files of the PCB design

[via: http://embedded-lab.com/]

 

The use of a graphical LCD (GLCD) drastically changes the look of your project. It provides more freedom for presenting data than the HD44870 based character LCDs. Today we will see how to interface a KS0108 (name of the display controller chip) based GLCD to a PIC microcontroller. This experimental tutorial is divided into two parts. In the first part, we will see how to write a firmware for the PIC microcontroller to initialize the GLCD and send data to plot points and lines on the screen. The second part will focus more on exploring the built-in GLCD Library of mikroC Pro for PIC compiler to display more complex texts and objects. Since GLCDs are real resource hungry devices (in terms of required I/O pins and memory), a bigger size PIC microcontroller (PIC16F887, which has 36 I/O pins and 14KB flash memory) is selected for this experiment. I am using MikroElektronika’s UNI-DS6 development board to demonstrate this project, but the circuit setup can also be made on a breadboard.

Interfacing a 128×64 pixels GLCD

Theory

The graphical LCD used in this experiment is Winstar’s WDG0151-TMI module, which is a 128×64 pixel monochromatic display. It uses two Neotic display controller chips: NT7108C and NT7107C, which are compatible with Samsung KS0108B and KS0107B controllers. The KS0108B (or NT7108C)is a dot matrix LCD segment driver with 64 channel output, and therefore, the WDG0151 module contains two sets of it to drive 128 segments. On the other hand, the KS0107B (or NT7107C) is a 64-channel common driver which generates the timing signal to control the two KS0108B segment drivers. The KS0108B and KS0107B are a very popular controllers and have made their way into many graphical LCDs. The internal block diagram of the WDG0151 GLCD module is shown below.

Internal block diagram of a KS0108B (NT7108C) based 128×64 pixel GLCD module

The NT1707C drives the 64 display lines, COM1 – COM64. The first NT7108C drives the left half segments (SEG1 to SEG64) and the second one drives the right half segments (SEG65 to SEG128) of the display. The two halves of the display can be individually accessed through the chip select pins (CS1 and CS2) of the two NT7108C drivers. Each half consists of 8 horizontal pages (0-7) which are 8 bits (1 byte) high. This is illustrated in the drawing below.

GLCD pages

Starting from page 0 on the left half (/CS1 = 0) if you transmit one data byte, it will appear on the first column of page 0. If you repeat this 64 times, then switch to the second half, and repeat until 128th position is reached, the first 8 display lines will be plotted. The next 8 lines can be plotted similarly by switching to page address 1. The total amount of bytes needed for a complete display frame (128×64 pixels) is, therefore,2 * 64 pixels * 8 bits = 1024 bytes.

The Winstar WDG0151-TMI module does have an internal negative voltage generator circuit which provides a negative voltage at VEE external pin. An external potentiometer (usually 10 K) is connected between Vcc and VEE pins to set the LCD working voltage (contrast) at Vo pin. The pin diagrams of KS0108 based GLCDs is not standardized and it is therefore, important to read the manufacturer’s datasheet for correct wiring of a GLCD module. The following table shows the pin descriptions ofWinstar WDG0151-TMI module. It has altogether 20 pins. The first two pins (1 and 2) are the chip select pins for the left and right display controller. They are active low in a WDG0151-TMI module, but they could be active high in some other models. That’s why I said reading manufacturer’s datasheet is very important. The WDG0151-TMI module operates at 5.0 V power supply. Pin number 6 is Data/Instruction (also called Register Select, RS) select pin. The 8-bit data fed to D0-D7 pins of the GLCD is received by the LCD controller chip as an instruction if D/I = 0, and as data if D/I is 1. The R/W and E pins have similar functions as in a HD44780 based character LCD module.A fixed resistor value must be connected in series with the back-light LED (pins 19 and 20) to limit the current.

Pin description of Winstar WDG01510 GLCD module

The KS0107/KS0108 does not have a character generator so this must be implemented in the microcontroller firmware. The LCD controller supports a handful of instructions which are summarized in the table shown below. Note that the RS (D/I) pin is high only during data read and data write operations, and stays low when a transmitted byte is an instruction.

Display control instructions (click on image to enlarge)

Circuit diagram

The circuit diagram for this experiment is shown below. The PIC16F887 microcontroller is used to drive a Winstar WDG0151-TMI GLCD. The data pins are connected to PORTD and other control signals are driven through PORTB pins. I am trying this circuit on MikroElektronika’s UNI-DS6 development board.

Circuit diagram for GLCD interfacing

Software

We will be writing our test program in C using MikroElektronika’s mikroC Pro for PIC compiler. Although, the compiler does provide built-in library routines for GLCD operations, we will first try to write our own test code for transferring display data from the PIC16F887 to the GLCD. Later, we will explore the MikroElektronika’s GLCD library for more complex operations. The code provided below generates 11 dotted horizontal lines on the GLCD screen with a six-line spacing between two. I took most portion of it from Osama’s Lab GLCD library and modified it to suit with mikroC Pro for PIC and WDG0151-TMI GLCD. Here’s a brief description of various user-defined function subroutines used in the code.

GLCD_ON() : This function turns the display on. This can be done by sending the command 3Fh to both the controllers. So, while sending this command, both CS1 and CS2 must be pulled low. Similarly the RS pin should be low too as the byte sent is an instruction.

Set_Start_Line() :This function changes the line number to be displayed at the top of the screen. You can set it to be any number between 0 to 63. It does not affect the data in the display RAM, it just scrolls the display up and down.

GOTO_COL() : Moves the cursor to specified column (0-127).

GOTO_ROW() : Moves the cursor to specified row or page number (0-7).

GOTO_XY() : Moves the cursor to specified row and column.

GLCD_Write() : Writes a byte of data to the current location.

GLCD_Read() : Returns a byte read from the current display location. If you see the code for this subroutine, you will see there are two read operations involved. The first one is a dummy read during which the data is fetched from the display RAM is latched in to the output register of KS0108B. In the second read, the microcontroller can get the actual data.

GLCD_Clrln() : Clears a specified row (0-7).

GLCD_CLR() : Clears the whole screen (all 8 pages).

Draw_Point() : Plots a dark or light color point at a specified position.

At the end, the dotted lines are created by plotting too many points in horizontal directions.

/** Project name: Testing GLCD with PIC16F887Embedded-Lab.com, 2011.* Description:This routine demonstrates how to initialize a KS0108 based GLCD andactivate the pixels on the display. A sub-routine is written to draw a point onthe GLCD at a given coordinates, and is used to draw dotted lines.* Test configuration:MCU:PIC16F887Dev.Board:UNI-DS6Oscillator:HS, 10.0000 MHzExt. Modules:GLCD 128x64, KS108/107 controller*/// Glcd module connections#define GLCD_DataPORTD#define GLCD_DirTRISDsbit GLCD_CS1 at RB0_bit;sbit GLCD_CS2 at RB1_bit;sbit GLCD_RSat RB2_bit;sbit GLCD_RWat RB3_bit;sbit GLCD_RSTat RB4_bit;sbit GLCD_EN at RB5_bit;sbit GLCD_CS1_Direction at TRISB0_bit;sbit GLCD_CS2_Direction at TRISB1_bit;sbit GLCD_RS_Directionat TRISB2_bit;sbit GLCD_RW_Directionat TRISB3_bit;sbit GLCD_RST_Directionat TRISB4_bit;sbit GLCD_EN_Direction at TRISB5_bit;// End Glcd module connectionsvoid Enable_Pulse(){GLCD_EN = 1; //EN highdelay_us(5);GLCD_EN = 0; //EN lowdelay_us(5);}void GLCD_ON(){//Activate both chipsGLCD_CS1 = 0;GLCD_CS2 = 0;GLCD_RS = 0;//RS low for command byteGLCD_RW = 0;//RW low for writeGLCD_Data = 0x3F;//ON commandEnable_Pulse();}void Set_Start_Line(unsigned short line){GLCD_RS = 0;//RS low for commandGLCD_RW = 0;//RW low for write//Activate both chipsGLCD_CS1 = 0;GLCD_CS2 = 0;GLCD_Data = 0xC0 | line;//Set Start Line commandEnable_Pulse();}void GOTO_COL(unsigned int x){unsigned short Col_Data;GLCD_RS = 0;GLCD_RW = 0;if(x<64)//Left Half{GLCD_CS1 = 0;//select chip 1GLCD_CS2 = 1;//deselect chip 2Col_Data = x;//put column address on data port}else//Right Half{GLCD_CS2 = 0;GLCD_CS1 = 1;Col_Data = x-64;//put column address on data port}Col_Data = (Col_Data | 0x40 ) & 0x7F;//Command formatGLCD_Data = Col_Data;Enable_Pulse();}void GOTO_ROW(unsigned int y){unsigned short Row_Data;GLCD_RS = 0;GLCD_RW = 0;Row_Data = (y | 0xB8 ) & 0xBF; //put row address on data portGLCD_Data = Row_Data;Enable_Pulse();}void GOTO_XY(unsigned int x,unsigned int y){GOTO_COL(x);GOTO_ROW(y);}void GLCD_Write(unsigned short b){GLCD_RS = 1;GLCD_RW = 0;GLCD_Data = b;delay_us(1);Enable_Pulse();}unsigned short GLCD_Read(unsigned short column){unsigned short read_data = 0; //Read data hereGLCD_Dir = 0xFF;//PORTD as InputGLCD_RW = 1;//ReadGLCD_RS = 1;//DataGLCD_CS1 = (column>63);GLCD_CS2 = !GLCD_CS1;//Disable/Enable CS2delay_us(1);GLCD_EN = 1;//Latch RAM data into ouput registerdelay_us(1);//Dummy read to fetch data from the display RAMGLCD_EN = 0;//Low Enabledelay_us(5);GLCD_EN = 1;//latch data from output register to data busdelay_us(1);read_data = GLCD_Data;//Input dataGLCD_EN = 0;//Low Enable to remove data from the busdelay_us(1);GLCD_Dir = 0x00;//PORTD is Output againreturn read_data;}void GLCD_Clrln(unsigned short ln){int i;GOTO_XY(0,ln);//At start of line of left sideGOTO_XY(64,ln);//At start of line of right sideGLCD_CS1 = 0;for(i=0;i<65;i++)GLCD_Write(0);}void GLCD_CLR(){unsigned short m;for(m=0;m<8;m++){GLCD_Clrln(m);}}void Draw_Point(unsigned short x,unsigned short y, unsigned short color){unsigned short Col_Data;;GOTO_XY(x,(y/8));switch (color){case 0://Light spotCol_Data = ~(1<<(y%8)) & GLCD_Read(x);break;case 1://Dark spotCol_Data = (1<<(y%8)) | GLCD_Read(x);break;}GOTO_XY(x,(y/8));GLCD_Write(Col_Data);}void main() {unsigned short u, v;ANSEL= 0;// Configure AN pins as digitalANSELH = 0;C1ON_bit = 0;// Disable comparatorsC2ON_bit = 0;TRISD = 0x00;TRISB = 0x00;PORTB = 0x00;PORTD = 0x00;GLCD_CS1 = 1;// De-Activate both chipsGLCD_CS2 = 1;GLCD_RST = 1;GLCD_ON();GLCD_CLR();Set_Start_Line(0);do {for(u=0; u<64; u+=6)for (v=0; v<128; v+=2)Draw_Point(v, u, 1);delay_ms(1000);GLCD_CLR();delay_ms(1000);} while(1);}

Download the Source code and HEX file

When the microcontroller runs the program, you will see 11 dotted horizontal lines displayed on the screen as shown below.

Dotted horizontal lines

Summary

In this experimental tutorial, the internal block diagram of a KS0108 based 128×64 pixel GLCD, its pin configurations, and instruction set were discussed briefly. The GLCD operation was demonstrated with a Winstar WDG0151 GLCD module interfaced to the PIC16F887 microcontroller. A very basic firmware was written in mikroC Pro for PIC to demonstrate how to activate selected pixels on the screen to draw a line. In the second part of this tutorial (will be posted soon) we will learn to use the built-in GLCD library of mikroC Pro for PIC compiler to draw more complex objects and texts on the screen.

[via: http://embedded-lab.com/]

 

Probably some of you are struggling in finding a proper LCD driver that would work on any circuit. Just wanted to point out that I found some time to improve my current LCD library so it would support a mixed pin connection case. Earlier you had to connect LCD in pretty strict way where pins had to be aligned and connected to single AVR port. Sometimes this can’t be done due to various reasons – you want to use those pins for other alternative functions or simply you want to trace your PCB better etc.

In this updated version of library there are two more modes added : LCD_4BIT_M and LCD_8BIT_M that allow controlling LCDs either in 4 or 8 bit mode but with any pin connection layout. So data pins and control pins can be connected to any pin number and any port. Couple examples should give some clue on how to start using it. If you used this library for some project you only need to modify header file while project source code can remain same. Download LCD library here.

[via: http://www.scienceprog.com/]

 

Last year, Texas Instruments (TI) released industry’s lowest cost (only $4.30) microcontroller development kit named Launchpad. It is a complete development tool for rapid prototyping with TI’s ultra-low power MSP430 Value Line MCUs. I also bought a Launchpad kit several months ago but haven’t really looked into it. Last weekend, I spent a couple of hours reading about the MSP-EXP430G2 experimenter board (that comes with the Launchpad kit) and the Code Composer Studio (CCS) software tool. CCS is an integrated development environment (IDE) to develop and debug applications for TI’s embedded processor families. As always, a “Hello World” program is the best thing to start with any new development platform. If you ran it successfully, it means you have setup the compiler and the programmer/debugger correctly and you are all set to go. Here’s a step-by-step guide to set up the CCS v4 build and debug environment for the MSP-EXP430G2 experimenter board.

TI’s MSP430 Launchpad board

A brief introduction to MSP430 Launchpad

Launchpad is a low cost development solution to get started with TI’s MSP430 Value Line devices. The Launchpad kit includes a MSP430G2 experimenter board, two MSP430G2xx flash microcontrollers (MSP430G2231 and MSP430G2211), a mini USB cable, four 10-pin headers (two male and two female), and a 32.768 KHz external crystal. The total cost of the kit is only $4.30. The experimenter board has an integrated DIP target socket that supports up to 20 pin devices. All of the pins are broken out to the header ports on either side of the board for easy access. The on-board flash emulation tool allows direct interface to a PC through an USB port for easy programming and debugging. The same interface can also be used by the MSP430G2xx device on the board to communicate with the host PC through a 9600 baud UART serial connection (virtual COM port).

There are two LEDs (red and green) on the board that are connected to P1.0 and P1.6 I/O pins. Similarly, two push buttons for user input and device reset are also available on the board.

There are different development tools available to write, download, and debug applications for the MSP-EXP430G2 development board. Code Composer Studio (CCS) is one of them and is also available in a free limited version that allows 16 KB of C-code compilation. This is just enough for experimenting with MSP-EXP430G2 board.

MSP430 Launchpad

Installation of Code Composer Studio v4

Code Composer Studio™ (CCS) is an integrated development environment (IDE) for developing and debugging embedded applications for TI’s various processor families.It includes compilers for each of TI’s device families, source code editor, project build environment, debugger, profiler, simulators and many other features. There are many versions of CCS IDE, but I will be talking about CCS v4, which is a recommended version for MSP430 users. So first you need to go to the CCS download wiki by clicking the following link.

http://processors.wiki.ti.com/index.php/Download_CCS

Then scroll down a little bit and click on the download button, “Download latest production CCSv4 MSP430/C28x code size limited image“, as shown below.

Download the FREE Code Size Limited Tools for MSP430 and C28x

You will be directed to my.TI Account page where you need to log in. If you don’t have a TI account you can create one for free. The link to the installation zip file will be then sent to your email. Click on the link and download the zip file. Unzip it and double click on the file named setup_CCS_MC_Core_x.x.x.x.exe. Follow the installation instructions and select MSP430-only Core Tools option in the Product Configuration dialogue window and click Next. Use the default settings in theSelect Components dialog and click Next. Then click Next again in the Start Copying Files dialogue and wait till the installation completes.

Note: By default, Microsoft Windows Vista and Windows 7 do not allow user level accounts to write files in the Program Files directory. This can prevent CCS from starting when installed in the Program Files or Program Files (x86) directory, because CCS stores configuration data by default in its install directory. So it is recommended to install CCS outside the Program Files or Program Files (x86) directory.

Writing a “Hello World” program

The Launchpad experimenter board includes a pre-programmed MSP430G2231 device which is already placed in the target DIP socket. When the board is powered through a USB cable, the preloaded demo program starts and the on-board red and green LEDs toggle in sequence. This is the quickest way of verifying that the Launchpad board is working. But this is not our “Hello World” program. We will first explore the basic features of CCS and see how to create a new project, build the code, and eventually load the program into the flash memory of the MSP430 device on board. Our test program will make the MSP430G2231 device to continuously flash the on-board red LED connected to P1.0 pin.

Step 1: Start CCS by double clicking the icon on the desktop or selecting it from the Windows Start menu. When CCS loads, the first thing it asks is to define a workspace – a directory that holds all elements (projects, links to projects, possibly source code) used in the development. Browse the directory path where you want it to be and do NOT check the Use this as the default and do not ask again checkbox. Click OK.

Select Workspace folder

Step 2: If this is the first time you have opened CCS then a Welcome to Code Composer Studio v4 page appears.

CCS Welcome screen

Close the page by clicking the X on the Welcome tab. You should now see an empty CCS workbench with a C/C++ icon in the upper right hand corner. This is called the C/C++ Perspective and is used to create or build C/C++ projects. When you will be the debugging mode, it will automatically switch to the Debug Perspective. The layout views of the workbench windows, tool-bars, and menus are different for code development and debugging. Therefore, the use of separate perspectives minimizes clutter to the user interface.

Empty CCS workbench in C/C++ Perspective

Step 3: Next step is to create a new project. A project contains all the files you will need to develop an executable output file (.out) which can be run on the MSP430 hardware. To create a new project click: File -> New -> CCS Project

In the Project name field type FlashLED. It will create a new directory called FlashLED inside the Workspace folder (if Use default location is checked), where all the project files will be stored. Click Next.

Creating a new CCS project

Step 4: The next window that appears selects the platform and configurations. The Project Type should be set to MSP430 and the Debug and Release boxes should be checked in the Configurations box. Click Next.

Project type is MSP430

Step 5: The next window is to define inter-project dependencies (if any). There are none now. So select Next.

No inter-project dependencies for now

Step 6: The next step is to select the CCS project settings. Select the Device Variant using the pull-down list and choose MSP430G2231 (target device on the Launchpad experimenter board). This will select the appropriate linker command file, runtime support library, set the basic build options for the linker and compiler, and set up the target configuration.

Select MSP430G2231 for Device Variant

Click Finish and a new project is created. You will now see the C/C++ Projects window contains FlashLED project which is set active.

New project FlashLED added to the C/C++ Projects window

Step 7: At this point, the project does not include any source files. The next step is to add the source files to the project. To add a source file to the project, right-click on FlashLED in the C/C++ Projects window and select: New -> Source File. Name the source file main.c and click Finish.

Add a new source file main.c

Then an empty window will open for the main.c code.

Empty window for main.c

Step 8: Next, we will add code to main.c. Type in or copy and paste the following code into the main.c window. This code is for flashing the red LED connected to P1.0.

#include <msp430g2231.h>volatile unsigned int i;void main(void){WDTCTL = WDTPW + WDTHOLD;// Stop watchdog timerP1DIR = 0x01;// P1.0 is outputP1OUT = 0x00;// LED offwhile(1){P1OUT = ~P1OUT;// Complement output_delay_cycles(500000);// Delay 50K clock cycles}}

The comments in the code pretty much explains each line of the code. The delay statement is a built-in intrinsic function for generating delays. The only parameter needed is the number of clock cycles for the delay. The while(1) loop repeats the next two lines for ever. After you finish typing the above code into main.c window, save your work.

Save the main.c file after typing up the code

Step 9: Next click the Build button or select Project-> Build Active Project and watch the tools run in the Console window. Check for any errors in the Problems window.

Build and Debug buttons in the toolbar

Console and Problems windows

Step 10: If the build is successful, you are ready to load the program into the target MSP430 device. Make sure that the Launchpad board is connected to the PC. Then click the Debug button (green bug). The Debug Perspective view should open, the program is loaded into the MSP430 device automatically, and you should now be at the start of main().

Debug Perspective

In debug mode, you can run the application in either a single step mode using Step Into and Step Over function buttons or a free run mode by clicking on the Run button (see the picture below).

Debugging menu

If you click on the Run button, the MSP430G2231 processor starts executing the program and the LED on board should blink continuously.

Step 11: At last, terminate the active debug session using the Terminate All button. This will close the debugger and return CCS to the C/C++ Perspective view. CCS will remove the breakpoints and release the MSP430G2231 processor in the board so that it could execute freely the program without the need of the debugger.

Blinking LED at P1.2

Note:The _delay_cycles(500000); statement in the code creates a delay interval of 500000 clock cycles. If you want to convert it into time, you need to know the time period or frequency of the clock. MSP430 devices have multiple sources of clock including internally generated clocks (known as internal digitally controlled oscillator, DCO) and external source (using crystal). On power on reset, the default clock source comes from the internal DCO module. The default value of this clock is around 1.0 MHz (the internal oscillator is not very accurate). So, the  _delay_cycles(500000); statement should create a delay of approximately 0.5 sec interval.

References

Various documents and links available at “Getting Started with the MSP430 LaunchPad Workshop”.

[via: http://embedded-lab.com/]

 

When building AVR DDS2 signal generator there were lots of discussions about signal conditioning in analog part of device. First argument was that LM358 wasn’t the best choice for this purpose. Another one pointed to sine wave that weren’t smooth enough.

As you can see there are some dents on it. Other waveforms also are distorted especially when higher voltages are selected. This definitely asks for better analog part. Some people suggested to replace LM358 with OPA2134, but it seems to be quite expensive choice. In my opinion low noise general purpose op-amp can be great too. I’m gonna give a try to Texas Instruments TL074 low noise op-amp. It is low power, high slew rate (13V/us) IC – almost five times faster than LM358 and for same reasonable price.

If we look at DDS2 signal generator offset and gain control circuit we can see that there is a problem in it. We regulate offset before amplitude control – normally offset should be regulated after amplifying signal. And third problem is that offset can be regulated in small interval – in 5V range while we could do this in -12V … +12V range. Lets redesign analog part so it would give better result.

Zeroing offset voltage

First of all offset control we can slightly modify first stage so that offset voltage of signal would be set to 0. For instance if we are generating sine-wave on DAC we get maximum 2.5V amplitude signal with 2.5V DC offset. Instead of using POT1 we can calculate resistor divider so that on output we get 0V offset. We assume that all signals coming out of microcontroller have offset 2.5V (when VCC=5V), then we can model following circuit:

Calculating is easy:

We know that source voltage is Vs=2.5V. Output voltage is Vo=0V. We need gain 1 (-1 for inverting amp). So at inverting op – amp input we get voltage:

From from operational amp analysis we know that V- = V+ and currents to both inputs are equal to zero (I- = I+ = 0A). Then We must get 1.25V at V3. so we need to select voltage divider to scale voltage from 5V to 1.25. To do so we chose one fixed resistor value R3 to be 100kOhms and then we calculate R4:

evaluate R4:

We can get 33.33kΩ by connecting two resistors in series: 33kΩ + 330Ω that will give resulting voltage close enough to 0V.

Optional filter stage

In the next stage of analog part we are going to add a filter. Since DDS signal generator is capable of outputting various types of signals we don’t want it to be hardwired. For instance if we generate sine wave the we could use filter to make it more smooth while on square wave it would have negative effect by rounding the shape. For this we can add a bypass switch that would allow to insert or bypass filter on demand. What filter are we going to use for this purpose. Since we have plenty of op-amps on TL074 we can make an active filter.

I find Butterworth (Sallen – Key) low pass filter quite handy since it gives flattest response in band. Lets select parameters for this filter. DDS generator in our case isn’t going to exceed 100kHz frequency. Also we want attenuation in this up to roll – off frequency close 0dB. So lets increase roll-off frequency up to 200kHz. This value we are going to use while calculating.

Resistor and capacitor values of filter can be determined with these simple rules:

R1 = R2, C1 = 2·C2

If we select R2 = R1 = 33k (have plenty of these)

then we can calculate C2 as follows:

so standard value of C1 = 33pF.

You can check calculations on line here.

Signal amplitude gain control

After we have adjusted signal offset voltage filtered it (or not) the next part is to adjust amplitude. We need to adjust signal amplitude from 0 to 12V. For this purpose we are gonna use inverting amplifier with potentiometer adjustable gain. Wee need to calculate proper resistor values to get nice gain control over all potentiometer turn. Say we are going to use 47k potentiometer. Lets calculate input resistor value.

Another known condition is that input voltage from previous stage is 2.5V. Say we want to get 12V amplitude on output we need gain: 12/2.5 = 4.8. If we turn potentiometer to the max left we get:

R1=47k/4.8=9.79k~10k. In order to get 0V amplitude we just need to turn potentiometer to the right so gain ration gets close to 0.

Signal offset regulation

And last stage of analog part is signal offset regulation. We want to regulate offset in range of -12V to 12V. The easiest way to do so is to add offset voltage to signal voltage.

Since we already have two inverting cascades we don’t want the last one to be inverted that would lead to inverted signal on output. So we are going to implement non inverting summing:

Lets see how to calculate resistor values. R6 and R7 we select to be 100k resistors as they aren’t critical while in recommended range 1k to 1M. More interesting part is gain. Lets see how non inverting summing amplifier output voltage look like:

What we see here is a voltages added and multiplied by gain. Since our R7 and R8 are equal to 100k we get that only half of these voltages are added. So we need to adjust amplifier gain to 2 in order to operate with full values. So we need:

after solving we get that both resistors have to be equal. In order to keep resistor values less scattered we also chose those to be 100k.

Putting it all together

Now we have all blocks ready and can connect them in to single circuit:

Here we have connected all four modeled parts: offset adjust, low pass filter, amplitude control, ofset control. TL074 chip comes with four operational amplifiers built in so we will get away with single chip and this functionality. If you are interested in simulating here is LTspiece simulation file. In simulator TL074 were replaced with similar TL1359 op amp, so actual results shouldn’t differ much.

And corresponding signals on various nodes:

As you can see on first stage we have a sine signal with small distortion added and its offset is 2.5V. After offset adjust signal still is distorted but it crosses 0V now. After filtering we see no more distortion – only smooth sine. Then next stage adjusts signal voltage gain and on output we get signal with selected offset somewhere at -5V. Results looks promising so next step will be to put everything in to working project. Comments and suggestions are welcome.

[via: http://www.scienceprog.com/]

 

A bubble or spirit level meter is a handy tool to find whether a surface is horizontal or vertical. It is often carried by civil engineers, mechanical engineers, surveyors, carpenters, and many other professionals whose work involve precise alignments of horizontal and vertical planes. Original spirit levels had two banana-shaped curved glass vials at each viewing point and were much more complicated to use. Mechanical spirit level meters are still available both in 1D and 2D formats. However at present time their electronic counterparts have also emerged and are even available in modern Android equipped cell phones. It’s from there I got my inspiration to make a very simple digital spirit level. Here’s a demo of such an electronic spirit level made by using a Microchip PIC16F684 micro, a SCA610 accelerometer and a handful of other discrete components.

Digital spirit level meter

Theory

The heart of this project is the SCA610 accelerometer IC that senses the inclination of a surface. Bases on VTI 3D MEMS technology, SCA610 is a very reliable, accurate and stable one-axis analog accelerometer. It requires a single power supply and provides an analog output voltage proportional to the inclination. According to its datasheet, if the device is powered with a precise +5.0V, the analog output voltages for +1g (vertical), 0g (horizontal), and -1g (vertical in opposite direction) inclinations would be 3.75V, 2.50V, and 1.25V, respectively. Voltages between 1.25V and 3.75V are linearly interpolated and mapped to the inclination angles varying from +90° to -90°. The analog signal can be processed by a microcontroller through an ADC channel to retrieve the inclination information.

Circuit diagram

The microcontroller used in this project is PIC16F684 which has just enough I/O pins and a built-in 10-bit A/D converter required for this project. The microcontroller runs at 8.0 MHz using the internal oscillator. The SCA610 sensor output goes to AN0 ADC channel of PIC16F684. The microcontroller takes quick multiple ADC samples which are averaged for a better estimation of inclination angle. There are five LEDs connected to RC0 through RC4 port pins and they are arranged in a row. Based on the direction of inclination the LEDs run in either forward or in reverse direction. There is a buzzer connected to RC5 pin which beeps continuously until the entire board settles at 0g or horizontal position. The buzzer used in the circuit is a ON/OFF type. What that means is it turns on when the RC5 pin goes high. If the device is aligned perfectly horizontal then only a central blue LED flashes and the buzzer mutes.

Circuit diagram of a simple LED spirit level meter (Click image to enlarge)

The axis direction of the SCA610 accelerometer is marked on the chip, as shown in the circuit diagram above.

Circuit setup on a breadboard

Software

The firmware for this project has been written in C using mikroElektronika’s mikroC Pro for PIC compiler. Note that the buzzer used in this project is a high impedance ON/OFF type and the software just turns the RC5 pin high in order to make the buzzer on. So this firmware won’t drive a buzzer that requires an ac signal. You can download the source code and compiled HEX output of the firmware from the following link.

Download firmware

Configuration bit settings for PIC16F684

The video below shows the project in action.

This project used a one-axis accelerometer chip and therefore, the alignment can be measured in only one direction at a time. However, the concept can be extended for simultaneous measurements of orientation in two dimensions by adding one more SCA610 sensor or just choosing a dual axis accelerometer (like ADXL202).

[via: http://embedded-lab.com/]

 

High density line of STM32 microcontrollers have quite a bunch on features that can be used in user programs. The more features you add to source the more complicated program becomes and this way it starts to be difficult to keep up with all things. Using only main loop and interrupts becomes time consuming task to manage. If you don’t want to struggle in tuning things up manually you can use one of many real time operating systems (RTOS). They are great when you need lots of separate functions to run in parallel so no task would be missed. RTOS scheduler takes care of giving each task a decent time to perform. There are lots of great RTOS systems around. Many of them are free and opensource.

It happens so that I love using FreeRTOS which has quite long history and is flexible enough to fit multiple types of hardware. You can check out my recent demo on Atmega128. I encourage you to give a try to other RTOS systems like ChibiOS, BeRTOS, and many more. But lets stick with FreeRTOS. Simply speaking FreeRTOS is quite simple and easy to use. It has practically most of features you’d look for in RTOS. Some of key features would include preemptive, cooperative and hybrid scheduler, task and co-routine support, queues, semaphores and mutexes for task synchronisation and communication. Many demos, many ports to to get started with.

We’ve done some demos for our STM32F103ZET6 board that include LEDs, Buttons, USART, and LCD. All we did was wrote some code that main loop and generated interrupts for some events. This is efficient and OK since program isn’t complicated. But if you plan your program to be big, start building it with RTOS to keep it smooth til the end.

Simply we are going to do these previous tasks but with scheduler.

First of all we need to build our template that include FreeRTOS source files. We just import FreeRTOS folder to our project tree.

Then we have to add FreeRTOSConfig.h file to project where all RTOS configurations are set. Next thing is to match interrupts handlers for systick, SVC and PendSV interrupts. They are used by scheduler and have a bit different nomenclature. If you look at port.c file you’ll see that it uses exception handlers:

voidxPortPendSVHandler( void ) __attribute__ (( naked ));voidxPortSysTickHandler( void );voidvPortSVCHandler( void ) __attribute__ (( naked ));

All we have to place them instead of

void SVC_Handler (void) __attribute__((weak));void PendSV_Handler (void) __attribute__((weak));void SysTick_Handler (void) __attribute__((weak));

In our vector table where start-up code residues.

Before we start writing code lets configure RTOS. To do so we need to edit FreeRTOSConfig.h file contents. There are lots of them but most important are following:

#define configUSE_PREEMPTION 1#define configCPU_CLOCK_HZ ( ( unsignedlong ) 72000000 )#define configTICK_RATE_HZ ( ( portTickType ) 1000 )#define configMAX_PRIORITIES ( ( unsigned portBASE_TYPE ) 5 )#define configMINIMAL_STACK_SIZE ( ( unsignedshort ) 120 )#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 18 * 1024 ) )#define configMAX_TASK_NAME_LEN ( 16 )#define configUSE_TRACE_FACILITY 1#define configIDLE_SHOULD_YIELD 1#define configUSE_MUTEXES 1#define configUSE_COUNTING_SEMAPHORES 1#define INCLUDE_vTaskPrioritySet 1#define INCLUDE_vTaskDelayUntil 1#define INCLUDE_vTaskDelay 1

just a quick overview of these. We are going to use preemption so we set it to 1, then we select cpu clock rate which is 72MHz, also we configure tick timer what means that scheduler will run every 1ms.

Then we select minimal stack size for a task. Also set total heap size.

We are going to use task priorities so we set vTaskPrioritySet to 1. Also we are going to use vTaskDelay utilities that help with task timing. So we select them to. There are lot more settings youll find in Config file. Many of them are self explanatory but be sure to check their meaning before using as setting one or another may lead to significant increase of ram or CPU usage.

Lets proceed to some code. If you are familiar with RTOS, then you know that program writen for RTOS is organized as set of independent tasks. So each tasks simply shouldn’t rely on other tasks and run withing its own context. Practically speaking task is a function with its own stack. If multiple tasks are created, scheduler switches between these tasks according to priorities set. Task itself is a function with endless loop and function should never return from it:

void vATaskFunction( void *pvParameters ){for( ;; ){-- Task application code here. --}}

I put all my tasks in separate source file mytasks.c (and mytasks.h). Ok lets start with LED flasher. This is simple routine which flashes LED every 1s. This is how tasks look like:

void vLEDFlashTask( void *pvParameters ){portTickType xLastWakeTime;const portTickType xFrequency = 1000;xLastWakeTime=xTaskGetTickCount();for( ;; ){LEDToggle(5);vTaskDelayUntil(&xLastWakeTime,xFrequency);}}

To set timing we are using vTaskDelayUntil function. FreeRTOS counts ticks everytime scheduler is called (every 1ms). By setting frequency value to 1000 we are getting 1s delay. This is simple with LEDs.

Another task would be checking button state. In order to pass button state we don’t want to use any global variables here. FreeRTOS have special means for this – binary semaphores. Thay are convenient when we need to sent a binary value between tasks. In my example I declared semaphore handlers for each button:

staticxSemaphoreHandle xButtonWakeupSemaphore = NULL;staticxSemaphoreHandle xButtonTamperSemaphore = NULL;staticxSemaphoreHandle xButtonUser1Semaphore = NULL;staticxSemaphoreHandle xButtonUser2Semaphore = NULL;

then within vButtonCheckTask before main loop created semaphores to be used:

vSemaphoreCreateBinary(xButtonWakeupSemaphore);vSemaphoreCreateBinary(xButtonTamperSemaphore);vSemaphoreCreateBinary(xButtonUser1Semaphore);vSemaphoreCreateBinary(xButtonUser2Semaphore);

Once sempahores are created succesfully we can start using them. There are several functions available for manipulating semaphores. This time we are going to use two of them: Give and take. So when we check button and detect that it was pressed we give semaphore which is something like setting bolean value to ’1′:

 if (ButtonRead(BWAKEUPPORT, BWAKEUP)==pdTRUE){count++;if(count==DEBOUNCECOUNTS){xSemaphoreGive(xButtonWakeupSemaphore);count = 0;}}

Semaphores stay set until they are taken. Other function have to take semaphores in order to reset. For this I created another task which toggles particular LED when button is pressed:

void vButtonLEDsTask( void *pvParameters ){portTickType xLastWakeTime;const portTickType xFrequency = 100;xLastWakeTime=xTaskGetTickCount();for( ;; ){if((xButtonWakeupSemaphore!=NULL)){if (xSemaphoreTake(xButtonWakeupSemaphore, (portTickType)10)==pdTRUE){LEDToggle(1);//give semaphore backxSemaphoreGive(xButtonWakeupSemaphore);}}vTaskDelayUntil(&xLastWakeTime,xFrequency);}}

In this shortened version of task you can see how single semaphore is taken. If statement checks if semaphore was given. If condition is met then LED is toggled. XsemaphoreTake function also resets semaphore automatically. As you can see after toggling LED I give semaphore back because Ialso plant to detect button press withing another task. This is vLCDTask:

void vLCDTask( void *pvParameters ){extern uint8_t Image_Table[];portTickType xLastWakeTime;const portTickType xFrequency = 100;xLastWakeTime=xTaskGetTickCount();LCD_SetDisplayWindow(00, 00, 239, 319);LCD_Clear(Black);LCD_DisplayStringLine(Line4,(char*)pcLCDTaskStartMsg, White, Black);for(;;){if((xButtonWakeupSemaphore!=NULL)&&(xButtonTamperSemaphore!=NULL)&&(xButtonUser1Semaphore!=NULL)&&(xButtonUser2Semaphore!=NULL)){if (xSemaphoreTake(xButtonWakeupSemaphore, (portTickType)10)==pdTRUE){LCD_Clear(Blue);LCD_WriteBMP_Dim(30, 30, 210, 210, Image_Table);}}vTaskDelayUntil(&xLastWakeTime,xFrequency);}}

With button presses I trigger different things to display on LCD. You have to be careful by using such technique because you cant predict that LCD task will be called after LED task. It can happen that first will be LCD task and there fore LED won’t be toggled as semaphore will be taken. Probably it would be better to create two semaphores for each task.

Last task I am using here is USART task which sends and receives vie terminal. With USART things start to be more complicated. Because we don’t want to miss any received character and be sure that all messages have been sent. This can be achieved by using interrupt based transferring. Doing this in task where scheduler runs every 1m would definitely lead to loos of data. Another issue is to ensure buffering so that data could be retained until task is called. Who knows there might be 100 characters received before task runs and serves data. So there is another nice FreeRTOS feature – queues. These are like predefined lengths buffers to store any type of messages. To start using queues we simply declare two queue handlers:

xQueueHandle RxQueue, TxQueue;

one for receiving and another for transmitting. Within task we then create both queues with xQueueCreate function where we put queue length as first parameter and size of message which in our case will be char size (8-bit).

void vUSARTTask( void *pvParameters ){portTickType xLastWakeTime;const portTickType xFrequency = 50;xLastWakeTime=xTaskGetTickCount();char ch;// Create a queue capable of containing 128 characters.RxQueue = xQueueCreate( configCOM0_RX_BUFFER_LENGTH, sizeof( portCHAR ) );TxQueue = xQueueCreate( configCOM0_TX_BUFFER_LENGTH, sizeof( portCHAR ) );USART1PutString(pcUsartTaskStartMsg,strlen( pcUsartTaskStartMsg ));for( ;; ){//Echo backif (Usart1GetChar(&ch)){Usart1PutChar(ch);}vTaskDelayUntil(&xLastWakeTime,xFrequency);}}

Usart1GetChar function simply pulls out char value out of queue as follows:

uint32_t Usart1GetChar(char *ch){if(xQueueReceive( RxQueue, ch, 0 ) == pdPASS){return pdTRUE;}return pdFALSE;}

While Usart1PutChar sends char to queue:

uint32_t Usart1PutChar(char ch){if( xQueueSend( TxQueue, &ch, 10 ) == pdPASS ){USART_ITConfig(USART1, USART_IT_TXE, ENABLE);return pdTRUE;}else{return pdFAIL;}}

The rest is left for interrupt handler which simply responds to interrupt requests and either sends bytes from TxQueue or receives bytes and places them to RxQueue.

void USART1_IRQHandler(void){long xHigherPriorityTaskWoken = pdFALSE;uint8_t ch;//if Receive interruptif (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){ch=(uint8_t)USART_ReceiveData(USART1);xQueueSendToBackFromISR( RxQueue, &ch, &xHigherPriorityTaskWoken );}if (USART_GetITStatus(USART1, USART_IT_TXE) != RESET){if( xQueueReceiveFromISR( TxQueue, &ch, &xHigherPriorityTaskWoken ) ){USART_SendData(USART1, ch);}else{//disable Transmit Data Register empty interruptUSART_ITConfig(USART1, USART_IT_TXE, DISABLE);}}portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );}

it important to know that within interrupt handler special functions has to be used xQueueReceiveFromISR and xQueueSentoBackFromISR.

After having all tasks prepared we can add to scheduler queue in our main source file:

//STM32F103ZET6 FreeRTOS Test#include "stm32f10x.h"//#include "stm32f10x_it.h"#include "mytasks.h"//task priorities#define mainLED_TASK_PRIORITY( tskIDLE_PRIORITY )#define mainButton_TASK_PRIORITY( tskIDLE_PRIORITY )#define mainButtonLEDs_TASK_PRIORITY( tskIDLE_PRIORITY + 1 )#define mainLCD_TASK_PRIORITY( tskIDLE_PRIORITY )#define mainUSART_TASK_PRIORITY( tskIDLE_PRIORITY )#define mainLCD_TASK_STACK_SIZE configMINIMAL_STACK_SIZE+50#define mainUSART_TASK_STACK_SIZE configMINIMAL_STACK_SIZE+50int main(void){//init hardwareLEDsInit();ButtonsInit();LCD_Init();Usart1Init();xTaskCreate( vLEDFlashTask, ( signed char * ) "LED", configMINIMAL_STACK_SIZE, NULL, mainLED_TASK_PRIORITY, NULL );xTaskCreate( vButtonCheckTask, ( signed char * ) "Button", configMINIMAL_STACK_SIZE, NULL, mainButton_TASK_PRIORITY, NULL );xTaskCreate( vButtonLEDsTask, ( signed char * ) "ButtonLED", configMINIMAL_STACK_SIZE, NULL, mainButtonLEDs_TASK_PRIORITY, NULL );xTaskCreate( vLCDTask, ( signed char * ) "LCD", mainLCD_TASK_STACK_SIZE, NULL, mainLCD_TASK_PRIORITY, NULL );xTaskCreate( vUSARTTask, ( signed char * ) "USART", mainUSART_TASK_STACK_SIZE, NULL, mainUSART_TASK_PRIORITY, NULL );//start schedulervTaskStartScheduler();//you should never get herewhile(1){ }}

As you can see we have created 5 tasks. Each of them have their own priority level and stack size. Probably hardest part is to define proper stack size it it’s to small it may crash your program, it its too large, then we wasting resources. To detect stack overflow you can use special function called

voidvApplicationStackOverflowHook( xTaskHandle *pxTask, signed portCHAR *pcTaskName )

where you can set your indicators like LED flash when ever stack overflow occurred. Then you can increase task stack and start again.

Hopefully you find this part useful and interesting. As always comments and questions are welcome.

Working source code fro Codesourcery with Eclipse IDE is here: STM32F103ZET6FreeRTOS

[via: http://www.scienceprog.com/]

© 2011 Geko Geek This is a news aggregator website. Articles and images are copyrighted to their original source authors. Gekogeek takes no responsibility about the articles content Suffusion theme by Sayontan Sinha