2017年8月28日 星期一

Read DS18B20 Temperature via Mapping Memory Method in Linux


    In the website, there are lots code to demonstrate how to set and fetch the temperature data measured by ds18b20, but Most are drive-based, some are UART-base;  there are only a few code fetching data via mapping memory method.

    Basically, programmer should avoid to insert driver as possible as he can, for driver within defect may entail whole system would not work or be disordered. Accessing mapped memory is a much better solution from my perspective.

    This post is to complement my previous article, to demonstrate another example of
how to manipulate GPIO for transmission of data via mapped memory.

DS18B20 is a good digitall temperature measurer featuring its high precision from -10 ~ 85 degree, and supporting multi-sensor on one wire(GPIO). And it is very easy to buy a DS18B20 in cable form, which is very useful to measure the temperature of liquid.


For DS18B20 is pull-up based voltage, you need to connect Vcc pin to Data pin with a resistor in middle value (2k Ohm- 10k, 4.7k is the most typical), even you use the water-proof one.





According to DS18B20 datasheet, the float chart/pesudo-code is :


---Reset DS18B20 ---
 pull down the pin for 600 us
 read the pin, and the pin would transmit to down within 60 us.
 the pin would be down at max of 240 us.
 the pin would be up
---END Reset DS18B20 END---


--- SetDS18B20 Resolution ---
-Write command SKIP_DS18B20_ROM-
-Write command READ_DS18B20_RAM-
- Write 2 dummy bytes -
- Write resultion -
---END SetDS18B20 Resolution END---



-Write command SKIP_DS18B20_ROM-
-Write command CONVERT_DS18B20_TEMP-


---Wait DS18B20 Converting Done---
wait the pin be high, the max time according to reslultion
---END Wait DS18B20 Converting Done END---


-Write command SKIP_DS18B20_ROM-
-Write command READ_DS18B20_RAM-

-Read DS18B20 Bytes-

- Convert the raw data to temperature - 


========================================


--Read DS18B20---

-- for 8 bits, one bit costs one time slot, time slot min =  60 us --
pull down the pin for over 1  us
wait a piece of time within 15 us.
read the pin, high is 1, low is 0
wait the time slot be ended.
--end for --

---End Read DS18B20---



--Write DS18B20---

-- for 8 bits, one bit costs one time slot, time slot min = 60 us --
pull down the pin for over 1 us
within 15 us, set the pin be high(1) or low(0)
wait the time slot be ended.
--end for --

---End Write DS18B20---


The corresponding code for OpenWrt is :

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include <signal.h>

#include <sys/time.h>
#include <sys/mman.h>

#include <fcntl.h>


#include <errno.h>



#define GPIO_ADDR        (0x18040000)
#define GPIO_MEM_BLOCK_SIZE     (48)


int MappingGPIOToMemory(unsigned long **pp_gpio_address)
{
 int mem_fd;

 if ((mem_fd = open("/dev/mem", O_RDWR)) < 0)
 {
  printf("Open /dev/mem fail\r\n");
  return -1;
 }/*if */

 *pp_gpio_address = (unsigned long*)mmap(NULL, GPIO_MEM_BLOCK_SIZE,
  PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, GPIO_ADDR);

 close(mem_fd);

 if (*pp_gpio_address == MAP_FAILED)
 {
  printf("mapping memory fail\r\n");
  printf("error message = %s\r\n", strerror(errno));
  return -2;
 }

}/*MappingGPIOToMemory*/


#define PIN_OUT        (1)
#define PIN_IN         (0)


void SetGPIODirection(unsigned long *p_gpio_address, int gpio_numer, int direction)
{
#define GPIO_OUTPUT_ENABLE_OFFSET   (0)

 unsigned long value;
 value = *(p_gpio_address + GPIO_OUTPUT_ENABLE_OFFSET);

 if (PIN_IN == direction)
  value &= ~(1 << gpio_numer);
 else
  value |= (1 << gpio_numer);

 *(p_gpio_address + GPIO_OUTPUT_ENABLE_OFFSET) = value;

}/*SetGPIODirection*/

#define HIGH         (1)
#define LOW         (0)

void SetGPIOValue(unsigned long *p_gpio_address, int gpio_numer, int value)
{

#if(1)

#define GPIO_OUT_VALUE_OFFSET    (2)

 unsigned long full_register_value;

 full_register_value = *(p_gpio_address + GPIO_OUT_VALUE_OFFSET);

 if (0 == value)
  full_register_value &= ~(1 << gpio_numer);
 else
  full_register_value |= (1 << gpio_numer);

 *(p_gpio_address + GPIO_OUT_VALUE_OFFSET) = full_register_value;

#else

#define GPIO_SET_OFFSET      (3)
#define GPIO_CLEAR_OFFSET     (4)

 if(0 == value)
  *(p_gpio_address + GPIO_CLEAR_OFFSET) = (1 << gpio_numer);
 else
  *(p_gpio_address + GPIO_SET_OFFSET) = (1 << gpio_numer);

#endif

}/*SetGPIOValue*/


void GetGPIOValue(unsigned long *p_gpio_address, int gpio_numer, int *p_value)
{
#define GPIO_INPUT_VALUE_OFFSET    (1)
 unsigned long full_register_value;

 full_register_value = *(p_gpio_address + GPIO_INPUT_VALUE_OFFSET);

 *p_value = (full_register_value >> gpio_numer) & 0x01;
}/*GetGPIOValue*/


