401 lines
7.1 KiB
C
401 lines
7.1 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <fcntl.h>
|
|
#include <sys/ioctl.h>
|
|
#include <linux/i2c-dev.h>
|
|
#include <unistd.h>
|
|
#include <linux/spi/spidev.h>
|
|
#include <time.h>
|
|
#include <getopt.h>
|
|
#include <string.h>
|
|
|
|
#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;
|
|
}
|
|
|