#include #include #include #include #include #include #include #include #include #include #define CHANNEL_A 0x00 #define CHANNEL_B 0x01 #define RHR 0x00 // Recv Holding Register #define THR 0x00 // Xmit Holding Register #define IODir 0x0A // I/O P:ins Direction Register #define IOState 0x0B // I/O Pins State Register #define IOIntEna 0x0C // I/O Interrupt Enable Register #define IOCONTROL 0x0E // I/O Pins Control Register #define IER 0x01 // Interrupt Enable Register #define FCR 0x02 // FIFO Control Register in WRITE Mode #define IIR 0x02 // Interrupt Identification Register in READ Mode #define LCR 0x03 // Line Control Register #define MCR 0x04 // Modem Control Register #define LSR 0x05 // Line status Register #define MSR 0x06 // Modem Status Register #define SPR 0x07 // ScratchPad Register #define TCR 0x06 // Transmission Control Register #define TLR 0x07 // Trigger Level Register #define TXLVL 0x08 // Xmit FIFO Level Register #define RXLVL 0x09 // Recv FIFO Level Register #define EFCR 0x0F // Extra Features Control Register #define DLL 0x00 // Divisor Latch LSB 0x00 #define DLH 0x01 // Divisor Latch MSB 0x01 #define EFR 0x02 #define FREQ (1843200UL) #define BAUDRATE 9600 #define CHANNEL CHANNEL_A #define chreg(reg) (reg << 3 | CHANNEL) typedef unsigned char u8; #define I2C 0x11 #define SPI 0x22 // 设备路径 char *device; // 片选脚 char *cs = "/proc/rp_gpio/gpio4c5"; // 测试字符串 char *tx = "spi or i2c test string"; // 设备类型 u8 type; // i2c 设备地址 u8 addr = 0x4d; u8 read_reg(u8 reg); void open_dev(); void write_reg(u8 reg, u8 val); void gpio_set(int val); int fd = -1; int fd_gpio = -1; struct timespec delay = { .tv_sec = 0, .tv_nsec = 100000 }; static inline u8 available_data() { return read_reg(RXLVL); } void write_byte(u8 val) { u8 lsr; do{ lsr = read_reg(LSR); }while((lsr & 0x20) == 0); write_reg(THR, val); } u8 read_byte() { return read_reg(RHR);; } void test_connect() { u8 val; write_reg(SPR, 0x77); if((val = read_reg(SPR)) != 0x77){ printf("0x%02x != 0x77\n", val); printf("connect failed\n"); exit(-1); } write_reg(SPR, 0x99); if((val = read_reg(SPR)) != 0x99){ printf("0x%02x != 0x99\n", val); printf("connect failed\n"); exit(-2); } printf("connect okay\n"); } u8 read_reg(u8 reg) { reg = chreg(reg); unsigned char val[2]; if(type == I2C){ if (write(fd, ®, 1) != 1) { perror("write failed"); exit(-3); } if (read(fd, &val[1], 1) != 1) { perror("read failed"); exit(-4); } } else { u8 buf[] = {0x80 | reg, 0xff}; struct spi_ioc_transfer tr = { .tx_buf = (unsigned long)buf, .rx_buf = (unsigned long)val, .len = 2, .delay_usecs = 10, .bits_per_word = 8, }; if(ioctl(fd, SPI_IOC_MESSAGE(1), &tr) < 0){ perror("ioctl failed"); exit(-5); } } return val[1]; } void write_reg(u8 reg, u8 val) { reg = chreg(reg); u8 buf[] = {reg, val}; if(type == I2C){ if (write(fd, buf, 2) != 2) { perror("Failed to write to the i2c bus."); exit(-6); } } else { struct spi_ioc_transfer tr = { .tx_buf = (unsigned long)buf, // .rx_buf = (unsigned long)rxval, .len = 2, }; if(ioctl(fd, SPI_IOC_MESSAGE(1), &tr) < 0){ perror("ioctl failed"); exit(-7); } } } void set_baudrate() { int prescale = 4; u8 lcr; unsigned int div; if((read_reg(MCR) & 0x80) == 0) prescale = 1; div = (FREQ / prescale) / (BAUDRATE * 16); lcr = read_reg(LCR); lcr |= 0x80; write_reg(LCR, lcr); write_reg(DLL, (u8)div); write_reg(DLH, (u8)(div >> 8)); lcr &= 0x7f; write_reg(LCR, lcr); } void set_line() { u8 lcr; lcr = read_reg(LCR); lcr &= 0xC0; lcr |= 0x03; write_reg(LCR, lcr); } void reset() { write_reg(IER, 0x0); write_reg(IIR, 0x1); write_reg(FCR, 0x0); write_reg(LCR, 0x1d); write_reg(MCR, 0x0); write_reg(TCR, 0x0); write_reg(TLR, 0x0); write_reg(EFCR, 0x0); read_reg(RHR); } void fifo_en() { u8 fcr; fcr = read_reg(FCR); fcr |= 0x01; write_reg(FCR, fcr); } void fifo_reset() { u8 fcr; fcr = read_reg(FCR); fcr |= 0x06; write_reg(FCR, fcr); } void init() { fifo_en(); fifo_reset(); set_baudrate(); set_line(); } void write_bytes(char *buf, size_t len) { for(int i = 0; i < len; i ++){ write_byte(buf[i]); } } void loop() { size_t len = strlen(tx); write_bytes(tx, len); while(available_data() > 0){ printf("%c", read_byte()); } printf("\n"); } void open_dev() { if(type == I2C){ if ((fd = open(device, O_RDWR)) < 0) { perror("open bus failed\n"); exit(-8); } if (ioctl(fd, I2C_SLAVE, addr) < 0) { close(fd); perror("failed access i2c dev\n"); exit(-9); } }else { if ((fd = open(device, O_RDWR)) < 0) { perror("open bus failed"); close(fd_gpio); exit(-10); } u8 mode = SPI_MODE_0; if (ioctl(fd, SPI_IOC_WR_MODE, &mode) < 0) { perror("Error setting SPI mode"); close(fd); close(fd_gpio); exit(-11); } } } void usage() { puts("usage:"); puts("-d device, such as /dev/spidev0.0 or /dev/i2c-4\n" "-s string, default is \"spi or i2c test string\"\n" "\nexample:\n" "./sc16is752 -d /dev/spidev0.0 -c /proc/rp_gpio/gpio4c5 -s \"TestString\"\n" "./sc16is752 -d /dev/i2c-4 -s \"TestString\"\n"); exit(-12); } void parse_cmd(int argc, char *argv[]) { static const struct option lopts[] = { {"device", 1, 0, 'd'}, // 设备节点 {"tx", 1, 0, 's'}, // 传输的字符串 {"cs", 1, 0, 'c'}, // SPI 设备所使用的片选脚 {NULL, 0, 0, 0} }; while(1){ int c = getopt_long(argc, argv, "D:c:s:d:b:i:o:vS:I:", lopts, NULL); if(c == -1) break; switch(c){ case 'd': device = optarg; break; case 'c': cs = optarg; break; case 's': tx = optarg; break; default: usage(); } } if(device == NULL){ printf("device is null\n"); exit(-13); } if(strstr(device, "i2c") != NULL) type = I2C; else if(strstr(device, "spi") != NULL) type = SPI; else { printf("no i2c or spi device\n"); exit(-14); } /* if(type == SPI && cs == NULL){ printf("spi must set gpio pin\n"); exit(-1); } */ } int main(int argc, char *argv[]) { parse_cmd(argc, argv); open_dev(); reset(); test_connect(); init(); loop(); close(fd); return 0; }