void delay_micro_sec(unsigned int delay_time_in_us)
{
 struct timeval now;
 struct timeval period;
 struct timeval end;

 gettimeofday(&now, NULL);

 period.tv_sec = delay_time_in_us / 1000000;
 period.tv_usec = delay_time_in_us % 1000000;

 timeradd(&now, &period, &end);

 while(timercmp(&now, &end, < ))
  gettimeofday(&now, NULL);

}/*delay_micro_sec*/




enum DS18B20_Resolution
{
 DS18B20_9_Bit_Resolution = 0,
 DS18B20_10_Bit_Resolution = 1,
 DS18B20_11_Bit_Resolution = 2,
 DS18B20_12_Bit_Resolution = 3,
} ;


int ResetDS18B20(unsigned long *p_gpio_address, int gpio_numer)
{
 unsigned int time_cnt;

 SetGPIODirection(p_gpio_address, gpio_numer, PIN_OUT);

 //SetGPIOValue(p_gpio_address, gpio_numer, HIGH);
 //delay_micro_sec(2);

#define RESET_DS18B20_PULL_DOWN_TIME_IN_USEC   (600)
 SetGPIOValue(p_gpio_address, gpio_numer, LOW);
 delay_micro_sec(RESET_DS18B20_PULL_DOWN_TIME_IN_USEC);


 SetGPIODirection(p_gpio_address, gpio_numer, PIN_IN);
#define DS18B20_RESISTOR_PULL_UP_IN_USEC    (60)
 time_cnt = 0;
 while(1)
 {
  int pin_value;

  GetGPIOValue(p_gpio_address, gpio_numer, &pin_value);
  delay_micro_sec(1);

  if(LOW == pin_value)
   break;

  if(DS18B20_RESISTOR_PULL_UP_IN_USEC == time_cnt)
   break;
  time_cnt++;
 }/*while high*/

 if(DS18B20_RESISTOR_PULL_UP_IN_USEC == time_cnt)
 {
  printf("RESTIOR_PULL_UP_ERROR\r\n");
  return -1;
 }/*over time*/


#define DS18B20_RESP_PULL_DOWN_IN_USEC     (240)
 time_cnt = 0;
 while(1)
 {
  int pin_value;

  GetGPIOValue(p_gpio_address, gpio_numer, &pin_value);
  delay_micro_sec(1);

  if(HIGH == pin_value)
   break;

  if(DS18B20_RESP_PULL_DOWN_IN_USEC == time_cnt)
   break;
  time_cnt++;
 }/*while low*/


 if(DS18B20_RESP_PULL_DOWN_IN_USEC == time_cnt)
 {
  printf("DS18B20_RESP_PULL_DOWN\r\n");
  return -2;
 }/*over time*/
#define DS18B20_RESP_TOTAL_TIME_IN_USEC    (480)

 delay_micro_sec(DS18B20_RESP_TOTAL_TIME_IN_USEC - time_cnt);
 return 0;
}/*ResetDS18B20*/


int ReadDS18B20Byte(unsigned long *p_gpio_address, int gpio_numer,
 unsigned char *p_datum)
{
 int i;
 unsigned char datum;
 int one_bit_value;

 datum = 0;

 for(i = 0; i < 8; i++){

  SetGPIODirection(p_gpio_address, gpio_numer, PIN_OUT);
  SetGPIOValue(p_gpio_address, gpio_numer, LOW);
#define DS18B20_MASTER_PULL_DOWN_TIME_FOR_READ_IN_USEC   (2)
  delay_micro_sec(DS18B20_MASTER_PULL_DOWN_TIME_FOR_READ_IN_USEC);

  SetGPIODirection(p_gpio_address, gpio_numer, PIN_IN);

#define DS18B20_RECOVER_TIME_AFTER_PULL_DOWN_FOR_READ_IN_USEC (5)
  delay_micro_sec(DS18B20_RECOVER_TIME_AFTER_PULL_DOWN_FOR_READ_IN_USEC);
  GetGPIOValue(p_gpio_address, gpio_numer, &one_bit_value);

  datum |= (one_bit_value << i);

#define DS18B20_ONE_BIT_READ_TIME_IN_USEC      (62)
  delay_micro_sec(DS18B20_ONE_BIT_READ_TIME_IN_USEC
   - DS18B20_RECOVER_TIME_AFTER_PULL_DOWN_FOR_READ_IN_USEC
   - DS18B20_MASTER_PULL_DOWN_TIME_FOR_READ_IN_USEC);
 }/*for one byte*/

 //SetGPIODirection(p_gpio_address, gpio_numer, PIN_IN);
 *p_datum = datum;

 return 0;
}/*ReadDS18B20Byte*/


