The communication interface of PCF8591 is I2C, so programming must comply with this protocol. The microcontroller initializes PCF8591 by sending a total of three bytes. The first byte, similar to EEPROM, is the device address byte, of which 7 bits represent the address and 1 bit represents the read/write direction. The high 4 bits of the address are fixed to 0b1001, and the low 3 bits are A2, A1, and A0. These three bits are connected to GND in our circuit, so they are 0b000, as shown in Figure 17-5.
Figure 17-5 PCF8591 address byte
Figure 17-5 PCF8591 address byte
The second byte sent to PCF8591 will be stored in the control register to control the function of PCF8591. The third and seventh bits are fixed to 0, and the other six bits have their own functions, as shown in Figure 17-6. I will introduce them one by one.
Figure 17-6 PCF8591 control byte
Figure 17-6 PCF8591 control byte
The sixth bit of the control byte is the DA enable bit. This bit is set to 1 to enable the DA output pin, which will generate an analog voltage output function. The fourth and fifth bits can be used to configure the 4 analog inputs of PCF8591 into single-ended mode and differential mode. The difference between single-ended mode and differential mode is introduced in Section 17.5. Here, you only need to know that these two bits are the control bits for configuring the AD input mode, as shown in Figure 17-7.
Figure 17-7 PCF8591 analog input configuration
Figure 17-7 PCF8591 analog input configuration
The second bit of the control byte is the auto-increment control bit. The meaning of auto-increment is that, for example, if we have 4 channels in total, when we use all of them, after reading channel 0, the next time we read, it will automatically enter channel 1 for reading, and we don’t need to specify the next channel. Since the data read by A/D each time is the result of the previous conversion, students should pay special attention to the fact that the current value read is the value of the previous channel when using the auto-increment function. In order to maintain the versatility of the program, our code does not use this function, and directly makes a general program.
Bits 0 and 1 of the control byte are the channel selection bits. 00, 01, 10, and 11 represent a total of 4 channel selections from 0 to 3.
The third byte sent to PCF8591 is the D/A data register, which indicates the voltage value of the D/A analog output. We will introduce D/A simulation later, and you only need to know the function of this byte. If we only use the A/D function, we don't need to send the third byte.
Next, we use a program to display the voltage values measured by AIN0, AIN1, and AIN3 on the LCD. At the same time, you can turn the potentiometer and find that the value of AIN0 changes.
/***************************Lcd1602.c file program source code********************************/
(Omitted here, please refer to the code in the previous section)
/********************************I2C.c file program source code*******************************/
(Omitted here, please refer to the code in the previous section)
/********************************main.c file program source code******************************/
#include
bit flag300ms = 1; //300ms timing flag
unsigned char T0RH = 0; //T0 high byte of reload value
unsigned char T0RL = 0; //Low byte of T0 reload value
void ConfigTimer0(unsigned int ms);
unsigned char GetADCValue(unsigned char chn);
void ValueToString(unsigned char *str, unsigned char val);
extern void I2CStart();
extern void I2CStop();
extern unsigned char I2CReadACK();
extern unsigned char I2CReadNAK();
extern bit I2CWrite(unsigned char dat);
extern void InitLcd1602();
extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);
void main(){
unsigned char val;
unsigned char str[10];
EA = 1; // Enable general interrupt
ConfigTimer0(10); //Configure T0 timing 10ms
InitLcd1602(); //Initialize LCD
LcdShowStr(0, 0, "AIN0 AIN1 AIN3"); //Show channel indication
while (1){
if (flag300ms){
flag300ms = 0; //Display the voltage of channel 0
val = GetADCValue(0); //Get the conversion value of ADC channel 0
ValueToString(str, val); //Convert voltage value to string format
LcdShowStr(0, 1, str); //Display on LCD
//Display the voltage of channel 1
val = GetADCValue(1);
ValueToString(str, val);
LcdShowStr(6, 1, str);
//Display the voltage of channel 3
val = GetADCValue(3);
ValueToString(str, val);
LcdShowStr(12, 1, str);
}
}
}
/* Read the current ADC conversion value, chn-ADC channel number 0~3 */
unsigned char GetADCValue(unsigned char chn){
unsigned char val;
I2CStart();
if (!I2CWrite(0x48<<1)){ //Address PCF8591. If no response, stop the operation and return 0
I2CStop();
return 0;
}
I2CWrite(0x40|chn); //Write control byte and select conversion channel
I2CStart();
I2CWrite((0x48<<1)|0x01); //Address PCF8591, specify the subsequent read operation
I2CReadACK(); //Read a byte first to provide sampling conversion time
val = I2CReadNAK(); //Read the value just converted
I2CStop();
return val;
}
/* ADC conversion value is converted to the string form of the actual voltage value, str-string pointer, val-AD conversion value*/
void ValueToString(unsigned char *str, unsigned char val){
//Voltage value = conversion result * 2.5V/255, where 25 implies a decimal place
val = (val*25) / 255;
str[0] = (val/10) + '0'; // integer character
str[1] = '.'; //decimal point
str[2] = (val%10) + '0'; //decimal character
str[3] = 'V'; //voltage unit
str[4] = '\0'; //Terminator
}
/* Configure and start T0, ms-T0 timing time*/
void ConfigTimer0(unsigned int ms){
unsigned long tmp; //temporary variable
tmp = 11059200 / 12; //Timer counting frequency
tmp = (tmp * ms) / 1000; //Calculate the required count value
tmp = 65536 - tmp; //Calculate the timer reload value
tmp = tmp + 12; //Compensate for the error caused by interrupt response delay
T0RH = (unsigned char)(tmp>>8); //Timer reload value is split into high and low bytes
T0RL = (unsigned char)tmp;
TMOD &= 0xF0; // Clear the control bit of T0
TMOD |= 0x01; //Configure T0 to mode 1
TH0 = T0RH; //Load T0 reload value
TL0 = T0RL;
ET0 = 1; // Enable T0 interrupt
TR0 = 1; //Start T0
}
/* T0 interrupt service function, execute 300ms timing*/
void InterruptTimer0() interrupt 1{
static unsigned char tmr300ms = 0;
TH0 = T0RH; //Reload the reload value
TL0 = T0RL;
tmr300ms++;
if (tmr300ms >= 30){ //timing 300ms
tmr300ms = 0;
flag300ms = 1;
}
}
Students who read the program carefully will find that when the program performs A/D reading, it uses two programs to read 2 bytes: I2CReadACK(); val = I2CReadNAK(); The conversion clock of PCF8591 is the SCL of I2C, and 8 SCL cycles complete a conversion, so the current conversion result can only be read on the 8 SCL of the next byte. Therefore, the first statement here is used to generate an overall SCL clock to provide PCF8591 for A/D conversion, and the second is to read the current conversion result. If we only use the second statement, each time we read the previous conversion result.
Previous article:MCU SPI communication interface
Next article:A problem that needs attention in the multiplication operation of MSP430G2333 lower computer
- Popular Resources
- Popular amplifiers
- Learn ARM development(16)
- Learn ARM development(17)
- Learn ARM development(18)
- Embedded system debugging simulation tool
- A small question that has been bothering me recently has finally been solved~~
- Learn ARM development (1)
- Learn ARM development (2)
- Learn ARM development (4)
- Learn ARM development (6)
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- LED chemical incompatibility test to see which chemicals LEDs can be used with
- Application of ARM9 hardware coprocessor on WinCE embedded motherboard
- What are the key points for selecting rotor flowmeter?
- LM317 high power charger circuit
- A brief analysis of Embest's application and development of embedded medical devices
- Single-phase RC protection circuit
- stm32 PVD programmable voltage monitor
- Introduction and measurement of edge trigger and level trigger of 51 single chip microcomputer
- Improved design of Linux system software shell protection technology
- What to do if the ABB robot protection device stops
- CGD and Qorvo to jointly revolutionize motor control solutions
- CGD and Qorvo to jointly revolutionize motor control solutions
- Keysight Technologies FieldFox handheld analyzer with VDI spread spectrum module to achieve millimeter wave analysis function
- Infineon's PASCO2V15 XENSIV PAS CO2 5V Sensor Now Available at Mouser for Accurate CO2 Level Measurement
- Advanced gameplay, Harting takes your PCB board connection to a new level!
- Advanced gameplay, Harting takes your PCB board connection to a new level!
- A new chapter in Great Wall Motors R&D: solid-state battery technology leads the future
- Naxin Micro provides full-scenario GaN driver IC solutions
- Interpreting Huawei’s new solid-state battery patent, will it challenge CATL in 2030?
- Are pure electric/plug-in hybrid vehicles going crazy? A Chinese company has launched the world's first -40℃ dischargeable hybrid battery that is not afraid of cold
- [Bluesun AB32VG1 RISC-V evaluation board] IDE installation and use
- It's New Year's Eve soon. I wish you all a happy New Year.
- 【DIY Creative LED】Add touch button and install it on LED lamp
- [RISC-V MCU CH32V103 Review] - 2: Making an LED flash is not easy
- SensorTile.box uses air pressure sensors to analyze environmental changes
- When using an oscilloscope probe to measure the AC waveform between the two ends of a component, how should the probe ground wire be connected?
- 【ST NUCLEO-H743ZI Review】(2)Ethernet Test
- TI.com Online Purchasing Special (Smart Building) has a limited time offer, with discounts as low as 30%!
- DLP Lightcrafter 4500 EVM FAQ Summary
- NRF24L01 module usage