Anderson Melo
Anderson Melo

Reputation: 21

Segmentation Failure with print and write instructions, in Implementation between Fortran and C

I'm trying to call a C function from Fortran, using the iso_c_binding interoperability. However, I am getting a SegFault error when trying to use print and write statements. Without the print and write statements the code works fine, but I need these statements to create an output file with the simulation data. Does anyone know how to solve this problem?

Note: I am using Ubuntu 20.04, GFortran, and GCC to compile the respective source codes.

gcc -c subroutine_in_c.c
gfortran -o exec main.f90 subroutine_in_c.o -lwiringPi

main.f90:

PROGRAM main
   USE, INTRINSIC:: iso_c_binding, ONLY: C_FLOAT

   IMPLICIT NONE

   REAL(KIND = 4) :: leitura_sensor = 0.0

   INTERFACE 
     SUBROUTINE ler_sensores(s1) BIND(C)
        USE, INTRINSIC :: iso_c_binding, ONLY: C_FLOAT
        IMPLICIT NONE
        REAL(KIND=C_FLOAT) :: s1
      END SUBROUTINE ler_sensores
   END INTERFACE

   !print*, 'Call subroutine in C language'

   call ler_sensores(leitura_sensor)

   !print*, 'Return to main.f90'

   OPEN(UNIT=1, FILE='output.txt', STATUS='unknown')

   WRITE(1,*) leitura_sensor

   CLOSE(UNIT=1)

END PROGRAM main

subroutine_in_c.c:

#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <wiringPiSPI.h>
#include <wiringPiI2C.h>

#define LCDADDR             0x27 //IIC LCD address
#define BLEN                1   //1--open backlight,0--close backlight

#define CHAN_CONFIG_SINGLE  8   //setup channel 0 as Single-ended input
#define SPICHANNEL          0   //MCP3008 connect to SPI0
#define ANALOGCHANNEL       0   //Potentiometer connect MCP3008 analog channel 0
#define ANALOGCHANNEL2      1

static int spifd;
static int i2cfd;

void
spiSetup (int spiChannel)
{
  if ((spifd = wiringPiSPISetup (spiChannel, 10000)) < 0)
    {
      fprintf (stderr, "Can't open the SPI bus: %s\n", strerror (errno)) ;
      exit (EXIT_FAILURE) ;
    }
}

int
myAnalogRead(int spiChannel,int channelConfig,int analogChannel)
{
   if (analogChannel<0 || analogChannel>7)
       return -1;
   unsigned char buffer[3] = {1}; // start bit
   buffer[1] = (channelConfig+analogChannel) << 4;
   wiringPiSPIDataRW(spiChannel, buffer, 3);
   return ( (buffer[1] & 3 ) << 8 ) + buffer[2]; // get last 10 bits
}

//write a word to lcd
void
write_word(int data)
{
  int temp = data;
  if ( BLEN == 1 )
    temp |= 0x08;
  else
    temp &= 0xF7;
  wiringPiI2CWrite(i2cfd, temp);
 }

//send command to lcd
void
send_command(int comm)
{
  int buf;
  // Send bit7-4 firstly
  buf = comm & 0xF0;
  buf |= 0x04;            // RS = 0, RW = 0, EN = 1
  write_word(buf);
  delay(2);
  buf &= 0xFB;            // Make EN = 0
  write_word(buf);

  // Send bit3-0 secondly
  buf = (comm & 0x0F) << 4;
  buf |= 0x04;            // RS = 0, RW = 0, EN = 1
  write_word(buf);
  delay(2);
  buf &= 0xFB;            // Make EN = 0
  write_word(buf);
}

//send data to lcd
void
send_data(int data)
{
  int buf;
  // Send bit7-4 firstly
  buf = data & 0xF0;
  buf |= 0x05;            // RS = 1, RW = 0, EN = 1
  write_word(buf);
  delay(2);
  buf &= 0xFB;            // Make EN = 0
  write_word(buf);

  // Send bit3-0 secondly
  buf = (data & 0x0F) << 4;
  buf |= 0x05;            // RS = 1, RW = 0, EN = 1
  write_word(buf);
  delay(2);
  buf &= 0xFB;            // Make EN = 0
  write_word(buf);
}

//initialize the lcd
void
init()
{
  send_command(0x33); // Must initialize to 8-line mode at first
  delay(5);
  send_command(0x32); // Then initialize to 4-line mode
  delay(5);
  send_command(0x28); // 2 Lines & 5*7 dots
  delay(5);
  send_command(0x0C); // Enable display without cursor
  delay(5);
  send_command(0x01); // Clear Screen
  wiringPiI2CWrite(i2cfd, 0x08);
}

//clear screen
void
clear()
{
  send_command(0x01); //clear Screen
}

//Print the message on the lcd
void
write(int x, int y, char data[])
{
  int addr, i;
  int tmp;
  if (x < 0)  x = 0;
  if (x > 15) x = 15;
  if (y < 0)  y = 0;
  if (y > 1)  y = 1;

  // Move cursor
  addr = 0x80 + 0x40 * y + x;
  send_command(addr);

  tmp = strlen(data);
  for (i = 0; i < tmp; i++) {
    send_data(data[i]);
  }
}

void
ler_sensores(float *s1)
{
  int adc;
  int adc2;
  int i;
  float voltage;
  float voltage2;
  char buf[5];
  if (wiringPiSetup() < 0)
    {
      fprintf(stderr,"Can't init wiringPi: %s\n",strerror(errno));
      exit(EXIT_FAILURE);
    }

  spiSetup(SPICHANNEL);//init spi

  i2cfd = wiringPiI2CSetup(LCDADDR); //init i2c
  init(); //init LCD
  clear(); //clear screen

  for (i = 0; i <25; i++) {
    adc = myAnalogRead(SPICHANNEL,CHAN_CONFIG_SINGLE,ANALOGCHANNEL);
    adc2 = myAnalogRead(SPICHANNEL, CHAN_CONFIG_SINGLE, ANALOGCHANNEL2);
    voltage = adc/1024.*20.0;
    write(0,0,"Ch Linear:");
    sprintf(buf,"%2.2f",voltage);//float change to string
    write(10,0,buf);//print voltage on lcd
    write(15,0,"V");//print unit

    write(0,1,"Ch Logarit:");
    voltage2 = adc2/1024.*20.0;
    sprintf(buf,"%2.2f",voltage2);
    write(11,1, buf);
    write(16,1,"V");
    delay(1000);
  }

  *s1 = voltage;
}

Thank you in advance to everyone who helps.

Upvotes: 2

Views: 93

Answers (1)

This question probably deserves an answer even if the reason for the problem is the obscure one, identified by Craig Estay.

Gfortran's runtime library, called when using the print and write statements, contains calls to write() and having another C function called write will cause the gfortran runtime to call a wrong function.

It can easily be tested in a simple program like this:

testwrite.c:

#include "stdio.h"
void write(){
  puts("my C write");
}

testwrite.f90:

print *,"test print"
write(*,*) "test write"
end

When using gfortran testwrite.c testwrite.f90, the output is:

my C write
my C write
my C write
my C write

The same output appears when using icc testwrite.c -c -o c.o and ifort c.o testwrite.f90.

Upvotes: 1

Related Questions