int WriteDS18B20Byte(unsigned long *p_gpio_address, int gpio_numer,
 unsigned char data)
{
 int i;

 SetGPIODirection(p_gpio_address, gpio_numer, PIN_OUT);

 for(i = 0; i < 8; i++){

#define DS18B20_RECOVER_TIME_FOR_INDIVIDUAL_SLOT_WRITE_IN_USEC  (2)
  SetGPIOValue(p_gpio_address, gpio_numer, LOW);
  delay_micro_sec(DS18B20_RECOVER_TIME_FOR_INDIVIDUAL_SLOT_WRITE_IN_USEC);

#define DS18B20_ONE_BIT_WRITE_TIME_IN_USEC       (60)
  if(0x01 & data)
   SetGPIOValue(p_gpio_address, gpio_numer, HIGH);
  else
   SetGPIOValue(p_gpio_address, gpio_numer, LOW);

  delay_micro_sec(DS18B20_ONE_BIT_WRITE_TIME_IN_USEC
   - DS18B20_RECOVER_TIME_FOR_INDIVIDUAL_SLOT_WRITE_IN_USEC);

  data >>= 1;

#define DS18B20_ONE_BIT_WRITE_END_PULL_UP_TIME_IN_USEC    (1)
  SetGPIOValue(p_gpio_address, gpio_numer, HIGH);
  delay_micro_sec(DS18B20_ONE_BIT_WRITE_END_PULL_UP_TIME_IN_USEC);
 }/*for 8 bit*/

 //SetGPIODirection(p_gpio_address, gpio_numer, PIN_IN);

 return 0;
}/*WriteDS18B20Byte*/


int WriteDS18B20Command(unsigned long *p_gpio_address, int gpio_numer,
 unsigned char command_code)
{
 return WriteDS18B20Byte(p_gpio_address, gpio_numer, command_code);
}/*SendDS18B20Command*/


int WaitDS18B20TemperatureConvertingDone(unsigned long *p_gpio_address, int gpio_numer,
 enum DS18B20_Resolution resolution)
{
 unsigned int time_cnt;
 unsigned int max_duration_time_in_msec;
#define DS18B20_MIN_CONVERT_TIME_IN_MSEC     (750/8)
#define BUFFER_TIME           (100/8)

 max_duration_time_in_msec
  = (1 >> resolution)*(DS18B20_MIN_CONVERT_TIME_IN_MSEC + BUFFER_TIME);

 SetGPIODirection(p_gpio_address, gpio_numer, PIN_IN);
 delay_micro_sec(1);

 while(1)
 {
  int pin_value;

  GetGPIOValue(p_gpio_address, gpio_numer, &pin_value);
  delay_micro_sec(1);

  if(1 == pin_value)
   break;

  if(max_duration_time_in_msec == time_cnt)
   break;
  time_cnt++;
 }/*while 1*/

 if(max_duration_time_in_msec == time_cnt)
 {
  printf("Convert fail\r\n");
  return -1;
 }

 return 0;

}/*WaitDS18B20TemperatureConvertingDone*/


unsigned char CRCiButtonUpdate(unsigned char crc, unsigned char data)
{
 int i;

 crc = crc ^ data;

#define CRC_POLY        (0x8C)

 for(i = 0; i < 8; i++){

  if (crc & 0x01)
   crc = (crc >> 1) ^ CRC_POLY;
  else
   crc >>= 1;
 }/*for i*/

 return crc;
}/*CRCiButtonUpdate*/

#define SKIP_DS18B20_ROM       (0xCC)
#define CONVERT_DS18B20_TEMP      (0x44)
#define WRITE_DS18B20_RAM        (0x4E)
#define READ_DS18B20_RAM       (0xBE)


int SetDS18B20Resolution(unsigned long *p_gpio_address, int gpio_numer,
 enum DS18B20_Resolution resolution)
{
 if(0 != ResetDS18B20(p_gpio_address, gpio_numer))
 {
  printf("ResetDS18B20 error in %s\r\n", __func__);
  return -1;
 }/*avoid the reseting does not be called in the begin*/


 WriteDS18B20Command(p_gpio_address, gpio_numer, SKIP_DS18B20_ROM);
 WriteDS18B20Command(p_gpio_address, gpio_numer, WRITE_DS18B20_RAM);

 {
#define DS18B20_MINUS_SIGN(VALUE)     ((0x1<<7)|(VALUE))
  WriteDS18B20Byte(p_gpio_address, gpio_numer, 100);
  WriteDS18B20Byte(p_gpio_address, gpio_numer, DS18B20_MINUS_SIGN(55));
 }/*useless block to fill the head 2 bytes of write ram */

 WriteDS18B20Byte(p_gpio_address, gpio_numer, ((0x03 & resolution) << 5) | 0x1f );

 if(0 != ResetDS18B20(p_gpio_address, gpio_numer))
 {
  printf("%s error\r\n", __func__);
  return -1;
 }/*if ResetDS18B20 error*/

 return 0;
}/*SetDS18B20Resolution*/


int ReadDS18B20Temperature(unsigned long *p_gpio_address, int gpio_numer,
 enum DS18B20_Resolution resolution, float *p_temperature)
{

 if(0 != ResetDS18B20(p_gpio_address, gpio_numer))
 {
  printf("ResetDS18B20 1st error\r\n");
  return -1;
 }

 SetDS18B20Resolution(p_gpio_address, gpio_numer, resolution);


 WriteDS18B20Command(p_gpio_address, gpio_numer, SKIP_DS18B20_ROM);
 WriteDS18B20Command(p_gpio_address, gpio_numer, CONVERT_DS18B20_TEMP);

 if(0 != WaitDS18B20TemperatureConvertingDone(p_gpio_address, gpio_numer, resolution))
 {
  return -3;
  printf("Temperature converting error\r\n");
 }

 if(0 != ResetDS18B20(p_gpio_address, gpio_numer))
 {
  printf("ResetDS18B20 3nd error\r\n");
  return -4;
 }



 WriteDS18B20Command(p_gpio_address, gpio_numer, SKIP_DS18B20_ROM);
 WriteDS18B20Command(p_gpio_address, gpio_numer, READ_DS18B20_RAM);

