2018年11月24日 星期六

[OpenWRT] 用C語言寫UART轉RS485

RS-485 (EIA-485 標準) 為 RS-422 的改良版本,從原本的 10 組裝置
提高至 32 組裝置,僅並定義必要的電子特性,並沒有定義傳輸介面,
所以你會看到有人用普通的單芯線直接連接,或是RJ45/DMX之類的接頭
來做485的傳輸,亦可以混用不同介面在同一個RS-485網路中。

RS485與RS232最大的不同在於它是半雙工,也就是同一時間只能傳送或是接收,RS-485網路中只有同時間有1個以上裝置在發送資料就會出現干擾,所以還會透過Modbus之類的協定來搭配實際的應用。

RS-485是依靠A/B二端的電壓差做為判定1或是0 ,只要A/B之間壓差大於0.2V特,
即判定訊號為1,否則為0 ,RS-485可以接受的電壓範圍比較大,在-7~+12V。

一般的MCU若需要加入RS-485傳輸,則可以透過UART轉RS485 Chip,例如MX232/SP3485等等,透過控制DE/RE腳來控制目前為發送或接收狀態。

程式碼,在AR9331中測試過,DE為PIN 7 ,RE為PIN 13


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <termios.h>

#define GPIO_ADDR 0x18040000 // base address
#define GPIO_BLOCK 48     // memory block size
char *portname = "/dev/ttyATH0";
volatile unsigned long *gpioAddress;



int set_interface_attribs (int fd, int speed, int parity)
{
        struct termios tty;
        memset (&tty, 0, sizeof tty);
        if (tcgetattr (fd, &tty) != 0)
        {
                printf("error %d from tcgetattr", errno);
                return -1;
        }

        cfsetospeed (&tty, speed);
        cfsetispeed (&tty, speed);

        tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;     // 8-bit chars
        // disable IGNBRK for mismatched speed tests; otherwise receive break
        // as \000 chars
        tty.c_iflag &= ~IGNBRK;         // disable break processing
        tty.c_lflag = 0;                // no signaling chars, no echo,
                                        // no canonical processing
        tty.c_oflag = 0;                // no remapping, no delays
        tty.c_cc[VMIN]  = 0;            // read doesn't block
        tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

        tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl

        tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
                                        // enable reading
        tty.c_cflag &= ~(PARENB | PARODD);      // shut off parity
        tty.c_cflag |= parity;
        tty.c_cflag &= ~CSTOPB;
        tty.c_cflag &= ~CRTSCTS;

        if (tcsetattr (fd, TCSANOW, &tty) != 0)
        {
                printf ("error %d from tcsetattr", errno);
                return -1;
        }
        return 0;
}

void set_blocking (int fd, int should_block)
{
        struct termios tty;
        memset (&tty, 0, sizeof tty);
        if (tcgetattr (fd, &tty) != 0)
        {
                printf ("error %d from tggetattr", errno);
                return;
        }

        tty.c_cc[VMIN]  = should_block ? 1 : 0;
        tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

        if (tcsetattr (fd, TCSANOW, &tty) != 0)
                printf ("error %d setting term attributes", errno);
}


int gpioRead(int gpio)
{
    unsigned long value = *(gpioAddress + 1);
    //printf("value =%d\n",value);    
    //printf("(1<<gpio) =%d",(1 << gpio));
    return (value & (1 << gpio));
    
}

// "1" (output) or "0" (input) 
void gpioDirection(int gpio, int direction)
{
    unsigned long value = *(gpioAddress + 0); // obtain current settings
    if (direction == 1)
    {
        value |= (1 << gpio); // set bit to 1
    }
    else
    {
        value &= ~(1 << gpio); // clear bit
    }
    *(gpioAddress + 0) = value;
}

void gpioSet(int gpio, int data)
{
    unsigned long value = *(gpioAddress + 2); // obtain current settings
    if (data == 1)
    {
        value |= (1 << gpio); // set bit to 1
    }
    else
    {
        value &= ~(1 << gpio); // clear bit
    }
    *(gpioAddress + 2) = value;
}

/*void gpioSet(int gpio, int value)
{
    if (value == 0)
    {
        *(gpioAddress + 4) = (1 << gpio);
    }
    else
    {
        *(gpioAddress + 3) = (1 << gpio);
    }
}*/

int gpioSetup()
{
    int  m_mfd;
    if ((m_mfd = open("/dev/mem", O_RDWR)) < 0)
    {
        return -1;
    }    
    gpioAddress = (unsigned long*)mmap(NULL, GPIO_BLOCK, PROT_READ|PROT_WRITE, MAP_SHARED, m_mfd, GPIO_ADDR);
    close(m_mfd);

    if (gpioAddress == MAP_FAILED)
    {
        return -2;
    }
    return 0;
}

int main(int argc, char **argv)
{
   int rs485_de=7;
   int rs485_re=13;

   gpioSetup();
   gpioDirection(rs485_de,1);//set pin output
   gpioDirection(rs485_re,1);//set pin output
  
    int fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0)
    {
            printf ("error %d opening %s: %s", errno, portname, strerror (errno));
            return -1;
    }

    set_interface_attribs (fd, B115200, 0);  // set speed to 115,200 bps, 8n1 (no parity)
    set_blocking (fd, 0);                // set no blocking
    
    //get Model Name
    int cmd_len=7;    
    char w_buff[8];
    w_buff[0]=0x01;
    w_buff[1]=0x02;
    w_buff[2]=0x03;
    w_buff[3]=0x04;
    w_buff[4]=0x05;
    w_buff[5]=0x06;
    w_buff[6]=0x07;
    w_buff[7]='\0';


    for (int t=0;t<20;t++)
    {
        gpioSet(rs485_de,1);              //Set RS485 to Send Mode
        gpioSet(rs485_re,1);
        usleep (100);
        write (fd, w_buff, cmd_len);
        usleep (cmd_len*100);             // sleep enough to transmit the command date
        gpioSet(rs485_de,0);              // Set RS485 to Receive Mode
        gpioSet(rs485_re,0);
        usleep (10 * 1000);               //wait a little time before read data
        char buf [100];
        int n = read (fd, buf, sizeof buf);  // read up to 100 characters if ready to read
        printf("rec:%d bytes\n",n);    
        for (int i=0;i<n;i++)
        {
            printf("%d\t",buf[i]);
        }
        printf("\n");

    }
}