| | | | | | | WORLD OF ELECTRO | | | | | | | | | | | | | | | | In Part 1 of this tutorial, we discussed about Sensirion's SHT1x and SHT7x series of humidity sensors, their interface specifications, the communication protocol used for transferring data in and out of the sensor, and the equations to convert their digital outputs to actual physical quantities. These sensors are capable of measuring temperature along with relative humidity and provide outputs in fully-calibrated digital words. We will now see how a PIC microcontroller can be programmed to communicate with these sensors, read the temperature and relative humidity data, and display the information on a character LCD. Circuit setup on breadboard Circuit Diagram We will be interfacing the SHT11 and SHT75 sensors simultaneously to different port pins of PIC18F2550 and display the measured values of relative humidity and temperature from both on a 16×2 character LCD. The two sensors are placed next to each other and are supposed to measure the same values for relative humidity and temperature. The circuit diagram below shows the connection of the two sensors and a 16×2 character LCD to StartUSB for PIC board. StartUSB for PIC is a small development board for PIC18F2550 from mikroElektronika. The microcontroller comes preprogrammed with a bootloader, and therefore, no external programmer is required. Connecting a LCD and SHT sensors to StartUSB for PIC board The LCD is operating in 4-bit mode, where the data pins D4-D7 are driven by RB4-RB7 pins of PIC18F2550. The RS and E pins are connected to RC6 and RC7 respectively. The clock lines of SHT11 and SHT75 are driven separately by RB1 and RB3 pins of PIC18F2550, respectively. On the other hand, the RB0 and RB2 pins are connected to the data pins (SDA or DATA) of SHT11 and SHT75, respectively. A decoupling capacitor (100 nF) is connected between Vcc and Gnd pins of each sensor. The PIC18F2550 microcontroller on-board StartUSB for PIC board runs at 48 MHz using the internal PLL along with an external 8.0 MHz crystal. A pull-up resistor of 10 K is required for each DATA line (in fact, the SHT11 board from mikroElektronika has pull-up resistors of 1 K on both DATA and SCK lines, but the manufacturer's datasheet recommends 10 K). Software The sensor's serial communication protocol was described in Part 1. We will implement it for PIC18F2550 using mikroC Pro for PIC. MikroElektronika provides a sample code written in mikroC for reading temperature and relative humidity from a SHT11 sensor. This code was later modified by Uroš Pešović (from Serbia) in order to account for the new conversion coefficients released by Sensirion for its version 4 sensors. I am adapting the same code for dual sensor case with some modifications that are required for our specific case. The following subroutine is to reset the interface, in case the communication to the sensor is lost. In our case this routine will be always called before sending a new measurement command to the sensor. In order to pull the DATA line high, all you need is to define the direction of PIC port pin driving the DATA line as input. The pull-up resistor will then pull the line high. However, the same port pin direction must be defined as output and set to logic '0′ to pull the line low. void SHT_Reset() { SCL = 0; // SCL low SDA_Direction = 1; // Define SDA as input to pull the DATA line high for (i = 1; i <= 10; i++) // repeat 18 times SCL = ~SCL; // invert SCL } Once the connection is reset, you need to send a Start Transmission sequence. The subroutine for this would be something like this. void Transmission_Start() { SDA_Direction = 1; // Pull SDA high SCL = 1; // Clock high Delay_1us(); // 1 us delay SDA_Direction = 0; // SDA as output SDA = 0; // DATA low Delay_1us(); // 1us delay SCL = 0; // Clock low Delay_1us(); // 1us delay SCL = 1; // Clock high Delay_1us(); // 1 us delay SDA_Direction = 1; // SDA as input, DATA is high Delay_1us(); // 1us delay SCL = 0; // SCL low } As we discussed in Part 1 of this tutorial that the microcontroller is required to send an acknowledge pulse on receiving a data byte from the sensor, the following routine takes care of that. void MCU_ACK() { SDA_Direction = 0; // define SDA as output SDA = 0; // DATA low SCL = 1; // Clock high Delay_1us(); // 1us delay SCL = 0; // Clock low Delay_1us(); // 1us delay SDA_Direction = 1; // DATA high } The following subroutine sends a measurement command to the sensor, then waits till the measurement is finished, and finally receives the two byte measurement readings. long int Measure(short command) { j = command; // j = command (0x03 or 0x05) SHT_Reset(); // Reset interface Transmission_Start(); // procedure for sTmprting transmission k = 0; // k = 0 SDA_Direction = 0; // define SDA as output SCL = 0; // SCL low for(i = 1; i <= 8; i++) { // repeat 8 times if (j.F7 == 1) // if bit 7 = 1 SDA_Direction = 1; // make DATA line high else { // else (if bit 7 = 0) SDA_Direction = 0; // set DATA line as output and SDA = 0; // pull DATA line low } Delay_1us(); // 1us delay SCL = 1; // SCL high Delay_1us(); // 1us delay SCL = 0; // SCL low j <<= 1; // move contents of j one place left // until all 8-bits of command are sent } // Wait until the DATA line is pulled low by the sensor SDA_Direction = 1; // define SDA as input SCL = 1; // SCL high Delay_1us(); // 1us delay SCL = 0; // SCL low Delay_1us(); // 1us delay while (SDA == 1) // while DATA is high, do nothing Delay_1us(); // 1us delay // Read 2 bytes of measurement data after it is ready for (i = 1; i <=16; i++) { // repeat 16 times k <<= 1; // move contents of k one place left SCL = 1; // SCL high if (SDA == 1) // if DATA is high k = k | 0x0001; // set the corresponding bit of K to 1 SCL = 0; if (i == 8 ) // Send an acknowledge after each byte MCU_ACK(); } return k; // Return the measurement data } Once you get the two-byte measurement output from the sensor, you can use the conversion coefficients provided in the datasheet to retrieve the temperature or relative humidity. This part is just a mathematical manipulation and hence not described in here. The complete code for this project is however, downloadable from the link provided below. Download complete mikroC project files Important note The datasheet recommends to use a 10K pull-up resistor to the DATA line, and the SCK line may work fine without a pull-up resistor. However, the SHT11 proto board from mikroElektronika has got two 1 K resistors to pull-up both SCL and SDA lines. The resistors dissipate some heat when the lines are pulled low. This may introduce error in the measurement of both temperature and relative humidity, as the resistors are in the close proximity of the sensor as all of them are on the same board. In order to minimize this heat dissipation, the port pins of microcontrollers that drive SCK and SDA lines, must be defined as input pins, while in idle state. If you leave them as outputs with logic 0 states, there will be current continuously flowing through the pull-up resistors, which will dissipate more heat and raise the temperature of the sensor. Output The picture below shows the two sets of temperature and relative humidity readings shown on the LCD, which were measured by SHT11 and SHT75 sensors. The temperature values are consistent within 0.1 °C. However, the SHT11 seems to be off from SHT75 by 1.2 % in the measurement of relative humidity. These readings are taken right after the circuit is powered on. Temperature and relative humidity readings from two sensors The sensors are scheduled to take the measurements in every 2 sec. Because of heat dissipation in the resistors on-board, the temperature measurement from SHT11 module slowly increases and drifts apart from that of SHT75. SHT11 drifting apart from SHT75 because of heat dissipation The drifting of SHT11 becomes more severe if you don't change the SCK pin direction to input while it is in idle condition. See the picture below where the SHT11 measurement of the temperature is more than 2 °C higher than that of SHT75. This drift occurred within less than first two minutes after the circuit is powered on. Here, you can also see the inverse relationship between temperature and humidity. With increasing temperature, the relative humidity is decreasing. Temperature of the SHT11 module increases significantly due to power dissipation in the pull-up resistors during idle conditions Conclusion Sensirion's SHT series of humidity sensors are very handy in measuring both relative humidity and temperature because of on-board signal processing and providing fully calibrated digital outputs. They communicate with a host controller through two wire serial bus: DATA and SCK. The DATA line requires an external pull-up resistor that dissipates some power as heat when the DATA line is pulled low. Therefore, care must be taken, specially for surface mount type sensor like SHT1x, in placing the resistor along with the sensor on the same module. The port pins of the microcontroller driving the clock and data lines must be set as input during idle conditions to minimize the heat dissipation in the pull-up resistors. References: Further suggestions SHT1x/7x sensors cannot measure dew point directly, but it can be derived from the humidity and temperature measurements. The datasheet provides the equation and necessary coefficients required to calculate dew point. There are some other details such as the stability of sensor, normal operating conditions, wiring considerations, etc. that are not discussed here, but readers can find those information in the datasheet. Related posts: - Humidity and temperature measurements with Sensirion's SHT1x/SHT7x sensors (Part 1)
- Testing active analog temperature sensors with a multimeter
- A Digital temperature meter using an LM35 temperature sensor
| | | | | | | | | | | | | | | | | | | | | Temperature and relative humidity are two very important ambient parameters that are directly related to human comfort. Sometimes, you may be able to bear higher temperatures, if there is a lower relative humidity, such as in hot and dry desert-like environment. However, being in a humid place with not very high temperature may make you feel like melting. This is because if there is high relative humidity, sweat from our body will evaporate less into the air and we feel much hotter than the actual temperature. Humidifiers and dehumidifiers help to keep indoor humidity at a comfortable level. Today we will discuss about Sensirion's SHT series of digital sensors, more specifically SHT11 and SHT75, which are capable of measuring both temperature and relative humidity and provide fully calibrated digital outputs. We will interface both the sensors to PIC18F2550 microcontroller and compare the two sets of measurements to see the consistency between the two sensors. This tutorial is divided into two parts. The first part will cover all the details regarding the sensors, including their specification, interface, and communication protocol. The second part will be more focussed on the circuit diagram, implementation of the communication protocol with PICMicro, and the results. Relative humidity and temperature measurements with SHT1x and SHT7x sensors Theory Sensirion offers multiple SHT series of digital sensors for measuring both relative humidity and temperature. The temperature is measured using a band-gap sensor, whereas the humidity sensor is capacitive; which means the presence of moisture in air changes the dielectric constant of the material in between the two plates of a parallel-plate capacitor, and hence varies the capacitance. The required signal conditioning, analog-to-digital conversion, and digital interface circuitries are all integrated onto the sensor chip. The various SHT series sensors have different levels of accuracy for humidity and temperature measurements, as described below. SHT1x, 2x, and 7x series of humidity sensors (Source: http://www.sensirion.com) SHT1x are available in surface mount type whereas SHT7x are supplied with four pins which allows easy connection. The SHT11 and SHT75 sensors both provide fully calibrated digital outputs that can be read through a two-wire (SDA for data and SCK for clock) serial interface which looks like I2C but actually it is not compatible with I2C. An external pull-up resistor is required to pull the signal high on the SDA line. However, the SCK line could be driven without any pull-up resistor. The signaling detail of the serial bus is described in the datasheet, which we will implement for PIC18F2550 microcontroller using mikroC pro for PIC compiler. The operating range of both the sensors is 0 to 100% for relative humidity, and -40.0 to 123.8 °C for temperature. The sensor consumes 3 mW power during measurement, and 5 μW, while in sleep mode. The SHT11 module that I have got is from mikroElektronika. The sensor (SMD) is soldered on a tiny proto board with all the four pins accessible through a standard 0.1 inch spacing male header. The board comes with pull-up resistors connected to both SDA and SCK lines. One concern in this type of arrangement is the heat dissipated by the pull-up resistors could affect the measurements if the resistors and the sensor are close in the board. We will discuss about this issue later too. The SHT75 module from Sensirion, however, does not include any pull-up resistor for SDA line and therefore must be included externally. SHT11 proto board from mikroElektronika comes with pull-up resistors for both SDA and SCK lines Pin descriptions of SHT75 Brief description of SHT1x/7x sensors Please read the datasheets for SHT1x and SHT7x for detail information about these sensors. I am only providing a brief summary here. SHT11 and SHT75 are functionally same with SHT75 being more accurate (±1.8% vs ±3%) in measuring relative humidity. Both the sensors can operate from 2.4-5.5 V supply voltage, however the datasheet recommends to use 3.3V for highest accuracy. The default measurement resolution is 14-bit for temperature and 12-bit for relative humidity, which can be reduced to 12- and 8-bit respectively by changing the bit settings of the Status Register (discussed later) inside the sensor chip. We will be using the default resolution settings for measurements. SCK is the clock line that is used to synchronize the communication between the micrcontroller and the sensor. It is an input only pin on the sensor's side and therefore the microcontroller should be responsible to generate the clock signal. DATA or SDA is a bidirectional data transfer pin for sending data in and out of the sensor. The sensor should receive a conversion command from the microcontroller in order to start measuring temperature or relative humidity. The measurement commands for relative humidity and temperature are 00000101 (05H) and 0000011 (03H), respectively [The first three most-significant bits are actually the address bits, which are always zero for SHT1x and SHT7x sensors, and the remaining 5 bits are the command bits]. Prior to sending a command, a Transmission Start sequence must be issued by the microcontroller which consists of a lowering of the DATA line while SCK is high, followed by a low pulse on SCK and raising the DATA line high again, while the SCK is already high. Transmission Start signal After receiving a command from microcontroller, the sensor issues an acknowledge pulse by pulling the DATA line low for one clock cycle. It takes place after the falling edge of the 8th clock (corresponding to the 8th bit of command sent) on the SCK line, and the DATA line is pulled low until the end of the 9th clock on the SCK line. After issuing a measurement command ('00000101' for relative humidity, '00000011' for temperature) the microcontroller has to wait for the measurement to complete, which takes a maximum of 20/80/320 ms for a 8-/12-/14-bit measurement. During this time, the microcontroller can stop generating clocks on SCK line, and release DATA line. Upon the completion of measurement, the sensor generates a Data Ready signal by pulling the DATA line low. Upon receiving the Data Ready signal, the microcontroller can restart the clock on SCK line to readout the measurement data. The measurement data is kept stored into the memory of the sensor until readout. The readout involves two bytes of measurement data and one byte of CRC checksum. The transmission of data is secured by an 8-bit checksum which is optional. The microcontroller must acknowledge each byte by pulling the DATA line low. The bytes are transferred with MSB first, and are right justified. For 8-bit measurement, the first byte is not used. If CRC-8 checksum is not used the microcontroller may terminate the communication by keeping ACK high after receiving the LSB of the measurement data. The device automatically returns to Sleep Mode after measurement and communication are completed. The figure below is taken from the datasheet of SHT7x and it shows the sequence of relative humidity measurement . Source: SHT7x datasheet While sending a command to SHT sensor, DATA is valid on the rising edge of the serial clock (SCK) and must remain stable while SCK is high. However, for reading data from the sensor, DATA is valid after SCK has gone low and remains valid until the next falling edge of SCK. Status register This is an 8-bit register inside SHT1x/SHT7x sensors that controls some of the advanced functions such as selecting measurement resolution, end-of-battery notice, OTP reload or using the on-chip heater. This register is both readable and writable through appropriate commands. The individual bit definition of the status register is shown below: Status Register bit definition (from SHT7x datasheet) The commands to read and write to Status Register are 00000111 (07H) and 00000110 (06H), respectively. In order to write a byte to Status Register, the microcontroller must send the write command (06H) followed by the data byte to be written. The sensor must generate an acknowledge signal upon receiving both the command and the data byte. The calibration coefficients of the sensor are pre-programmed inside a small on-board one-time programmable (OTP) memory. These coefficients are used internally by the sensor during measurements. Clearing bit 1 of Status Register disables loading of the calibration coefficients on each measurement and therefore, speeds up the conversion process. The sensor has also got an on-chip heating element that can be turned on by setting the bit 2 of Status Register. If the heater is turned on, the temperature of the sensor may increase by 5-10 °C. The on-chip heater can be helpful to study the effect of raising the temperature on the measurement of relative humidity. Bit 6 of Status Register is a read-only flag, which is set to 1 when the supply voltage of the sensor drops below 2.47 V. Connection Reset sequence If communication between the microcontroller and the sensor is disrupted for some reasons, the following signal sequence may be used to reset the interface. Remember that only the interface is reset, and the value of Status Register is preserved. The reset sequence consists of toggling the SCK line nine or more times, while keeping the DATA line high. Connection reset sequence (from SHT7x datasheet) Conversion coefficients for relative humidity and temperature In order to convert the digital readouts from the sensor into appropriate physical quantities (relative humidity and temperature), the datasheet provides optimized conversion coefficients and equations. The humidity sensor is non-linear by design and therefore requires a second order conversion equation, whereas the temperature sensor is pretty linear. At temperatures significantly different from 25°C (~77°F) the humidity signal requires correction to compensate the effect of temperature. The correction coefficients and equation are also provided in the datasheet. Equation for computing relative humidity (from SHT7x datasheet) Temperature compensation coefficients for RH (from SHT7x datasheet) Temperature conversion coefficients (from SHT7x datasheet) Related posts: - Testing active analog temperature sensors with a multimeter
- A Digital temperature meter using an LM35 temperature sensor
| | | | | | | | | | | | | | | | | | | | | Measurement of light intensity is a prime necessity in several occasions. The diversity of such needs make their way to various branches of physics and engineering as well as in media. For instance, in engineering, such kinds of measurements are needed to design optimum lighting conditions of a room. In photography, light intensity measurements ensure good quality pictures by determining the right exposure. Wiring a phototransistor or a light-dependent-resistor (LDR) with an analogue LED voltmeter chip like the LM3914 or even to a microcontroller and displaying the ADC values is a pretty simple technique of measuring light intensity. The bad part of this technique is that these simple and amateur-level devices can only measure relative intensity of light and are unable to provide measurements on an absolute scale. However, with a precise knowledge of the transfer characteristic (resistance vs light intensity) of the LDR it is possible to relate the LDR output to the intensity of light in standard unit. In case the LDR characteristic is unknown or unreliable, you can still calibrate the sensor output by using a variable light source and an external reference photometer. This project is about a microcontroller based light intensity meter where an LDR light sensor is calibrated against an external photometer to obtain the intensity of incoming light in the unit of lux. The lux is the SI unit of illuminance and luminous emittance, and measures lumens per square meter (lm/m2). The microcontroller used in this project is ATMega8L and its firmware is written using mikroElektronika's MikroC Pro for AVR compiler. AVR based LUX meter Circuit diagram The project's hardware is simple and requires little explanation. I used my custom-built 28 pin AVR demo board that provides all the necessary interfaces. An LDR is used as a light sensing device. I could have used advanced light sensors like TSL257 or TSL230 but they are pretty expensive and rare. A photo transistor is also a good option but photo transistors and photo diodes are very responsive to rapidly changing signals which is not desired here. Besides, ordinary photo transistors and diodes are more specific to a particular wavelength and therefore may not be equally sensitive to the same intensity of different wavelengths. According to the schematic shown below, an LDR and a precision analogue potentiometer form an adjustable light-dependent voltage divider. The output of this divider goes to a 3rd order analog low pass filter (LPF). This filter is designed with Texas Instrument's (TI) TL072 dual Op-Amp and allows frequencies lower than 100Hz to pass and stops anything above this value. The LPF is here needed to reduce noise and unwanted high frequency transitions due to sudden flickering lights, glare, pulsating light sources and so on. Op-Amp Filter stage Next the output from the LPF is fed to the first analog input channel ADC0 (pin C0). Additional signal conditioning is done inside the microcontroller by performing the root-mean square (R.M.S) of the ADC samples. Thus both analogue and digital filtering are done. The result of such dual filtering is a highly processed signal which is reasonably reliable and accurate than with a direct connection to the ADC pin. However complexities arise both at hardware and software ends. The rest of the works after signal conditioning are done inside the AVR chip itself and in the software end. A 16×2 alphanumerical LCD is connected to PORTB of the AVR micro to show the measured light intensity level in lux. It should be noted that the AVR runs at 8.0 MHz clock derived from its internal source. Microcontroller and LCD circuit Op-Amp circuit on breadboard Software As stated earlier the coding for the Atmega8L is done with MikroC Pro for AVR compiler from Mikroelektronika. The internal 8MHz RC oscillator is used as the clock source for the Atmega8L. The chip was programmed with low fuse byte of value 0XE4 and high fuse byte of value 0xC9. The lock fuse bytes were unused as they were not necessary. The code was written in a straight-forward manner. The main function first initializes the required variables, registers and library functions. In the main loop, the RMS value of 512 ADC samples is computed and based on a matching "if" condition, the corresponding lux value is determined and displayed. #define no_of_samples 512 sbit LCD_RS at PORTB2_bit; sbit LCD_EN at PORTB3_bit; sbit LCD_D4 at PORTB4_bit; sbit LCD_D5 at PORTB5_bit; sbit LCD_D6 at PORTB6_bit; sbit LCD_D7 at PORTB7_bit; sbit LCD_RS_Direction at DDB2_bit; sbit LCD_EN_Direction at DDB3_bit; sbit LCD_D4_Direction at DDB4_bit; sbit LCD_D5_Direction at DDB5_bit; sbit LCD_D6_Direction at DDB6_bit; sbit LCD_D7_Direction at DDB7_bit; static void setup(); unsigned int filter_adc(); void main() { unsigned int disp_avg[4]; unsigned int avg=0; unsigned long lux_value=0; setup(); do { avg=filter_adc(); if(avg>=0 && avg<=286) { lux_value=0; } else if(avg>286 && avg<410) { lux_value=1600; } else if(avg>=410 && avg<470) { lux_value=3200; } else if(avg>=470 && avg<500) { lux_value=4800; } else if(avg>=500 && avg<510) { lux_value=6400; } else if(avg>=510 && avg<520) { lux_value=8000; } else if(avg>=520 && avg<530) { lux_value=9600; } else if(avg>=530 && avg<535) { lux_value=11200; } else if(avg>=535 && avg<540) { lux_value=12800; } else if(avg>=540 && avg<545) { lux_value=14400; } else if(avg>=545 && avg<550) { lux_value=16000; } else { Lcd_Out(2,6,"Max Limit! "); } LongToStr(lux_value,disp_avg); Lcd_Out(2,6,disp_avg); }while(1); } static void setup() { PORTB=0x00; DDRB=0x00; PORTC=0x00; DDRC=0x00; PORTD=0x00; DDRD=0x00; TCCR0=0x00; TCNT0=0x00; TCCR1A=0x00; TCCR1B=0x00; TCNT1H=0x00; TCNT1L=0x00; ICR1H=0x00; ICR1L=0x00; OCR1AH=0x00; OCR1AL=0x00; OCR1BH=0x00; OCR1BL=0x00; ASSR=0x00; TCCR2=0x00; TCNT2=0x00; OCR2=0x00; TIMSK=0x00; UCSRB=0x00; ACSR=0x80; SFIOR=0x00; ADCSRA=0x00; SPCR=0x00; TWCR=0x00; Lcd_Init(); Lcd_Cmd(_LCD_CLEAR); Lcd_Cmd(_LCD_CURSOR_OFF); ADC_Init(); delay_ms(100); LCD_Out(1, 1, "AVR Lux Meter"); LCD_Out(2, 1, "Lux: "); } unsigned int filter_adc() { register float adc_value=0; register unsigned long tmp_value=0; unsigned int sample=0; while(sample<no_of_samples) { adc_value=(adc_read(0)); tmp_value=(tmp_value+((unsigned long)(adc_value*adc_value))); delay_us(20); sample++; } adc_value=(tmp_value/no_of_samples); tmp_value=(sqrt(adc_value)); return tmp_value; } Download complete source code and HEX files Calibration of LDR output You must be wondering how did I come up with the numbers inside the "if"checks in the program mentioned above. I calibrated the output of LDR in the multiples of 1600 lux using a reference photometer and a varying source of light. The photometer I used was Model LM631. For a varying light source I used a white filament bulb with an electronic regulator that changes the voltage across the bulb to control the light intensity. The photometer and the LDR are both placed equidistant from the light source. I varied the intensity of light in the step of 1600 lux with the aid of the photometer reading. For each setting I noted the ADC output for the incoming input voltage from the Op-Amp filter stage. Of course I wrote a separate program for this that displayed ADC count on the LCD. Later I implemented those numbers in the main program to convert the ADC counts back to lux. Displaying light intensity in lux Important note The purpose of this project was to demonstrate a technique of building a digital lux meter using a simple LDR which was calibrated against a reference photometer. It should be kept in mind that the calibration numbers used in this project are not universal and may not be applied to other LDRs. You have to find out the right numbers for your LDR in the similar way as described here. No related posts. | | | | | | | | | | | | | | | | | | | | | It is a very simple data logger project based on PIC12F683 microcontroller. The microcontroller reads temperature values from a temperature sensor on a regular interval basis and stores them into its internal EEPROM memory. The recorded temperatures can be later transferred to a PC through serial interface. I originally published this project on electronics-lab.com last summer. I thought this could be a very good learning project for beginners, and so I am posting it here for Embedded Lab's readers too. Finished temperature logger powered from a 9V battery Theory The sensor used in this project is DS18B20. It is a digital temperature sensor manufactured by Dallas Semiconductor (now MAXIM) that can measure temperature ranging from -55°C to +125°C with an accuracy of ±0.5°C over the range of -10°C to +85°C. The sensor provides the measured temperature (°C) output in user-configurable 9, 10, 11, or 12-bit data corresponding to the desired resolution of 0.5, 0.25, 0.125, and 0.0625 °C. The sensor communicates with a host microcontroller over a 1-wire bus. Readers are suggested to refer the datasheet on Maxim's website for details on this sensor. Please keep in mind that there are three versions of this sensors, namely DS1820, DS18S20, and DS18B20, and they have some architectural differences. All of them have the same pin configuration and therefore the circuit diagram would be same for all three types. However, some modification in the software may be required while the microcontroller reads the temperature data from them. PIC12F683 has 256 bytes of internal EEPROM. Each temperature reading is stored as a byte, which means only the eight most significant bits of DS18B20 output is recorded. Therefore, the temperature resolution is decreased down to 1 °C. This temperature logger can store up to 254 temperature values (254 bytes) in its internal EEPROM. The remaining two EEPROM locations are used to store the sampling time and number of samples information. Three tact switches are used to provide user inputs for controlling the operation of the data logger. Circuit Diagram The PIC microcontroller uses its internal clock source operated at 4.0 MHz. The DS18B20 sensor is interfaced to GP0 pin (7) of the microcontroller. An LED connected to the GP2 pin serves as the only display in the circuit to indicate various actions of the data logger. For example, it blinks every time a sample is recorded into EEPROM. The circuit is powered with +5 V derived from a 9V battery using an LM78L05 regulator IC. The LM78L05 circuit is a very common circuit and therefore, it is not shown here. Circuit diagram of data logger The three tact switches provide the following functions. - Start: Starts data logging
- Stop: Stops the logging procedure
- Send/Reset: Transfers data to PC through serial port. However, if it is held pressed for 2 sec or more, the EEPROM locations are cleared and ready for new recordings.
Selection of sampling time This data logger offers three options for sampling interval: 1 sec, 1min, and 10 min. The selection is made through the same three tact switches. Here is how it works. Suppose if 10 min sampling time is needed, then first turn OFF the power, hold the 'Send/Reset' button pressed, turn the power ON, and wait till the LED glows. Once the LED glows, release the button, and the sampling interval is set to 10 min. The new sampling time will be updated to EEPROM location 0 so that in case of power failure, the previous sampling time will be restored. Similarly, the use of 'Start' or 'Stop' button instead of the Send/Reset one sets the sampling time to 1 sec, or 1 min respectively. With 10 min sampling interval, this data logger can record temperature samples over 42 hours. Serial Interface to PC Transferring data to PC through serial port requires a voltage translation circuit to convert the TTL logic levels from PIC12F683 to appropriate RS232 voltage levels. A regular PNP transistor with few other passive components can do this job. The RS232 standard uses a -3 to -12 V for Logic 1 and +3 to +12 V for Logic 0. The required negative voltage is stolen from the TX pin of the RS232 port on PC's side which is unused as there won't be any data transfer from the PC to PIC12F683. Under idle condition the TX pin on PC's side is held high (-12 V). The two figures below describe the operation of converting TTL levels for 1 and 0 to corresponding RS232 levels. The positive terminal of the 10 uF capacitor is grounded because its negative terminal has to deal with a more negative voltage. Translating TTL Logic 1 level to RS232 Logic 1 level Translating TTL Logic 0 level to RS232 Logic 0 level Finished project board Software The firmware for PIC12F683 is developed in C using mikroC Pro for PIC compiler from mikroElektronika. PIC12F683 doesn't have built-in hardware UART module for serial communication but the mikroC compiler has built-in library routines to implement software UART through any digital I/O pin of PIC microcontroller. Due to wide use of DS18B20 sensor for temperature measurements, mikroC also provides 1-wire library routines for controlling the operation of this sensor. The built-in functions for communicating with a 1-wire sensor are, - Ow_Reset is used for reseting sensor;
- Ow_Read is used for receiving data from sensor; and
- Ow_Write is used for sending commands to sensor.
MikroC also provides EEPROM library for read and write operations to internal EEPROM locations. The overall programming is made much easier with the help of these built-in library functions of mikroC. The source code with HEX files can be downloaded from the links below. MikroC project for ds18b20 sensor MirkoC project for ds1820 sensor The configuration bits setup for PIC12F683 can be done through Edit Project window in mikroC. The figure below shows the required settings for this project. Programming sequence When the circuit is powered on, the LED blinks for 3 times that shows the data logger is turned on and being initiated. The program checks for any key pressed for setting sampling interval. If yes, it identifies the key and store the appropriate sampling interval information to EEPROM location 0. The LED is turned on to indicate that the sampling interval has been set and it's time to release the pressed key. The program is now inside the main loop. The three switches (Start, Stop and Send/Reset) operate in Interrupt-on-change mode, which means, any time the button is pressed, an interrupt is generated. Pressing the Start button begins data recording. Each time a temperature sample is recorded in to the EEPROM, the LED blinks to indicate that the logging process is going on. Pressing the Stop button will interrupt this process, while Send will initiate the serial transfer of recorded samples through GP1 pin (6). If the Send button is pressed for more than 2 sec, the entire EEPROM locations will be cleared. On PC's side, the hyperterminal program is used to receive logged temperature data from PIC12F683. The rate of data transfer is set to 9600 baud on both end. The figure below shows the hyperterminal settings for this project. Hyperterminal settings on PC Connecting to PC’s serial port Data received by the hyperterminal program Conclusion and future work A simple data logger project was demonstrated using a PIC12F683 micrcontroller and DS18B20 temperature sensor. A number of improvements can be done with this project, such as adding an external EEPROM for expanding its recording capacity. A separate application program can be developed on PC's side to receive the serial data from the logger and generate temperature vs time plots. A real time clock chip can also be added in the project to keep record of the actual time stamp. A bigger PIC with more I/O pins would be more appropriate for implementing these additional features. Related posts: - A Digital temperature meter using an LM35 temperature sensor
| | | | | | | | | | | | | | | | | | | | | A logic probe is considered as a stethoscope for engineers and technicians for debugging digital logic circuits that consists of logic gates, memories, registers, etc. A digital multimeter (DVM) can also be used for such analytical purposes but it gives you the numeric value of the voltage at a point instead of the logic state. Depending on whether the circuit is based on TTL or CMOS components, the voltage levels for logic 0 and 1 could be different for each family. DVM users, thus, have to calculate logic levels from the measured voltages, which consumes time and delays the troubleshooting procedure. A logic probe, on the other hand, does all these functions automatically and shows meaningful logic states at test points. In this project, we will discuss about making a digital logic probe that is applicable to both TTL and CMOS circuits and uses minimal components. Logic probe for CMOS and TTL circuits Theory There are mainly two logic families used in digital circuits: TTL and CMOS. While TTL operates at +5V, the power supply for CMOS circuits could range from +3V to 15V. According to the standard TTL convention, any voltage less than 0.8V is defined as logic low while any voltage above 2.2 is defined as logic high and anything in between these extremes is defined as intermediate state. In case of CMOS circuits, any voltage less than 1/3 of VDD is defined as logic low while any voltage above 2/3 of VDD is defined as logic high. Any voltage between these values is defined as intermediate or transitional state. This logic probe works for both TTL and CMOS circuits powered from +5V to 15V. This logic probe consists of a 78L05 voltage regulator, a Microchip PIC12F683 microcontroller, a CD4094BE CMOS shift register and a tiny seven segment display. Since the operating voltage range of this logic probe is between 5 to 15V, the role of the 78L05 voltage regulator is to ensure that the microcontroller and the rest of the circuit are always powered with a stable 5V source. There are two voltage divider networks, each made from a 9.1K and a 1K Ohms (1% tolerance) resistors. The outputs of the two networks go to the ADC channels AN0 and AN1. The AN0 channel reads the logic level voltage of the test pin whereas the AN1 channel is used to measure the supply voltage of the test circuit. The information of the supply voltage is necessary to identify a CMOS circuit powered with > 5V. The GP2, GP4, and GP5 pins of PIC12F683 drives the CD4094 shift register. The shift register acts as an I/O expander in this application and its outputs are connected to a seven segment display. Circuit diagram of the logic probe (click on image to enlarge) Note: In the above circuit, pins 8 and 16 of CD4094 should be connected to ground and Vcc, respectively. Logic probe circuit setup on a breadboard Software The software for this project is written in MikroC PRO for PIC version 5.30. Only the ADC built-in library is used. The rest of the code is written without any library aid. The code starts with the definitions of pin names and common variable declarations. Apart from the main function which does all the tasks, there are six functions in the code. The setup() function sets directions of I/O pins and initiates the built-in ADC for data acquisition. The test_display() function tests the LEDs of the seven segment display connected to the shift register. The way this is done with the shift register will be discussed later in this text. The feature that makes this logic probe universal and unique is its ability to determine the supply voltage level. In the function test_supply(), the power source is tested. During this test if it is found that the ADC average of supply voltage is within the span of 4.75V to 5.25V then the standard TTL minimum high and maximum low logic level voltages are set, otherwise conventional CMOS logic level voltage values are set. These values are kept in the variables called high_level and low_level and are retained till power down. The display(unsigned char val) function is a software-hardware link between the shift register and the micro. It converts any 8 bit value given to the variable val in a way that it can be sent as an output just like a regular I/O port. The strobe pin is at first kept low to allow the access of the internal latches of the CD4094. Eight clock pulses are needed to shift data into the latches. During each clock pulse to the shift register, the variable val's most significant bit (MSB) is read first and is sent as a data to the data input of the shift register. After sending data to the data pin, val is left shifted once and the clock pin is also toggled once. At the end of eight cycles the strobe pin is pulled high and the display is updated. The adc_avg(unsigned char ch) function read the designated ADC channel by taking 16 samples of ADC data and converting these samples into an average voltage value. Lastly the check_logic() function checks the logic input and sends logic symbol data to the seven segment display. //Definition of shift register pin names// #define data_pin GP5_bit #define clock_pin GP4_bit #define strobe_pin GP2_bit //Definition of global variables// float high_level = 0.0; //minimum logic high voltage value storage float low_level = 0.0; //maximum logic low voltage value storage const unsigned short disp [4] = {0x38, 0x3E, 0x76, 0x00}; //Symbols for display - L, U, H and blank //Function prototypes// static void setup(void); static void test_display(void); static void test_supply(void); void display(unsigned char val); float adc_avg(unsigned char ch); void check_logic(void); void main(void) { setup(); //Setup the micro for operation test_supply(); //Check supply voltage and choose convention test_display(); //Check display for(;;) //Infinite loop { check_logic(); //Test logic level at logic input terminal } } static void setup(void) { //Set directions and initial states of I/O pins// TRISIO5_bit = 0; TRISIO4_bit = 0; TRISIO3_bit = 1; TRISIO2_bit = 0; TRISIO1_bit= 1; TRISIO0_bit = 1; data_pin = 0; strobe_pin = 0; clock_pin = 0; adc_init(); //Initiate internal ADC module delay_ms(100); //Wait of some time } static void test_display(void) { unsigned short test = 0; while(test<3) //Test seven segment display { display(disp[test]); //Put called symbol on display delay_ms(400); //Wait for some time test++; //Continue test } display(disp[3]); //Turn off display delay_ms(300); // Wait for some time } static void test_supply(void) { register float supply = 0; supply = adc_avg(1); //Take the average of supply voltage delay_ms(150); //Wait for some time if(((supply >= 4.75) && (supply <= 5.25))) //TTL convention test { high_level = 2.2; low_level = 0.8; } //CMOS convention test else { high_level = (supply*0.67); low_level = (supply*0.33); } //Wait for a while delay_ms(50); } void display(unsigned char val) { unsigned short clk = 0; unsigned short d = 0; strobe_pin = 0; //Hold strobe pin low for(clk = 0; clk < 8; clk++) //Do following job eight times { d = (0x80 & val); //Read the MSB of val if(d == 0) //If the MSB is zero { data_pin = 0; //then send 0 } else //otherwise send 1 { data_pin = 1; } clock_pin = 1; //Pull clock pin high delay_ms(1); //Wait for a while val = val << 1; //Left shift val clock_pin = 0; //Hold clock pin low delay_ms(1); //Wait for a while } strobe_pin = 1; //Pull strobe pin high delay_ms(1); //Wait for a while } float adc_avg(unsigned char ch) { register unsigned int temp = 0; unsigned short samples = 0; register float avg = 0; delay_ms(1); for(samples = 1; samples <= 16; samples += 1) //Take 16 ADC samples { temp = adc_read(ch); //Read assigned ADC temp += temp; //channel data and delay_us(25); //store temporarily } avg = (temp * 5.0); //Convert to readable avg /= 1023.0; //value other than count avg *= 10.0; //Take voltage divider avg /= 16.0; //and number of samples return avg; //into account } void check_logic(void) { register float vin = 0; vin = adc_avg(0); //Read logic input channel delay_ms(20); //Wait for some time if(vin >= high_level) //Check if it is high { display(disp[2]); //Send 'H' to display } else if(vin <= low_level) //Check if it is low { display(disp[0]); //Send 'L' to display } else //Otherwise it is undefined { display(disp[1]); //Toggle and send 'U' to display delay_ms(400); display(disp[3]); delay_ms(400); } } Download source and HEX files Operation The logic probe discussed here is very simple to operate. It should be powered by the power supply of any circuit which it will test. After power up and initial tests, it is ready for normal use. To check logic level at any point, the logic input terminal is just held at a point and the device reads the logic level at that point. It will show H, U or L which mean logic High, Undefined or intermediate or logic Low. Logic probe displays H, L and U for logic states High, Low and Undefined, respectively Conclusion In this project not only a logic probe is designed but it was also discussed how to expand outputs of a small micro with an inexpensive shift register. Thus the maximum utility of a small micro is achieved. I/O ports can also be expanded with multiplexer, parallel-input-serial-output (PISO) registers, decoders, encoders and universal shift registers as well as dedicated port expanders like MCP23008, 8255, etc. The seven segment display could also have been driven using several diodes but this technique required a large number of diodes. Note: The datasheet of 78L05 regulator recommends that the input voltage should be greater than 7V to achieve a regulated +5V output. In our application, when the test circuit is a standard TTL, the supply voltage is only 5V, and as such the output of 78L05 is slightly less than 5V. This introduces some error in ADC calculations which is significantly less compared to the band difference between logic HIGH and LOW, and therefore doesn't affect the operation of the logic probe. The logic probe circuit discussed here was tested in real life under various conditions and results were satisfactory for both TTL and CMOS circuits. I designed this project in a bread board as it was my prototype. A PCB or strip board version of it would have been much smaller than what is shown here. No related posts. Media Files ULP.zip (ZIP File, 41 KB) | | | | | | | | | | | | | | | | | | | | | | A servo motor is a special geared DC motor equipped with an electronic circuit for controlling the direction of rotation, as well as the position, of the motor shaft. Because servo motors allows precise angular positioning of their output shaft, they are used extensively in robotics and radio-controlled cars, airplanes, and boats to control the motion of their various parts. In this lab session, we will first explore what a servo motor consists of and how it works and then illustrate how to interface it with a PIC microcontroller. Servo motor control using PIC microcontroller Theory A servo motor (or servo) is a little box that contains a DC motor, an output shaft (servo arm) which is connected to the motor through a series of gears, and an electronic circuit to control the position of the shaft. The objective of using a servo is to achieve precise angular positioning of an object. In order to accomplish a servo function, an instantaneous positioning information of the output shaft is fed back to the control circuit using a transducer. A simplest way of doing this is by attaching a potentiometer to the output shaft or somewhere in the gear train. The control electronics compares the feedback signal (which contains the current position of the shaft) from the potentiometer to the control input signal (which contains information of the desired position of the shaft), and any difference between the actual and desired values (known as an error signal) is amplified and used to drive the DC motor in a direction necessary to reduce or eliminate the error. The error is zero when the output shaft gets to the desired position. The functioning block diagram of a typical servomotor is shown below. Principle of a servomotor Servo parts (Source: http://tutorial.cytron.com.my/2011/09/19/how-rc-servo-works/) The control input to a servo is a pulse width modulated (PWM) signal, generally of frequency 50 Hz. This means the pulse should repeat every 20ms. The width of the pulse determines the angular position of the output shaft. An electronic circuit inside the servo converts the PWM signal to a proportional output voltage which is compared with the feedback voltage from the potentiometer. If a difference exists between the two, the control circuit drives the motor in an appropriate direction until the difference becomes zero. A typical value of the pulse width is somewhere in the range of 1.0 to 2.0 milliseconds (ms). For a standard servo, a pulse width between 1.0 ms to 1.5 ms makes the servo to turn clockwise (CW), between 1.5 ms to 2.0 ms makes it to turn counterclockwise (CCW), and a 1.5 ms pulse width turns the servo motor to its center. However, these values could vary depending on the brand and make of the motor. It is advised to read the datasheet of the servo to find the true values of the pulse widths required for positioning the servo at different angles. Most servos rotate through 180°. However. there are some that could rotate through a full 360° or more. Servos are widely used as the moving joints in robotic arms for their precise angular positioning. They also finds applications in radio controlled (RC) toys. For example, in RC cars they are used in the steering mechanisms, and in RC boats to control the rudder. A servomotor has three wires: two are designated for power supply (Vcc and Ground) and the third wire is for the control signal.The Vcc wire is usually red and the ground one is either black or brown. The control signal wire comes in white, yellow, or orange color. The servomotor used in this experiment is from iCircuit technologies and has red, brown, and yellow color wires for Vcc, Gnd, and control signal, respectively. It operates at 5.0 V power supply and provides angular rotation through 180° A typical servo motor The pulse width values for different angular positions of this servo are provided in the table below. Remember that the repetition rate of the pulse should be 50 Hz (period of 20 ms). Servo timing information for different anglular positions Different angular positions of the servo arm Circuit The circuit diagram of this experiment is depicted below. The control input for the servo is derived from the RB1 pin of the PIC16F628A microcontroller that operates at 4.0 MHz using an external ceramic resonator. A tact switch is connected to the RB0 pin to provide user input to control the position of the servo arm. The operation part of this experiment is described in the software section below. Circuit diagram for servo motor control demonstration Circuit setup on breadboard Software The firmware for PIC16F628A is written in MikroC Pro for PIC compiler. The Timer0 module is operated as timer with prescaler 1:256 to generate an approximate 20 ms gap between the two successive PWM pulses. Keep in mind that the clock frequency is 4.0 MHz, which results into 1 μs machine cycle, thus simplifying the math involved in calculating the delay using Timer0. MikroC provides a built-in library function, Delay_Cyc(), that generates a variable clock cycles delay. This function is used to vary the width of the control pulse from 0.7 to 2.3 ms. When the circuit is first powered up or reset, a 50 Hz PWM signal with 0.7 ms pulse width is continuously generated at RB1 pin. This control signal moves the servo arm clockwise all the way to the end, which is considered as 0 angular position. When the tact switch connected to the RB0 pin is pressed, the width of the pulse is increased by 0.2 ms, which turns the shaft counterclockwise (CCW) by approximately 22.5°. So each time the switch is pressed, the pulse width is increased by 0.2 ms, and the shaft further rotates in CCW direction. After 8 successive presses of the switch, the pulse width becomes 2.3 ms and the shaft reaches the other end (180° angular position). On 9th press, the pulse width is reset to 0.7 ms, and the motor shaft rotates in clockwise direction until it gets at 0 angular position back. /* Servo motor Control using PIC16F628A MCU: PIC16F628A running at 4.0 MHz, MCLR enabled, WDT is OFF, Brown out detect disabled Written by: admin (icforfun.com) 2012/03/29 Description: User input switch is connected to RB0 and Servo Control signal is generated from RB1 pin. */ sbit SW1 at RB0_bit; sbit Control at RB1_bit; unsigned short i=7, delay; void interrupt() { delay = i*10; Control = 1; Delay_Cyc(delay); // Generates delay equal to (10*delay) clock cycles Control = 0; TMR0 = 180; // TMR0 returns to its initial value INTCON.T0IF = 0; // Bit T0IF is cleared so that the interrupt could reoccur } void main() { CMCON = 0x07; // Disable Comparators TRISB = 0b00000001; PORTB = 0; OPTION_REG = 0x07; // Prescaler (1:256) is assigned to the timer TMR0 TMR0 = 180; // Timer T0 counts from 180 to 255 to create ~20 ms period INTCON = 0xA0; // Enable interrupt TMR0 and Global Interrupts do{ if(!SW1){ // Change pulse width when Switch is pressed Delay_ms(300); i = i+2; if(i>23) i=7; } }while(1); } Download complete source and HEX files Output After loading the HEX file into the PIC16F628A microcontroller, you can watch the servo controller in action. Watch the demo video at the bottom to see the output of this experiment. Servo control No related posts. | | | | | | | | | | | | | | | | | | | | | Description HD44780 based LCD displays are very popular among hobbyists because they are cheap and they can display characters. Besides they are very easy to interface with microcontrollers and most of the present day high-level compilers have in-built library routines for them. Today, we will see how to interface an HD44780 based character LCD to a PIC16F688 microcontroller. The interface requires 6 I/O lines of the PIC16F688 microcontroller: 4 data lines and 2 control lines. A blinking test message, "Welcome to Embedded-Lab.com", will be displayed on the LCD screen. Required Theory All HD44780 based character LCD displays are connected through 14 pins: 8 data pins (D0-D7), 3 control pins (RS, E, R/W), and three power lines (Vdd, Vss, Vee). Some LCDs have LED backlight feature that helps to read the data on the display during low illumination conditions. So they have two additional connections (LED+ and LED-), making altogether 16 pin. A 16-pin LCD module with its pin diagraam is shown below. Control pins The control pin RS determines if the data transfer between the LCD module and an external microcontroller are actual character data or command/status. When the microcontroller needs to send commands to LCD or to read the LCD status, it must be pulled low. Similarly, this must be pulled high if character data is to be sent to and from the LCD module. The direction of data transfer is controlled by the R/W pin. If it is pulled Low, the commands or character data is written to the LCD module. And, when it is pulled high, the character data or status information from the LCD registers is read. Here, we will use one way data transfer, i.e., from microcontroller to LCD module, so the R/W pin will be grounded permanently. The enable pin (E) initiates the actual data transfer. When writing to the LCD display, the data is transferred only on the high to low transition of the E pin. Power supply pins Although most of the LCD module data sheets recommend +5V d.c. supply for operation, some LCDs may work well for a wider range (3.0 to 5.5 V). The Vdd pin should be connected to the positive power supply and Vss to ground. Pin 3 is Vee, which is used to adjust the contrast of the display. In most of the cases, this pin is connected to a voltage between 0 and 2V by using a preset potentiometer. Data pins Pins 7 to 14 are data lines (D0-D7). Data transfer to and from the display can be achieved either in 8-bit or 4-bit mode. The 8-bit mode uses all eight data lines to transfer a byte, whereas, in a 4-bit mode, a byte is transferred as two 4-bit nibbles. In the later case, only the upper 4 data lines (D4-D7) are used. This technique is beneficial as this saves 4 input/output pins of microcontroller. We will use the 4-bit mode. For further details on LCDs, I recommend to read these two articles first from Everyday Practical Electronics magazine : How to use intelligent LCDs Part 1, and Part 2. Circuit Diagram Data transfer between the MCU and the LCD module will occur in the 4-bit mode. The R/W pin (5) of the LCD module is permanently grounded as there won't be any data read from the LCD module. RC0-RC3 serves the 4-bit data lines (D4-D7, pins 11-14) of the LCD module. Control lines, RS and E, are connected to RC4 and RC5. Thus, altogether 6 I/O pins of the PIC16F688 microcontrollers are used by the LCD module. The contrast adjustment is done with a 5K potentiometer as shown below. If your LCD module has backlight LED, use a 68Ω resistance in series with the pin 15 or 16 to limit the current through the LED. The detail of the circuit diagram is shown below. The whole circuit constructed on the breadboard is shown below. If you want to reduce some of the wire connections on the breadboard and save some space, you can first construct my PIC16F688 breadboard module, and then insert it on the breadboard (shown in the following pictures). LCD interfacing to PIC16F688 Breadboard Module Software The LCD data and control lines are driven through PORTC, so it must be defined as output port (TRISC=0). You need to disable the comparator functions (CMCON0=7), and select all I/O pins as digital (ANSEL=0). The programming of an HD44780 LCD is a little bit complex procedure as it requires accurate timing and proper sequence of various control signals and commands. But luckily, the MikroC Pro for PIC compiler has in-built library routines to communicate with a standard HD44780 based LCD module using the 4-bit mode. This makes programming a lot easier. Before using the built-in functions for LCD, the connections of LCD pins to the PIC microcontrollers must be defined. The comments are provided in the source code to understand how to use the library routines for LCDs in MikroC Pro for PIC. /* Lab 4: Blink Character Message on LCD Internal Clock @ 4MHz, MCLR Enabled, PWRT Enabled, WDT OFF Copyright @ icforfun.com May, 20102*/ // Define LCD module connections. sbit LCD_RS at RC4_bit; sbit LCD_EN at RC5_bit; sbit LCD_D4 at RC0_bit; sbit LCD_D5 at RC1_bit; sbit LCD_D6 at RC2_bit; sbit LCD_D7 at RC3_bit; sbit LCD_RS_Direction at TRISC4_bit; sbit LCD_EN_Direction at TRISC5_bit; sbit LCD_D4_Direction at TRISC0_bit; sbit LCD_D5_Direction at TRISC1_bit; sbit LCD_D6_Direction at TRISC2_bit; sbit LCD_D7_Direction at TRISC3_bit; // End LCD module connection definition // Define Messages char message1[] = "Welcome to"; char message2[] = "icforfun.com"; void main() { ANSEL = 0b00000000; //All I/O pins are configured as digital CMCON0 = 0x07 ; // Disbale comparators TRISC = 0b00000000; // PORTC All Outputs TRISA = 0b00000000; // PORTA All Outputs, Except RA3 Lcd_Init(); // Initialize LCD do { Lcd_Cmd(_LCD_CLEAR); // CLEAR display Lcd_Cmd(_LCD_CURSOR_OFF); // Cursor off Lcd_Out(1,4,message1); // Write message1 in 1st row Lcd_Out(2,1,message2); // Write message2 in 2nd row Delay_ms(1000); // Wait for 1 sec Lcd_Cmd(_LCD_CLEAR); // Clear display Delay_ms(1000); // Wait for 1 sec } while(1); // Infinite Loop } Output After the hex file is loaded into the PIC16F688 microcontroller, and the power is turned on, you will see the message, "Welcome com to icforfun.com", on the LCD display, blinking every second. Related posts: - Based Digital Voltmeter (DVM) with Pic16f688
Media Files lcd1.pdf (Adobe Acrobat Document, 264 KB) | | | | | | | | | | | | | | | | | | | | | | This module is based upon the basic setup circuit for PIC16F688 , I thought of soldering this circuit on a general purpose ptototyping board and use male headers to access the power supply pins and I/O ports of the PIC16F688 microcontroller. This will free up a lot of space on the breadboard as the ICSP header and the reset switch are transferred from the breadboard to the module. This will make prototyping on the breadboard easier and quicker. The general layout of the module is shown below. It is followed by the circuit diagram. Here's my finished breadboard module for PIC16F688 microcontroller. PIC16F688 module plugged into a breadboard Related posts: - Regulated Power Supply for Your Breadboard
| | | | | | | | | | | | | | | | | | | | | Introduction This project will describe how to make a simple digital voltmeter (DVM) using a PIC16F688 microcontroller. The range of this DVM is 0-20V, but you can easily increase or decrease the range of input voltage as your requirements after you understand the voltage scaling method described in this project. The PIC micro reads the input voltage through one of the 8 analog channels and convert it to a 10-bit digital number using the internal ADC. Doing some math with ADC conversion (you will see later), this number can be converted to the actual measured voltage. The voltage is displayed in an HD44780-based character LCD. Circuit Diagram and Description You cannot feed a 20V signal directly to a PIC microcontroller's input channel. It is too higher than its operating voltage, and the microcontroller could be damaged. So, first we need a voltage scaler that will scale down the input voltage to the safe operating voltage range of PIC16F688. It can be achieved by a simple resistor divider network shown below. Using two resistors, R1 and R2, the input voltage ranging from 0-20V can be down converted to 0-5V. For the chosen values of R1 and R2, you can see that the output (Va) from the resistor divider network is 1/4th of the input voltage. If the input voltage goes beyond 20V, Va will exceed 5V, and this could be harmful for the PIC microcontroller. If you connect a 5.1V Zener diode across the R1 resistor, the output voltage Va will never exceed 5.1V. This will protect the microcontroller from any possible damage due to high voltage input. The voltage Va will go to AN2 (pin 11) channel of the PIC16F688 microcontroller. The rest of the circuit is shown below. The LCD display is connected in 4-bit mode. If you have just 14 pins in your LCD module, then you may not have a back-light facility and you can ignore the pins 15 and 16. The contrast adjustment is done through a 5K potentiometer connected between +5V and Gnd. An in-circuit serial programming (ICSP) header is provided so that you can easily upgrade the firmware inside the PIC microcontroller in future if you make any changes. An external reset is helpful to bring the entire system to a known initial condition, when the microcontroller stops executing the program for some reason. The complete circuit built on a breadboard is shown here. You need a regulated +5V power supply for this project (Why regulated? You will find the answer in the Software section below). You can use a LM7805 linear regulator IC for this purpose (read my article Regulated Power Supply for your Breadboard). Software Before writing the code for this project, you need to do some math related to AD conversion. You know that any application that uses analog-to-digital converters (ADCs), requires a fixed reference voltage to provide accurate digital count for input analog signal. If the reference voltage is not stable, the ADC output is meaningless. In this project, the reference voltage for ADC operation is selected to be Vdd (= +5 V). This is done by clearing the VCFG bit in ADCON0 register. Therefore, the ADC will convert any input voltage between 0-5 V in to a digital count between 0-1023. A major source of error in this project is the accuracy of R1 and R2 resistors. You are recommended not to use the rated values of the resistors. Rather measure their values with a good quality digital multimeter, and use those values for your calculation. What I found is R1 = 1267 Ω and R2 = 3890 Ω. Now, 0 – 5 V Analog I/P is mapped to one of the 1024 levels (0-1023 Digital Count) => Resolution = 5/1024 = 0.0049 V/Count Also, Va = 1267*Vin/(1267+3890) = 0.2457*Vin => I/P voltage = 4.07*Va = 4.07* Digital Count * 0.0049 = 0.01994 * Digital Count = 0.02*Digital Count (Approx.) To avoid floating point, use I/P voltage = 2*Digital Count. Here's how it works. Suppose, Vin = 4.6V. Then, Va = 0.2457*Vin = 1.13V => Digital Count = 1.13/0.0049 = 231 => Calculated I/P Voltage = 2*231 = 0462 In the 4-digit product (0462), the first two digits are the tens and units digits of measured voltage. and the last two are the decimal digits. So, the measured voltage will be 04.62 V. Only the first three digits will be displayed (04.6 V). The firmware is developed in C and compiled with mikroC Pro for PIC compiler from Mikroelektronika. The PIC16F688 microcontroller uses internal clock oscillator at 4.0 MHz. MCLR is enabled and Power Up Timer is turned ON. You need to define RA2/AN2 input as analog by setting the corresponding bit in ANSEL register. VCFG bit of ADCON1 register is cleared to use Vdd = +5V as reference voltage for AD conversion. ADCON0 = 8 connects the AN2 input channel to the internal Sample and Hold circuit. Comparators on ports A and C pins must be disabled too (assign CMCON0 = 7). The configuration bits for the fuses are given below. You can set these in MikroC through Project->Edit Project. Oscillator -> Internal RC No Clock Watchdog Timer -> Off Power Up Timer -> On Master Clear Enable -> Enabled Code Protect -> Off Data EE Read Protect -> Off Brown Out Detect -> BOD Enabled, SBOREN Disabled Internal External Switch Over Mode -> Enabled Monitor Clock Fail-Safe -> Enabled The complete program written in mikroC is given here. /* Project: Digital Voltmeter based on PIC16F688 Internal Oscillator @ 4MHz, MCLR Enabled, PWRT Enabled, WDT OFF Copyright @ icforfun.com May 2012 */ // LCD module connections sbit LCD_RS at RC4_bit; sbit LCD_EN at RC5_bit; sbit LCD_D4 at RC0_bit; sbit LCD_D5 at RC1_bit; sbit LCD_D6 at RC2_bit; sbit LCD_D7 at RC3_bit; sbit LCD_RS_Direction at TRISC4_bit; sbit LCD_EN_Direction at TRISC5_bit; sbit LCD_D4_Direction at TRISC0_bit; sbit LCD_D5_Direction at TRISC1_bit; sbit LCD_D6_Direction at TRISC2_bit; sbit LCD_D7_Direction at TRISC3_bit; // End LCD module connections char Message1[] = "DVM Project"; unsigned int ADC_Value, DisplayVolt; char *volt = "00.0"; void main() { ANSEL = 0b00000100; // RA2/AN2 is analog input ADCON0 = 0b00001000; // Analog channel select @ AN2 ADCON1 = 0x00; // Reference voltage is Vdd CMCON0 = 0x07 ; // Disable comparators TRISC = 0b00000000; // PORTC All Outputs TRISA = 0b00001100; // PORTA All Outputs, Except RA3 and RA2 Lcd_Init(); // Initialize LCD Lcd_Cmd(_LCD_CLEAR); // CLEAR display Lcd_Cmd(_LCD_CURSOR_OFF); // Cursor off Lcd_Out(1,1,Message1); Lcd_Chr(2,10,'V'); do { ADC_Value = ADC_Read(2); DisplayVolt = ADC_Value * 2; volt[0] = DisplayVolt/1000 + 48; volt[1] = (DisplayVolt/100)%10 + 48; volt[3] = (DisplayVolt/10)%10 + 48; Lcd_Out(2,5,volt); delay_ms(500); // Hold for 500 ms } while(1); } // End main() Hex file : DVM Testing the DVM Here are the DVM measurement output for various input voltages ranging from 0-20V obtained through a variable DC power source. Variable power supply source Questions, comments, and suggestions are welcome. Related posts: - Basic digital input and output of Pic16f688
- A Digital temperature meter using an LM35 temperature sensor
| | | | | | | | | | | | | | | | | | | | | Embedded systems require electric power to operate. Most of the components in them including the processors can operate at a wide range of voltages. For example, the operating voltage range for PIC16F688 is from 2 to 5.5 V. It means you can supply power from three AA size batteries (4.5 V) and it will work just fine as long as the battery voltage doesn't fall below 2 V. But there are certain applications where you need a regulated constant voltage for safer operation of the embedded system. For instance, any application that uses analog-to-digital converters (ADCs). ADCs require a fixed reference voltage to provide accurate digital count for input analog signal. If the reference voltage is not stable, the ADC output is meaningless. So, today we are going to make a regulated +5V power source for our lab. An LM7805 linear regulator IC is used for this purpose. It converts a DC input voltage of range 7-25 V to a stable +5 V. It requires just two external capacitors and is very easy to use, as shown below. The input DC voltage for LM7805 could be obtained from a 9V DC wall adapter that can supply 1 Amp of load current. Actually, 12 to 24 V adapter will work too, but the LM7805 regulator dissipates an extreme amount of heat energy at higher input voltages and, therefore, requires a bulky heat sink. The wall adapter is chosen because it is cheap, easily available (you might already have got a spare one at home), and safe (the high voltage mains AC is isolated). You can solder this circuit on a general purpose prototyping board. Here are some pictures I took of my power supply unit. I have soldered male header pins (shown above) to +5 V and Gnd terminals so that the power supply unit can be plugged into the breadboard (shown below). The power supply unit is ready now. Measure the output voltage with a digital multimeter and see how close is it to +5 V. No related posts. | | | | | | | | | | | | | | | | | | | | | Introduction A digital thermometer is a good choice of project for beginners who just stepped in to the world of microcontrollers because it provides an opportunity to learn using sensors to measure the real world signals that are analog in nature. This article describes a similar project based on a PIC16F688 microcontroller and an LM35 temperature sensor. LM35 is an analog sensor that converts the surrounding temperature to a proportional analog voltage. The output from the sensor is connected to one of the ADC channel inputs of the PIC16F688 microcontroller to derive the equivalent temperature value in digital format. The computed temperature is displayed in a 16×2 character LCD, in both °C and °F scales. Theory The LM35 series of temperature sensors are produced by National Semiconductor Corporation and are rated to operate over a -55 °C to 150°C temperature range. These sensors do not require any external calibration and the output voltage is proportional to the temperature. The scale factor for temperature to voltage conversion is 10 mV per °C. The LM35 series sensors come in different packages. The one I used is in a hermatic TO-46 transistor package where the metal case is connected to the negative pin (Gnd). The measurement of negative temperatures (below 0°C) requires a negative voltage source. However, this project does not use any negative voltage source, and therefore will demonstrate the use of sensor for measuring temperatures above 0°C (up to 100°C). The output voltage from the sensor is converted to a 10-bit digital number using the internal ADC of the PIC16F688. Since the voltage to be measured by the ADC ranges from 0 to 1.0V (that corresponds to maximum temperature range, 100 °C), the ADC requires a lower reference voltage (instead of the supply voltage Vdd = 5V) for A/D conversion in order to get better accuracy. The lower reference voltage can be provided using a Zener diode, a resistor network, or sometime just simple diodes. You can derive an approximate 1.2V reference voltage by connecting two diodes and a resistor in series across the supply voltage, as shown below. As a demonstration, I am going to use this circuit in this project. I measured the output voltage across the two diodes as 1.196 V. The resistor R I used is of 3.6K, but you can use 1K too. The important thing is to measure the voltage across the two diodes as accurate as possible. We need do some math for A/D conversion. Our Vref is 1.196 V, and the ADC is 10-bit. So, any input voltage from 0-1.196 will be mapped to a digital number between 0-1023. The resolution of ADC is 1.196/1024 = 0.001168 V/Count. Therefore, the digital output corresponding to any input voltage Vin = Vin/0.001168. Now, lets see how to get the temperature back from this whole process of converting sensor's output to 10-bit digital number. Assume, the surrounding temperature is 26.4 °C. The sensor output will be 264 mV (0.264 V). The output of ADC will be 0.264/0.001168 = 226. If we reverse this process, we have 226 from ADC and we can go back and find the temperature by using the sensor scale factor (10 mV/°C), temperature = 226 * 0.001168 (V/Count) / 0.01 (V/°C) = 26.4 °C If you want to avoid floating point math in your program, just use, temperature = 226 * 1168 = 263968 While displaying this, you need to put a decimal at the fourth place from the left. So the calculated temperature is 26.3968°C, which is pretty close to the actual one. The difference is caused by quantization and rounding errors. In this project, we will display temperature accurate to one decimal place, i.e., we will divide the above number by 1000 to get 263. So the temperature will be displayed as 26.3 °C. Once you have derived the temperature back in °C, you can convert it to °F using a simple equation, temperature in °F = 9 x temperature in °C /5 + 32 In this case, the number you got for °C is scaled by 10 (263 for 26.3), you should use temperature in °F = 9 x temperature in °C /5 + 320 Since, the number for °C may not be exactly divisible by 5 (such as 263 is not), you can further eliminate the floating point by scaling it one more time by 10. So the new equation will be, temperature in °F = 9 x temperature in °C x 10 /5 + 3200 or, temperature in °F = 18 x temperature in °C + 3200 = 18 x 263+3200 = 7934 79.34 °F is equivalent to 26.3 °C. In this project, it will be displayed as 79.3 °F. Circuit Diagram An external reference voltage to the internal ADC of PIC16F688 can be provided through RA1 I/O pin. The output from the LM35 sensor is read through RA2/AN2 ADC channel. The temperature is displayed on a 16×2 character LCD that is operating in the 4-bit mode. A 5K potentiometer is used to adjust the contrast level on the display. The detail circuit diagram is given below. Note that the PIC16F688 uses its internal clock at 4.0 MHz. Circuit setup on the breadboard A closer look at the LM35DH sensor and the reference voltage circuit. Software The firmware for this project is developed with MikroC Pro for PIC compiler. The link to download the compiled HEX code is provided at the bottom of this section. The configuration bits for PIC16F688 are Oscillator -> Internal RC No Clock Watchdog Timer -> Off Power Up Timer -> On Master Clear Enable -> Enabled Code Protect -> Off Data EE Read Protect -> Off Brown Out Detect -> BOD Enabled, SBOREN Disabled Internal External Switch Over Mode -> Enabled Monitor Clock Fail-Safe -> Enabled You can set these fuses in the Edit Project window (Project-> Edit Project) /* Digital Thermometer using PIC16F688 and LM35 Internal Oscillator @ 4MHz, MCLR Enabled, PWRT Enabled, WDT OFF Copyright @ Rajendra Bhatt November 8, 2010 */ // LCD module connections sbit LCD_RS at RC4_bit; sbit LCD_EN at RC5_bit; sbit LCD_D4 at RC0_bit; sbit LCD_D5 at RC1_bit; sbit LCD_D6 at RC2_bit; sbit LCD_D7 at RC3_bit; sbit LCD_RS_Direction at TRISC4_bit; sbit LCD_EN_Direction at TRISC5_bit; sbit LCD_D4_Direction at TRISC0_bit; sbit LCD_D5_Direction at TRISC1_bit; sbit LCD_D6_Direction at TRISC2_bit; sbit LCD_D7_Direction at TRISC3_bit; // End LCD module connections // Define Messages char message0[] = "LCD Initialized"; char message1[] = "Room Temperature"; // String array to store temperature value to display char *tempC = "000.0"; char *tempF = "000.0"; // Variables to store temperature values unsigned int tempinF, tempinC; unsigned long temp_value; void Display_Temperature() { // convert Temp to characters if (tempinC/10000) // 48 is the decimal character code value for displaying 0 on LCD tempC[0] = tempinC/10000 + 48; else tempC[0] = ' '; tempC[1] = (tempinC/1000)%10 + 48; // Extract tens digit tempC[2] = (tempinC/100)%10 + 48; // Extract ones digit // convert temp_fraction to characters tempC[4] = (tempinC/10)%10 + 48; // Extract tens digit // print temperature on LCD Lcd_Out(2, 1, tempC); if (tempinF/10000) tempF[0] = tempinF/10000 + 48; else tempF[0] = ' '; tempF[1] = (tempinF/1000)%10 + 48; // Extract tens digit tempF[2] = (tempinF/100)%10 + 48; tempF[4] = (tempinF/10)%10 + 48; // print temperature on LCD Lcd_Out(2, 10, tempF); } void main() { ANSEL = 0b00000100; // RA2/AN2 is analog input ADCON0 = 0b01001000; // Connect AN2 to S/H, select Vref=1.19V CMCON0 = 0x07 ; // Disbale comparators TRISC = 0b00000000; // PORTC All Outputs TRISA = 0b00001110; // PORTA All Outputs, Except RA3 and RA2 Lcd_Init(); // Initialize LCD Lcd_Cmd(_LCD_CLEAR); // CLEAR display Lcd_Cmd(_LCD_CURSOR_OFF); // Cursor off Lcd_Out(1,1,message0); Delay_ms(1000); Lcd_Out(1,1,message1); // Write message1 in 1st row // Print degree character Lcd_Chr(2,6,223); Lcd_Chr(2,15,223); // Different LCD displays have different char code for degree symbol // if you see greek alpha letter try typing 178 instead of 223 Lcd_Chr(2,7,'C'); Lcd_Chr(2,16,'F'); do { temp_value = ADC_Read(2); temp_value = temp_value*1168; tempinC = temp_value/1000; tempinC = tempinC*10; tempinF = 9*tempinC/5 + 3200; Display_Temperature(); Delay_ms(1000); // Temperature sampling at 1 sec interval } while(1); } Hex file :LM35_TempMeter Output I took some pictures of the completed project displaying temperatures in both the scales. Temperature goes up if you touch the sensor with your fingers Accuracy of the measurement The accuracy of the temperature measurement highly depends upon the stability of the reference voltage. If the reference voltage drifts from the value that we considered in our calculation, the measured temperature value could be significantly off from the actual value. Using a simple diode-resistor network for deriving a reference voltage may not be a very good idea, but the purpose of this project was to demonstrate the technique, not to come up with a commercial digital thermometer product. You can also try a Zener diode or a potentiometer to derive the reference voltage. Besides, the quantization error introduced by the 10-bit ADC, rounding numbers while doing the math, and the accuracy of the sensor itself within the desired range of temperature also affect the measurement output. Read the manufacturer's datasheet for more details on the performance of the LM35 series sensor. Update This design has a little flaw. It uses a voltage drop across two diodes (≈ 1.2 V) as a reference voltage (Vref) for A/D conversion. However, the datasheet of PIC16F688 suggests to use Vref greater than 2.2 V to ensure 1-LSB accuracy in the A/D conversion. Therefore, I have re-written this project but this time using a MCP1525 device for creating a precise 2.5 V reference voltage. The new design is more precise and accurate in taking temperature measurements. Related posts: - Add a Lm35 thermometer to your digital multimeter
- Testing active analog temperature sensors with a multimeter
| | | | | | | | | | | | | | | | | | | | | Description Today we will learn how to read digital inputs from a push button switch. A digital input has only two values: 1 and 0. The configuration of the push button switch is same as that of the reset switch except it goes to a different port pin. The status of the switch will be read through RC1 and every time when it is pressed, an LED connected to RC0 will be toggled ON and OFF. Required Theory You must be familiar with the digital I/O ports of PIC16F688 and their direction settings. If you are not, read Digital I/O Ports in PIC16F688. Circuit Diagram To our last setup (see Lab 1: Flashing an LED), connect a push button switch to RC1 as shown below. The input ports of a PIC microcontroller have very high input impedance, and therefore, during normal condition when the switch is open, the RC1 pin is pulled 'High' through the external 10K resistor. When the switch is pressed, RC1 is pulled to ground and the microcontroller will read it as logic 'Low'. In this way, a simple push button switch with a pull-up resistor can be used to provide digital inputs (1 and 0) to the microcontroller. Circuit for reading digital inputs. Prototyped circuit on the breadboard Software Since the switch is connected to RC1, this port pin must be defined as input. This is done by setting the corresponding bit in the TRISC register. Again, don't forget to disable the comparators and ADC channels. The code for this experiment is provided below. You should compile it with the MikroC compiler and load the output hex file inside the PICMicro. The MikroC compiler has an in-built library routine (named Button) for reading digital inputs from push button switches. The syntax is, You can see that the Button command has debounce features built in. Debouncing a switchWhen a mechanical switch (like a push button) is pressed or released to make or break an electrical contact, the switch tends to make multiple transitions between 'open' and 'close' before coming to the stable final state. This is called switch bounce and this activity may go on for tens of milliseconds. We cannot see this bouncing but a microcontroller is relatively fast as compared to the action of the switch, and is, therefore, able to recognize this as multiple button presses and respond accordingly. Therefore, the switch must be debounced before it's status is read by a microcontroller. Debouncing circuits require extra hardware and thus raises the cost of the embedded system. The easiest solution is to implement debouncing in software. The simplest logic is to wait for a few milliseconds (5-20 ms) after the key press or release is detected, and read the final state of the switch again to make sure it is a valid button press or release. | Note that the configuration bits setting is same as in Lab 1. /* Lab 2: Toggle LED with a Tact Switch Input Internal Oscillator @ 4MHz, MCLR Enabled, PWRT Enabled, WDT OFF Copyright @ Rajendra Bhatt Oct 8, 2010 */ // Define LED @ RC0 and Tact switch @ RC1 sbit LED at RC0_bit; sbit Switch at RC1_bit; #define Switch_Pin 1 #define Switch_Port PORTC #define Debounce_Time 20 void main() { ANSEL = 0b00000000; //All I/O pins are configured as digital CMCON0 = 0x07 ; // Disbale comparators TRISC = 0b00000010; // PORTC All Outputs TRISA = 0b00001000; // PORTA All Outputs, Except RA3 LED = 0; do { if (Button(&Switch_Port, Switch_Pin, Debounce_Time, 0)) { if (!Switch) { LED = ~LED; } while (!Switch); // Wait for release of the button } } while(1); // Infinite Loop } Output Every time the switch is pressed, the LED toggles ON and OFF. Once the switch is pressed, the microcontroller will wait for its release before it detects another press. No related posts. | | | | | | | | | | | | | | | | | | | | | Description Today is our first session in PIC microcontroller lab, and we will begin with an experiment that flashes an LED on and off. While this looks very simple it is the best project to start because this makes sure that we successfully wrote the program, compiled it, loaded inside the PIC, and the circuit is correctly built on the breadboard. In this lab session we will connect an LED to one of the port pin of PIC16F688 and flash it continuously with 1 sec duration. Required Theory You must be familiarized with, - digital I/O ports (PORTA and PORTC) of PIC16F688
- direction control registers, TRISA and TRISC
- special function registers CMCON0 and ANSEL
If you are not then please read this first: Digital I/O Ports in PIC16F688. Circuit Diagram We will add a light-emitting-diode (LED) to port pin RC0 (10) with a current limiting resistor (470 Ohm) in series. The complete circuit diagram is shown below. Flashing LED circuit Prototyped circuit on the breadboard Software Open a new project window in mikroC and select Device Name as PIC16F688. Next assign 4.0 MHz to Device Clock. Go to next and provide the project name and the path of the folder. It is always a good practice to have a separate folder for each project. Create a folder named Lab1 and save the project inside it with a name (say, FlashLED). The mikroC project file has .mccpi extension. The next window is for "Add File to Project ". Leave it blank (there are no files to add to this project) and click next. The next step is to include libraries, select Include All option. Next, click Finish button. You will see a program window with a void main() function already included. Now, go to Project -> Edit Project. You will see the following window. This window allows you to program the configuration bits for the 14-bit CONFIG register inside the PIC16F688 microcontroller. The device configuration bits allow each user to customize certain aspects of the device (like reset and oscillator configurations) to the needs of the application. When the device powers up, the state of these bits determines the modes that the device uses. Therefore, we also need to program the configuration bits as per our experimental setup. Select, Oscillator -> Internal RC No Clock Watchdog Timer -> Off Power Up Timer -> On Master Clear Enable -> Enabled Code Protect -> Off Data EE Read Protect -> Off Brown Out Detect -> BOD Enabled, SBOREN Disabled Internal External Switch Over Mode -> Enabled Monitor Clock Fail-Safe -> Enabled Note that we have turned ON the Power-Up Timer. It provides an additional delay of 72 ms to the start of the program execution so that the external power supply will get enough time to be stable. It avoids the need to reset the MCU manually at start up. Read PIC16F688 datasheet for details on Configuration Word. Appropriate configuration bit selection Here's the complete program that must be compiled and loaded into PIC16F688 for flashing the LED. Copy and paste this whole program in your main program window. You have to delete the void main() function first that was already included. To compile, hit the build button, and if there were no errors, the output HEX file will be generated in the same folder where the project file is. Then load the HEX file into the PIC16F688 microcontroller using your programmer. /* Internal Oscillator @ 4MHz, MCLR Enabled, PWRT Enabled, WDT OFF Copyright @ icforfun.com 2012 */ // Define LED @ RC0 sbit LED at RC0_bit; void main() { ANSEL = 0b00000000; //All I/O pins are configured as digital CMCON0 = 0×07 ; // Disbale comparators TRISC = 0b00000000; // PORTC All Outputs TRISA = 0b00001000; // PORTA All Outputs, Except RA3 do { LED = 1; Delay_ms(1000); LED = 0; Delay_ms(1000); } while(1); // Infinite Loop } We used the in-built library function Delay_ms() to create the 1 sec delay for flashing On and Off. Delay_ms() returns a time delay in milliseconds. Output You will see the LED flashing On and Off with 1 sec duration. No related posts. | | | | | | | | | | | | | | | | | | | | | The PIC16F688 microcontroller has a built-in 10-bit ADC with eight input channels. The eight channels are available at RA0, RA1, RA2, RA4, RC0, RC1, RC2, and RC3. They have alternate labels, AN0-AN7, for this function, and are multiplexed into a single sample and Hold circuit. The output of the sample and hold is connected to the input of the A/D converter. The A/D conversion is successive approximation based and the 10-bit result is stored into the ADC result registers ADRESH (A/D Result Higher byte) and ADRESL (A/D Result Lower byte). Each of these registers is 8-bit. Pin diagram of the PIC16F688 microcontroller Functional block diagram of ADC The functionality of the A/D module is controlled by three registers: ANSEL, ADCON0, and ADCON1. The ANSEL register is used to configure the input mode of an I/O pin to analog. Setting the appropriate ANSEL bit High will cause all digital reads on the pin to be read as 0, and allow analog function on the pin to operate correctly. The state of ANSEL bits has no effect on digital O/P function. A pin with TRIS bit clear and ANSEL bit set will operate as digital O/P but the input mode will be analog. ANSEL register bits The ADCON0 register selects which analog input is to be measured. This is required, since there are several channels for analog input but only one A/D converter circuitry. The channel select bits CHS0-CHS2 of ADCON0 register controls which analog channel is connected to the internal Sample and Hold circuit. For example, if you want to measure input analog voltage at AN2 (RA2) pin, you should set CHS1, and clear CHS0 and CHS2. The reference voltage used in the A/D conversion process is software selectable to either Vdd or a voltage applied to the Vref pin (12). This is controlled by VCFG bit of ADCON0 register. If VCFG is set, the reference voltage for the A/D conversion will be the voltage applied at the Vref pin. Bit 0 (ADON) of ADCON0 is the On/Off switch for the A/D converter. When it is set, the ADC is On, and the PIC microcontroller consumes extra current. GO/DONE bit of ADCON0 is set to start an A/D conversion cycle. It is automatically cleared by hardware when the conversion is done. This bit can, therefore, be tested to see if the A/D conversion is completed or in progress. ADCON0 register bits As mentioned earlier, the 10-bit digital output is held over two registers, ADRESH and ADRESL. You have a choice in how you want the 10-bit number stored into the two 8-bit registers. It can be right-justified by setting ADFM bit of ADCON0 so that bits 0:7 of the 10-bit result are held in ADRESL and bits 8:9 are stored in bits 0:1 of ADRESH. Similarly, clearing the ADFM bit shift the 10-bit output to the left. This is illustrated below. Shifting the ADC result left or right with ADFM bit The ADCON1 register also plays an important role in programming the A/D module. An ADC requires a clock source to operate. ADCS (A/D conversion Clock Select) bits of ADCON1 register are the selection bits for the A/D conversion clock. The conversion time per bit is defined as TAD in PIC documentation from Microchip. The A/D conversion cycle requires 11 TAD. For correct conversion, the A/D conversion clock (1/TAD) must be selected to ensure a minimum TAD of 1.6 μs. In PIC16F688, there are seven possible clock options for A/D conversion that are software selected, as shown below. Software selectable clock sources for A/D conversion TAD calculations for selected frequencies. Acquisition Time The input of the ADC has a sample and hold circuit, to ensure that the voltage sampled is constant during the conversion process. This contains a holding capacitor that stores the sampled input voltage. Acquisition time is the amount time required to charge the holding capacitor on the front end of an analog-to-digital converter. The holding capacitor must be given sufficient time to settle to the analog input voltage level before the actual conversion is initiated, otherwise the conversion will be inaccurate. The source impedance of the input voltage and the impedance of the internal sampling switch are the two major factors to determine the required acquisition time. An increase in the source impedance will increase the required acquisition time. Therefore, the maximum recommended source impedance for analog sources is 10K. A typical value of acquisition time is 20 µs. Programming the A/D module consists of the following steps: 1. Configure the analog/digital I/O using ANSEL register. All analog lines are initialized as input in the corresponding TRIS registers. 2. Select the A/D input channel by setting the CHSx bits in the ADCON0 register. Also, select right- or left-justification (ADFM bit), and the reference voltage (VCFG bit). 3. Select the A/D conversion clock (ADCS bits), and enable the A/D module (ADON bit). 4. Wait the acquisition time. 5. Initiate the conversion by setting the GO/DONE bit in the ADCON0 register. 6. Wait for the conversion to complete. 7. Read and store the digital result. A minimum wait of 2 TAD is required before the next acquisition starts. Reference: Datasheet for PIC16F688 from Microchip. No related posts. Media Files 41203D.pdf (Adobe Acrobat Document, 3.6 MB) | | | | | | | | | | | | | | | | | | | | | | PIC16F688 is a 14-pin flash-based, 8-bit microcontroller. It can be obtained in different packages, but the DIP (Dual In-line Package) version is recommended for prototyping. The figure below shows a PIC16F688 microcontroller in DIP chip, and its pin outs. Most of the pins are for input and output, and are arranged as PORTA (6) and PORTC (6), giving a total of 12 I/O pins. All of these can operate as simple digital I/O pins but they do have more than one function. For example, eight of total 12 I/O pins also serve as analogue inputs for the internal analog-to-digital converter (ADC). Similarly, the PORTA pins RA0 and RA1 are also used to serially load an user program into the PIC16F688 flash memory. The mode of operation of each pin is selected by initializing various control registers inside the chip. All these options will be discussed later on. PORTA PORTA is a 6-bit wide, bidirectional port. A bidirectional port is one that can act as either an input port, to receive information from external circuitry, or an output port, to give information to external circuitry. The direction of the PORTA pins is controlled by the TRISA register. Setting a TRISA bit (= 1) will make the corresponding PORTA pin an input, and clearing a TRISA bit (= 0) will make the corresponding PORTA pin an output. The RA3 pin is an exception because it is input only and so its TRISA bit will always read as '1'. The TRISA register controls the direction of the PORTA pins, even when they are being used as analog inputs. Therefore, the user must ensure the bits in the TRISA register are maintained set when using them as analog inputs. I/O pins configured as analog input always read '0'. PORTC Similar to PORTA, PORTC also has 6 bidirectional I/O pins. Each of the pin can be selected as input or output by setting or clearing the corresponding bit in the TRISC register. Important PIC16F688 has two analog voltage comparators and eight 10-bit ADC channels. The inputs to the comparators are multiplexed with I/O port pins RA0, RA1, RC0 and RC1, while the outputs are multiplexed to pins RA2 and RC4. On a power-on reset, RA0, RA1, RC0, and RC1 are configured as analog inputs, as controlled by the CMCON0 register. In order to use these pins as digital inputs, the comparators must be turned OFF. This can be done by assigning decimal value 7 to CMCON0 register (CMCON0=0b00000111). Similarly, the PIC16F688 port pins that are multiplexed with ADC channel inputs are also configured as analog inputs on a power-on reset. The ANSEL register must be initialized to decimal 0 (ANSEL=0b00000000) to configure all ADC channels as digital inputs. The CMCON0 and ANSEL registers will be discussed in more detail later. Reference: Datasheet for PIC16F688 from Microchip. No related posts. Media Files 41203D.pdf (Adobe Acrobat Document, 3.6 MB) | | | | | | | | | | | | | | |