 {
  short temperature;
  unsigned char raw_temperature[2];

  int i;
  unsigned char byte_datum;
  unsigned char computed_crc, read_crc;

  computed_crc = 0x00;
  for(i = 0; i< 8; i++){
   ReadDS18B20Byte(p_gpio_address, gpio_numer, &byte_datum);
   computed_crc = CRCiButtonUpdate(computed_crc, byte_datum);

   if(0 == i)
    raw_temperature[0] = byte_datum;

   if(1 == i)
    raw_temperature[1] = byte_datum;

  }/*for i */

  ReadDS18B20Byte(p_gpio_address, gpio_numer, &read_crc);

  if(read_crc != computed_crc)
  {
   //printf("read_crc = %d, computed_crc = %d\r\n",
   // read_crc, computed_crc);
   return -4;
  }

  raw_temperature[0] >>= (3 - resolution);
  raw_temperature[0] <<= (3 - resolution);

  temperature = raw_temperature[0] + (raw_temperature[1] << 8);

#if(0)
  if(0x01 & (temperature >> 11))
    temperature = -1* (0x07ff & temperature);
#else
  temperature = ((temperature >> 11) | 0x01) * (0x07ff & temperature);
#endif

  *p_temperature = temperature/16.0;
 }
 return 0;
}/*ReadDS18B20*/


unsigned long *p_gpio_address = NULL;

void InterruptSignalHandlingRoutine(int sig)
{
 if(NULL != p_gpio_address)
  munmap(p_gpio_address, GPIO_MEM_BLOCK_SIZE);
 p_gpio_address = NULL;

 exit(0);
}/*InterruptSignalHandlingRoutine*/


void print_current_time(void)
{
 time_t t;
 struct tm calendar_time;
 t = time(NULL);
 localtime_r(&t, &calendar_time);

 printf("now time: %d-%d-%d %d:%d:%d, %s(+%d)\n",
  calendar_time.tm_year + 1900, calendar_time.tm_mon + 1,
  calendar_time.tm_mday,
  calendar_time.tm_hour, calendar_time.tm_min, calendar_time.tm_sec,
  calendar_time.tm_zone, (int)calendar_time.tm_gmtoff/3600);
}/*print_current_time*/


int main(int argc, char *argv[])
{

 float temperature;
 int executed_count;
 int succeeded_count;
 int g_gpio_number;
 int resolution;

 resolution = DS18B20_12_Bit_Resolution;
 signal(SIGINT, InterruptSignalHandlingRoutine);

 if(2 > argc)
 {
  printf("%s should be followed by led_gpio_pin_number\r\n", argv[0]);
  return -1;
 }

 //if(1 < argc)
 {
  char *p_temp;
  g_gpio_number = strtol(argv[1], &p_temp, 10);
  if(argv[1]== p_temp || g_gpio_number < 0)
  {
   printf("%s is not a number for specifying gpio pin number\r\n", argv[1]);
   return -2;
  }/*not a number*/

 }/*local variable*/


 if(2 < argc)
 {
  char *p_temp;
  resolution = strtol(argv[2], &p_temp, 10);
  if(argv[2]== p_temp)
  {
   printf("%s is not a number to specify resolution\r\n", argv[2]);
   return -3;
  }/*not a number*/

  if(resolution > 12 || resolution < 9)
  {
   printf("%s should be >= 9 and <= 12 \r\n", argv[2]);
   return -3;
  }
 }/*local variable*/


 MappingGPIOToMemory(&p_gpio_address);

 while(1)
 {
  if(0 == ReadDS18B20Temperature(p_gpio_address, g_gpio_number,
   (enum DS18B20_Resolution)(resolution - 9), &temperature))
  {
   printf("temperature = %7.4f\r\n\r\n", temperature);
   print_current_time();
   usleep(3*1000*1000);
  }

 }

 if(NULL != p_gpio_address)
  munmap(p_gpio_address, GPIO_MEM_BLOCK_SIZE);
 p_gpio_address = NULL;

 return 0;
}/*main*/

My code for GPIO is based on Atheros AR9330@MIPS, if your CPU is not the same as mine, you need to re-implement the GPIO manipulation functions.

The makefile is just to adopt cross(parasitic)-compiler to build the code:


OPENWRT_ROOT=/home/openwrt-cc/staging_dir
OPENWRT_TOOLCHAIN_PATH = $(OPENWRT_ROOT)/toolchain-mips_34kc_gcc-4.8-linaro_uClibc-0.9.33.2
CC = $(OPENWRT_TOOLCHAIN_PATH)/bin/mips-openwrt-linux-gcc



CFLAGS := -O2
all:
        $(CC)  $(CFLAGS)   main.c  -o ds18b20_temperature
clean:
        rm ds18b20_temperature -f


Run the binary:

The first argument is the GPIO connected with DS18B20 sensor, the second one is resolution, which is to specify how many bit for describing the temperature, from 9 to 12.

Console output:

root@GL-AR150:/tmp# ./ds18b20_temperature 26 9 #gpio = 26, reslution = 9(to 1/2)
temperature = 32.0000

now time: 2017-8-28 15:33:20, CST(+8)
temperature = 31.5000

now time: 2017-8-28 15:33:23, CST(+8)
temperature = 32.0000


