首页 > 解决方案 > 在 Fortran 和 C 之间的实现中,打印和写入指令的分段失败

问题描述

我正在尝试使用 iso_c_binding 互操作性从 Fortran 调用 C 函数。但是,在尝试使用 print 和 write 语句时出现 SegFault 错误。如果没有 print 和 write 语句,代码可以正常工作,但我需要这些语句来创建包含模拟数据的输出文件。有谁知道如何解决这个问题?

注意:我使用 Ubuntu 20.04、GFortran 和 GCC 来编译各自的源代码。

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;
}

提前感谢所有提供帮助的人。

标签: cfortran

解决方案


这个问题可能值得回答,即使问题的原因是由 Craig Estay 确定的晦涩难懂的原因。

print使用andwrite语句时调用的 Gfortran 运行时库包含对另一个 C 函数的write()调用并调用另一个 C 函数write将导致 gfortran 运行时调用错误的函数。

它可以很容易地在一个简单的程序中进行测试,如下所示:

testwrite.c:

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

testwrite.f90:

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

使用gfortran testwrite.c testwrite.f90时,输出为:

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

icc testwrite.c -c -o c.o使用和时会出现相同的输出ifort c.o testwrite.f90


推荐阅读