In the second part of the PCF8591 library, we shall build the library to interface the module with STM32F4.
In this guide, we shall cover the following:
- Creating the header and source file.
- Developing the header file.
- Developing the source file.
- Testing the library.
4. Creating the Header and Source File:
After the project has been generated by STM32CubeMX within STM32CubeIDE, we shall add new source file and header file.
Right click on Src folder and add new source file as following:
Give a name for the source file, we shall name it as PCF8591.c as following:
In similar manner, right click on Inc folder and add new header file with name of PCF8591.h.
5. Developing the Header File:
Open PCF8591.h then declare the following enumeration to handle the single ended conversion as following:
First, include the stdint header file:
#include "stdint.h"
Then the enumeration:
typedef enum { CH0=0, CH1=1, CH2=2, CH3=3 }ADC_ChannelTypedef;
This is taken from the datasheet as following:
Since the module supports the differential mode, we shall declare the following enumeration to handle the differential reading as following:
typedef enum { Diff_AN0_AN1=0, Diff_AN2_AN3=1 }DAC_DiffTypedef;
Next, declare the following function prototypes:
void PCF8591_SetAddress(uint8_t address);
This function shall change the address of the module if there were changes to the address pins as following:
Next, single ended read function as following:
uint8_t ReadADC_SingleEnded(ADC_ChannelTypedef channel);
The function measure single channel of the ADC and returns the measured values and take the channel number.
In similar manner, the differential function as following:
uint8_t ReadADC_Differential(DAC_DiffTypedef channel);
It will return the measured differential value and takes which channel.
Finally, set DAC value function as following:
void Set_DAC_Value(uint8_t DACValue);
The function shall set the DAC output.
Hence, the entire header file as following:
#ifndef INC_PCF8591_H_ #define INC_PCF8591_H_ #include "stdint.h" typedef enum { CH0=0, CH1=1, CH2=2, CH3=3 }ADC_ChannelTypedef; typedef enum { Diff_AN0_AN1=0, Diff_AN2_AN3=1 }DAC_DiffTypedef; void PCF8591_SetAddress(uint8_t address); uint8_t ReadADC_SingleEnded(ADC_ChannelTypedef channel); uint8_t ReadADC_Differential(DAC_DiffTypedef channel); void Set_DAC_Value(uint8_t DACValue); #endif /* INC_PCF8591_H_ */
6. Developing the Source File:
Open PCF8591.c source file.
We start by including the following header file:
#include "PCF8591.h" #include "i2c.h"
Next, declare a variable to hold the address of the PCF8591 as following:
static uint8_t PCF8591_Addr = 0x48<<1;
The shift to left is needed to add the read/write bit as needed by the I2C standard.
Note: This is for STM32F4Cube HAL API. Other MCU might do that in the API itself.
For changing the address of the module:
void PCF8591_SetAddress(uint8_t address) { PCF8591_Addr=address<<1; }
For reading single ended channel function:
uint8_t ReadADC_SingleEnded(ADC_ChannelTypedef channel) { uint8_t tempData[2]; uint8_t dataWriteTemp=0x40|channel; HAL_I2C_Mem_Read(&hi2c1, PCF8591_Addr, dataWriteTemp, 1, tempData, 2, 100); return tempData[1]; }
First, declare buffer of two byte to hold the read bytes.
Variable to hold the data to be written which is the control byte ored with the channel.
Read the memory address of 0x40|channel and return the second byte of the buffer since the first byte is dummy byte.
In similar manner, for differential channel reading:
uint8_t ReadADC_Differential(DAC_DiffTypedef channel) { uint8_t controlByte = channel; // 0x00: AIN0-AIN1, 0x01: AIN2-AIN3 uint8_t dummyRead, adcValue; // Send control byte to configure differential mode HAL_I2C_Master_Transmit(&hi2c1, PCF8591_ADDR, &controlByte, 1, 100); // Perform a dummy read to settle the ADC HAL_I2C_Master_Receive(&hi2c1, PCF8591_ADDR, &dummyRead, 1, 100); // Read actual ADC value HAL_I2C_Master_Receive(&hi2c1, PCF8591_ADDR, &adcValue, 1, 100); return adcValue; // Return the ADC value (0–255) }
For setting the DAC value:
void Set_DAC_Value(uint8_t DACValue) { uint8_t data[2]; data[0] = 0x40; // Control byte: Enable DAC data[1] = DACValue; // Digital value (0 to 255) HAL_I2C_Master_Transmit(&hi2c1, PCF8591_Addr, data, 2, 100); }
Hence, the source file as following:
#include "PCF8591.h" #include "i2c.h" static uint8_t PCF8591_Addr = 0x48<<1; void PCF8591_SetAddress(uint8_t address) { PCF8591_Addr=address<<1; } uint8_t ReadADC_SingleEnded(ADC_ChannelTypedef channel) { uint8_t tempData[2]; uint8_t dataWriteTemp=0x40|channel; HAL_I2C_Mem_Read(&hi2c1, PCF8591_Addr, dataWriteTemp, 1, tempData, 2, 100); return tempData[1]; } uint8_t ReadADC_Differential(DAC_DiffTypedef channel) { uint8_t controlByte = channel; // 0x00: AIN0-AIN1, 0x01: AIN2-AIN3 uint8_t dummyRead, adcValue; // Send control byte to configure differential mode HAL_I2C_Master_Transmit(&hi2c1, PCF8591_ADDR, &controlByte, 1, 100); // Perform a dummy read to settle the ADC HAL_I2C_Master_Receive(&hi2c1, PCF8591_ADDR, &dummyRead, 1, 100); // Read actual ADC value HAL_I2C_Master_Receive(&hi2c1, PCF8591_ADDR, &adcValue, 1, 100); return adcValue; // Return the ADC value (0–255) } void Set_DAC_Value(uint8_t DACValue) { uint8_t data[2]; data[0] = 0x40; // Control byte: Enable DAC data[1] = DACValue; // Digital value (0 to 255) HAL_I2C_Master_Transmit(&hi2c1, PCF8591_Addr, data, 2, 100); }
That all for the source file.
7. Testing the Library;
Before testing the library, connect the DAC output of the module to the AIN0 of the module since we shall use DAC to read the voltage generated by the DAC.
Open the main.c file.
In user code begin Includes, include the following header files:
#include "PCF8591.h" #include "stdio.h"
In user code begin PV, declare the following variables:
uint8_t adc_data; uint8_t uart_buffer[100];
In while 1 loop:
uint16_t len; len=sprintf(uart_buffer,"------------------\r\n"); HAL_UART_Transmit(&huart2, &uart_buffer, len, 100); Set_DAC_Value(128); adc_data=ReadADC_SingleEnded(CH0); len= sprintf(uart_buffer,"ADC Value = %d\r\n",adc_data); HAL_UART_Transmit(&huart2, &uart_buffer, len, 100); HAL_Delay(2000); Set_DAC_Value(255); adc_data=ReadADC_SingleEnded(CH0); len= sprintf(uart_buffer,"ADC Value = %d\r\n",adc_data); HAL_UART_Transmit(&huart2, &uart_buffer, len, 100); HAL_Delay(2000); Set_DAC_Value(128); adc_data=ReadADC_SingleEnded(CH0); len= sprintf(uart_buffer,"ADC Value = %d\r\n",adc_data); HAL_UART_Transmit(&huart2, &uart_buffer, len, 100); HAL_Delay(2000); Set_DAC_Value(0); adc_data=ReadADC_SingleEnded(CH0); len= sprintf(uart_buffer,"ADC Value = %d\r\n",adc_data); HAL_UART_Transmit(&huart2, &uart_buffer, len, 100); HAL_Delay(2000);
The len variable to hold the number of characters to be transmitted by the UART.
First, set the DAC to 128, then read CH0 and print the results.
In similar manner, set the DAC to 255, then 128 then 0.
Save the project, build it and run it on your board as following:
Open your favourite serial terminal application, set the baudrate to be 115200 and you should get the following:
The results are near the set value.
Happy coding 😉
Add Comment