root@GL-AR150:/tmp# ./ds18b20_temperature 26 12 #gpio = 26, resolution = 12(to 2^-4 =0.0625)
temperature = 31.2500

now time: 2017-8-28 15:36:49, CST(+8)
temperature = 31.3125

now time: 2017-8-28 15:36:52, CST(+8)
temperature = 31.3750

now time: 2017-8-28 15:36:55, CST(+8)
temperature = 31.2500

now time: 2017-8-28 15:36:58, CST(+8)
temperature = 31.3750

now time: 2017-8-28 15:37:1, CST(+8)
temperature = 31.4375

 GPIO 26 is the DS18B20 embedded in my development board.

The measured temperatures need to be compensated 10 degree, But I do know why.  The weather  is impossible so hot in Taiwan even it is the beginning of autumn.

2017年8月19日 星期六

Linux GPIO Manipulation via Accessing Mapped Physical Memory


      Form my previous post, I have demonstrated how to manipulate GPIO via sysfs and I have pointed out the insufficiency of this manipulation for sysfs speed is very slow. Sysfs accessing speed is in order of milli-second(KHz),  but the most peripheral chips communication speed are  in the order of micro-second(MHz). That fact leads the sysfs accessing is  limited for LEDs and buttons only.

     In this post, I will compliment the incompletion of sysfs accessing in low speed but without writing a driver to manipulating GPIO still. The driver is the extension of operation system (not only for Linux, aslo for Windows ). Applying a driver implicate the operation system has been patched. It is very natural that the code workers for developing applications should not modify  the operation system as possible as they could. Even for the code workers in porting, they should install the driver as less as possible, for the driver debugging makes the code worker in trepidation.

   
     

     I demonstrate how to read DHT11 data via GPIO in here.

     DHT11 is a sensor from 天朝 Celestial Empire widely adopted in the cheap electronic devices or the makers,  to measure humidity and Temperature via ONE GPIO pin. The communication speed between the sensor and host is  in the order of micro-sceond, a typical middle-speed signal transmission rate.
    The DHT11 I use has been modularized instead of a naked one, it integrates with the required capacity and resistor, so I could wave away the circuit issue.
   the connection is extremely intuitive: for I adopt GPIO pin 18 as my the object of my accessing, I connect the data  pin to GPIO pin 18, Vcc to Vcc, ground to ground.

My Device is an OpenWRT system :

root@GL-AR150:/tmp# cat /proc/cpuinfo 
system type  : Atheros AR9330 rev 1
machine   : TP-LINK TL-WR720N v3
processor  : 0
cpu model  : MIPS 24Kc V7.4
BogoMIPS  : 265.42
wait instruction : yes
microsecond timers : yes
tlb_entries  : 16
extra interrupt vector : yes
hardware watchpoint : yes, count: 4, address/irw mask: [0x0000, 0x0008, 0x0020, 0x0000]
isa   : mips1 mips2 mips32r1 mips32r2
ASEs implemented : mips16
shadow register sets : 1
kscratch registers : 0
core   : 0
VCED exceptions  : not available
VCEI exceptions  : not available


   一.  Look up the Atheros AR9330 document, in the page 65,  The table :


Address Name Description
0x18040000 GPIO_OE General Purpose I/O Output Enable
0x18040004 GPIO_IN General Purpose I/O Input Value
0x18040008 GPIO_OUT General Purpose I/O Output Value
0x1804000C GPIO_SET General Purpose I/O Bit Set
0x18040010 GPIO_CLEAR General Purpose I/O Per Bit Clear


That is :

0. One bit represents one GPIO pin.

1.  The address 0x1804000 is the base address of GPIOs,  which's bits is able to be set  to specified if the pins are output(1) or not (input, 0).

2.  The bits in address 0x1804004 is for reading the input values, while the corresponding GPIO pins have been set  as input pins.

3. The bits in address 0x1804008 is for setting the values for output, while the corresponding GPIO pins have been set as output pins.

4. (Synonym for 3). Set the bits in 0x180400C means the value in the output pins is 1.

5. (Synonym for 3 and 4). Set the bits in 0x1804010 means the value in the output pins is 0.


The function to achieve those operation in C are :


#include <sys/mman.h>

#include <fcntl.h>


#include <errno.h>



#define GPIO_ADDR        0x18040000 // base address
#define GPIO_MEM_BLOCK_SIZE     (48)   // memory block size


int MappingGPIOToMemory(unsigned long **pp_gpio_address)
{
 int mem_fd;

 if ((mem_fd = open("/dev/mem", O_RDWR)) < 0)
 {
  printf("Open /dev/mem fail\r\n");
  return -1;
 }/*if */

 *pp_gpio_address = (unsigned long*)mmap(NULL, GPIO_MEM_BLOCK_SIZE,
  PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, GPIO_ADDR);

 close(mem_fd);

 if (*pp_gpio_address == MAP_FAILED)
 {
  printf("mapping memory fail\r\n");
  printf("error message = %s\r\n", strerror(errno));
  return -2;
 }

}/*MappingGPIOToMemory*/


#define PIN_OUT        (1)
#define PIN_IN         (0)


void SetGPIODirection(unsigned long *p_gpio_address, int gpio_numer, int direction)
{
#define GPIO_OUTPUT_ENABLE_OFFSET   (0)

 unsigned long value;
 value = *(p_gpio_address + GPIO_OUTPUT_ENABLE_OFFSET);

 if (PIN_IN == direction)
  value &= ~(1 << gpio_numer);
 else
  value |= (1 << gpio_numer);

 *(p_gpio_address + GPIO_OUTPUT_ENABLE_OFFSET) = value;

}/*SetGPIODirection*/


void SetGPIOValue(unsigned long *p_gpio_address, int gpio_numer, int value)
{

#if(1)

#define GPIO_OUT_VALUE_OFFSET    (2)

 unsigned long full_register_value;

 full_register_value = *(p_gpio_address + GPIO_OUT_VALUE_OFFSET);

 if (0 == value)
  full_register_value &= ~(1 << gpio_numer);
 else
  full_register_value |= (1 << gpio_numer);

 *(p_gpio_address + GPIO_OUT_VALUE_OFFSET) = full_register_value;

#else

#define GPIO_SET_OFFSET      (3)
#define GPIO_CLEAR_OFFSET     (4)

 if(0 == value)
  *(p_gpio_address + GPIO_CLEAR_OFFSET) = (1 << gpio_numer);
 else
  *(p_gpio_address + GPIO_SET_OFFSET) = (1 << gpio_numer);

#endif

}/*SetGPIOValue*/


void GetGPIOValue(unsigned long *p_gpio_address, int gpio_numer, int *p_value)
{
#define GPIO_INPUT_VALUE_OFFSET    (1)
 unsigned long full_register_value;

 full_register_value = *(p_gpio_address + GPIO_INPUT_VALUE_OFFSET);

 *p_value = (full_register_value >> gpio_numer) & 0x01;
}/*GetGPIOValue*/


And do not forget to call munmap while the mapping address would not be used.

If you do not use the same CPU as mine, you need to implement those functions by inquiring data sheet of the CPU you use.

二. Understand the DHT11 communication data pockets.

DHT11 data sheet : 漢文, English.

As the datasheet's instruction, the procedure of read data from DHT11 be :


---Send init command to DHT11---
PULL_DOWN 18 ms
PULL_UP  30 us

---DHT11 init responding--------

Receive low for 80 us
Receive high for 80 us

--Receiving Data---------------
--One bit datum--receiving-----

Receive low for 50 us

if the datum is 0
 receiving high for 28 us
or (the datum is 1)
 receiving high for 70 us

--To fetch next bit-------------

----Receiving total 5 byte------

--Check the sum of head 4 bytes equals with the last byte--


三. Implementation :

The full code be :

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include <signal.h>

#include <sys/time.h>
#include <sys/mman.h>

#include <fcntl.h>


#include <errno.h>



#define GPIO_ADDR               (0x18040000)
#define GPIO_MEM_BLOCK_SIZE     (48)


int MappingGPIOToMemory(unsigned long **pp_gpio_address)
{
 int mem_fd;

 if ((mem_fd = open("/dev/mem", O_RDWR)) < 0)
 {
  printf("Open /dev/mem fail\r\n");
  return -1;
 }/*if */

 *pp_gpio_address = (unsigned long*)mmap(NULL, GPIO_MEM_BLOCK_SIZE,
  PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, GPIO_ADDR);

 close(mem_fd);

 if (*pp_gpio_address == MAP_FAILED)
 {
  printf("mapping memory fail\r\n");
  printf("error message = %s\r\n", strerror(errno));
  return -2;
 }

}/*MappingGPIOToMemory*/


#define PIN_OUT        (1)
#define PIN_IN         (0)


void SetGPIODirection(unsigned long *p_gpio_address, int gpio_numer, int direction)
{
#define GPIO_OUTPUT_ENABLE_OFFSET   (0)

 unsigned long value;
 value = *(p_gpio_address + GPIO_OUTPUT_ENABLE_OFFSET);

 if (PIN_IN == direction)
  value &= ~(1 << gpio_numer);
 else
  value |= (1 << gpio_numer);

 *(p_gpio_address + GPIO_OUTPUT_ENABLE_OFFSET) = value;

}/*SetGPIODirection*/


void SetGPIOValue(unsigned long *p_gpio_address, int gpio_numer, int value)
{

#if(1)

#define GPIO_OUT_VALUE_OFFSET    (2)

 unsigned long full_register_value;

 full_register_value = *(p_gpio_address + GPIO_OUT_VALUE_OFFSET);

 if (0 == value)
  full_register_value &= ~(1 << gpio_numer);
 else
  full_register_value |= (1 << gpio_numer);

 *(p_gpio_address + GPIO_OUT_VALUE_OFFSET) = full_register_value;

#else

#define GPIO_SET_OFFSET      (3)
#define GPIO_CLEAR_OFFSET     (4)

 if(0 == value)
  *(p_gpio_address + GPIO_CLEAR_OFFSET) = (1 << gpio_numer);
 else
  *(p_gpio_address + GPIO_SET_OFFSET) = (1 << gpio_numer);

#endif

}/*SetGPIOValue*/


void GetGPIOValue(unsigned long *p_gpio_address, int gpio_numer, int *p_value)
{
#define GPIO_INPUT_VALUE_OFFSET    (1)
 unsigned long full_register_value;

 full_register_value = *(p_gpio_address + GPIO_INPUT_VALUE_OFFSET);

 *p_value = (full_register_value >> gpio_numer) & 0x01;
}/*GetGPIOValue*/




void delay_micro_sec(unsigned int delay_time_in_us)
{
 struct timeval now;
 struct timeval period;
 struct timeval end;

 gettimeofday(&now, NULL);

 period.tv_sec = delay_time_in_us / 1000000;
 period.tv_usec = delay_time_in_us % 1000000;

 timeradd(&now, &period, &end);

 while(timercmp(&now, &end, < ))
  gettimeofday(&now, NULL);

}/*delay_micro_sec*/



int ReadDHT11Byte(unsigned long *p_gpio_address, int gpio_numer,
 unsigned char *p_read_data)
{
 int i;
 int pin_value;
 unsigned char one_byte_value, bit_value;
 unsigned int over_time_cnt;
 int is_the_headest_bit;
 int endurable_time_in_usec;

 is_the_headest_bit = 1;
 one_byte_value = 0;

#define BUFFER_TIME_IN_USEC         (5)

 for( i=0; i<8; i++) {


#define DHT11_INIT_PULL_UP_DURATION_IN_USEC     (80)
#define PREVIOUS_ONE_IN_HIGH_EXTERNAL_DURATION_IN_USEC  (70 - 28)

  if(0 != is_the_headest_bit)
  {
   endurable_time_in_usec = DHT11_INIT_PULL_UP_DURATION_IN_USEC
   + BUFFER_TIME_IN_USEC;
   is_the_headest_bit = 0;
  }
  else
  {
   endurable_time_in_usec = PREVIOUS_ONE_IN_HIGH_EXTERNAL_DURATION_IN_USEC
    + BUFFER_TIME_IN_USEC;
  }/*is_the_headest_bit == true */


  over_time_cnt = 0;
  while(1)
  {
   GetGPIOValue(p_gpio_address, gpio_numer, &pin_value);
   delay_micro_sec(1);

   if(0 == pin_value)
    break;

   if( endurable_time_in_usec == over_time_cnt)
    break;

   over_time_cnt++;
  }/*wait 0 */


  if(endurable_time_in_usec == over_time_cnt)
  {
#if(0)
   printf("stage 1 fail\r\n");
#endif
   return -1;
  }


#define DATA_HINT_SIGNAL_DURATION_IN_USEC     (50)
  endurable_time_in_usec = DATA_HINT_SIGNAL_DURATION_IN_USEC + BUFFER_TIME_IN_USEC;
  over_time_cnt = 0;
  while(1)
  {
   GetGPIOValue(p_gpio_address, gpio_numer, &pin_value);
   delay_micro_sec(1);

   if(0 != pin_value)
    break;

   if(endurable_time_in_usec == over_time_cnt)
   {
    break;
   }

   over_time_cnt++;
  }/*wait 1*/

  if(endurable_time_in_usec == over_time_cnt)
  {
#if(0)
   printf("stage 2 fail\r\n");
#endif
   return -2;
  }


#define SIGNAL_ZERO_DURATION_IN_USEC      (28)

  delay_micro_sec(SIGNAL_ZERO_DURATION_IN_USEC);

  GetGPIOValue(p_gpio_address, gpio_numer, &pin_value);

  if(0 == pin_value)
   bit_value = 0;
  else
   bit_value = 1;


  one_byte_value <<= 1;
  one_byte_value |= bit_value;

 }/*for i */

 *p_read_data = one_byte_value;

 return 0;
}/*ReadDHT11OneByte*/


void InitDHT11(unsigned long *p_gpio_address, int gpio_numer)
{
 unsigned int over_time_cnt;


 SetGPIODirection(p_gpio_address, gpio_numer, PIN_OUT);


#define HOST_PULL_DOWN_DURATION_IN_USEC      (20*1000)
 SetGPIOValue(p_gpio_address, gpio_numer, 0);
 delay_micro_sec(HOST_PULL_DOWN_DURATION_IN_USEC);


#define HOST_PULL_UP_DURATION_IN_USEC      (20)
 SetGPIOValue(p_gpio_address, gpio_numer, 1);
 delay_micro_sec(HOST_PULL_UP_DURATION_IN_USEC);


 SetGPIODirection(p_gpio_address, gpio_numer, PIN_IN);

#define WAIT_DHT11_INIT_RESP_PULL_DOWN_IN_USEC    (40)

 over_time_cnt = 0;
 while(1)
 {
  int pin_value;

  GetGPIOValue(p_gpio_address, gpio_numer, &pin_value);
  delay_micro_sec(1);

  if(0 == pin_value)
   break;

  if(WAIT_DHT11_INIT_RESP_PULL_DOWN_IN_USEC == over_time_cnt)
   break;

  over_time_cnt++;
 }/*wait 0*/


#define DHT11_INIT_RESP_PULL_DOWN_DURATION_IN_USEC   (80)

 over_time_cnt = 0;
 while(1)
 {
  int pin_value;

  GetGPIOValue(p_gpio_address, gpio_numer, &pin_value);
  delay_micro_sec(1);

  if(0 != pin_value)
   break;

  if(DHT11_INIT_RESP_PULL_DOWN_DURATION_IN_USEC == over_time_cnt)
    break;

  over_time_cnt++;
 }/*wait 1*/

}/*InitDHT11*/


int ReadDHT11(unsigned long *p_gpio_address, int gpio_numer,
 int *p_humidity, int *p_temperature)
{
 int i;
 int status;
 unsigned char raw_DHT11_data[8];

 status = 0;
 memset(&raw_DHT11_data[0], 0, 5);

 InitDHT11(p_gpio_address, gpio_numer);

 for(i = 0; i < 5; i++)
 {
  if(0 != ReadDHT11Byte(p_gpio_address, gpio_numer, &raw_DHT11_data[i]))
  {
   status = -1;
   goto End_Of_Read_DHT11;
  }
 }/*for i*/


 if(0 == raw_DHT11_data[0] && 0 == raw_DHT11_data[1]
  && 0 == raw_DHT11_data[2] && 0 == raw_DHT11_data[3]
  && 0 == raw_DHT11_data[4])
 {
  status = -1;
  goto End_Of_Read_DHT11;
 }/*if all zero*/

 status = -2;

 if(raw_DHT11_data[4] == raw_DHT11_data[0] + raw_DHT11_data[1]
  + raw_DHT11_data[2] + raw_DHT11_data[3])
 {
  *p_humidity = (int)raw_DHT11_data[0];
  *p_temperature = (int)raw_DHT11_data[2];

  status = 0;
 }/*if match checking code*/

End_Of_Read_DHT11:
 return status;
}/*ReadDHT11*/


unsigned long *p_gpio_address = NULL;

void InterruptSignalHandlingRoutine(int sig)
{
 if(NULL != p_gpio_address)
  munmap(p_gpio_address, GPIO_MEM_BLOCK_SIZE);
 p_gpio_address = NULL;

 exit(0);
}/*InterruptSignalHandlingRoutine*/


void print_current_time(void)
{
 time_t t;
 struct tm calendar_time;
 t = time(NULL);
 localtime_r(&t, &calendar_time);

 printf("now time: %d-%d-%d %d:%d:%d, %s(+%d)\n",
  calendar_time.tm_year + 1900, calendar_time.tm_mon + 1,
  calendar_time.tm_mday,
  calendar_time.tm_hour, calendar_time.tm_min, calendar_time.tm_sec,
  calendar_time.tm_zone, (int)calendar_time.tm_gmtoff/3600);
}/*print_current_time*/


main(int argc, char *argv[])
{

 int humidity, temperature;
 int executed_count;
 int succeeded_count;
 int g_gpio_number;

 signal(SIGINT, InterruptSignalHandlingRoutine);

 if(2 > argc)
 {
  printf("%s should be followed by led_gpio_pin_number\r\n", argv[0]);
  return -1;
 }

 {
  char *p_temp;
  g_gpio_number = strtol(argv[1], &p_temp, 10);
  if(argv[1]== p_temp)
  {
   printf("%s is not a number for specifying gpio pin number\r\n", argv[1]);
   return -2;
  }/*not a number*/

 }/*local variable*/

 MappingGPIOToMemory(&p_gpio_address);


#if(1)

 while(1)
 {
  humidity = temperature = 0;

#if(1)
  do
  {
   if(0 == ReadDHT11(p_gpio_address, g_gpio_number,
    &humidity, &temperature))
   {
    break;
   }/*if*/
  }while(1);
#else
  /*I do not like this style for while loop content does not exist*/
  while(0 != ReadDHT11(p_gpio_address, g_gpio_number,
    &humidity, &temperature));
#endif
  printf("\r\n");
  printf("humidity = %d, temperature = %d\r\n",
    humidity, temperature);
  print_current_time();

  usleep(5*1000*1000);
 }/*while 1*/

#else

 succeeded_count = executed_count = 0;

 while(1)
 {
  humidity = temperature = 0;
  if(0 == ReadDHT11(p_gpio_address, g_gpio_number, &humidity, &temperature))
  {
#if(0)
   printf("humidity = %d, temperature = %d\r\n",
    humidity, temperature);
#endif
   succeeded_count++;
  }

  executed_count++;

  if(100 == executed_count)
  {
   printf("yield rate = %3.2f\r\n", succeeded_count/(float)executed_count);
   succeeded_count = executed_count = 0;
  }

 }/*while 1*/
#endif

 if(NULL != p_gpio_address)
  munmap(p_gpio_address, GPIO_MEM_BLOCK_SIZE);
 p_gpio_address = NULL;

 return 0;
}/*main*/

Note : The function delay_micro_sec could not been replaced as usleep : while calling usleep, the system would current task to the others, that would entail the delaying time is not accurate enough, especially the time interval (sleep time) is in the order of micro-second.

 The makefile is


OPENWRT_ROOT=/home/gaiger/openwrt-cc/staging_dir
OPENWRT_TOOLCHAIN_PATH = $(OPENWRT_ROOT)/toolchain-mips_34kc_gcc-4.8-linaro_uClibc-0.9.33.2

CC = $(OPENWRT_TOOLCHAIN_PATH)/bin/mips-openwrt-linux-gcc



CFLAGS := -O2
all:
        $(CC)  $(CFLAGS)   main.c  -o dht11_measure
clean:
        rm dht11_measure -f

The code do not rely on non-default library, the Makefile call the cross-compiler( it should be called as parasitic compiler) for compilation only.


Run the binary :


root@GL-AR150:/tmp# ./dht11_measure 18

humidity = 45, temperature = 27
now time: 2017-8-20 1:34:21, CST(+8)

humidity = 32, temperature = 27
now time: 2017-8-20 1:34:26, CST(+8)

humidity = 50, temperature = 27
now time: 2017-8-20 1:34:32, CST(+8)

humidity = 20, temperature = 27
now time: 2017-8-20 1:34:37, CST(+8)


  It is the beginning month of Autumn, it's still very hot in Taiwan's midnight.