279 lines
9.2 KiB
C
279 lines
9.2 KiB
C
/*******************************************************************************
|
|
*
|
|
* i2c.c
|
|
*
|
|
* Copyright (c) 2013 Shahrooz Shahparnia (sshahrooz@gmail.com)
|
|
*
|
|
* Description:
|
|
* i2c is a command-line utility for executing i2c commands with the
|
|
* Broadcom bcm2835. It was developed and tested on a Raspberry Pi single-board
|
|
* computer model B. The utility is based on the bcm2835 C library developed
|
|
* by Mike McCauley of Open System Consultants, http://www.open.com.au/mikem/bcm2835/.
|
|
*
|
|
* Invoking spincl results in a read or write I2C transfer. Options include the
|
|
* the I2C clock frequency, read/write, address, and port initialization/closing
|
|
* procedures. The command usage and command-line parameters are described below
|
|
* in the showusage function, which prints the usage if no command-line parameters
|
|
* are included or if there are any command-line parameter errors. Invoking i2c
|
|
* requires root privilege.
|
|
*
|
|
* This file contains the main function as well as functions for displaying
|
|
* usage and for parsing the command line.
|
|
*
|
|
* Open Source Licensing GNU GPLv3
|
|
*
|
|
* Building:
|
|
* After installing bcm2835, you can build this
|
|
* with something like:
|
|
* gcc -o i2c i2c.c -l bcm2835
|
|
* sudo ./i2c
|
|
*
|
|
* Or you can test it before installing with:
|
|
* gcc -o i2c -I ../../src ../../src/bcm2835.c i2c.c
|
|
* sudo ./i2c
|
|
*
|
|
* History:
|
|
* 11/05 VERSION 1.0.0: Original
|
|
*
|
|
* User input parsing (comparse) and showusage\
|
|
* have been adapted from: http://ipsolutionscorp.com/raspberry-pi-spi-utility/
|
|
* mostly to keep consistence with the spincl tool usage.
|
|
*
|
|
* Compile with: gcc -o i2c i2c.c bcm2835.c
|
|
*
|
|
* Examples:
|
|
*
|
|
* Set up ADC (Arduino: ADC1015)
|
|
* sudo ./i2c -s72 -dw -ib 3 0x01 0x44 0x00 (select config register, setup mux, etc.)
|
|
* sudo ./i2c -s72 -dw -ib 1 0x00 (select ADC data register)
|
|
*
|
|
* Bias DAC (Arduino: MCP4725) at some voltage
|
|
* sudo ./i2c -s99 -dw -ib 3 0x60 0x7F 0xF0 (FS output is with 0xFF 0xF0)
|
|
* Read ADC convergence result
|
|
* sudo ./i2c -s72 -dr -ib 2 (FS output is 0x7FF0 with PGA1 = 1)
|
|
*
|
|
* In a DAC to ADC loop back typical results are:
|
|
*
|
|
* DAC VOUT ADC
|
|
* 7FFh 1.6V 677h Note ratio is FS_ADC*PGA_GAIN/FS_DAC = 4.096/3.3 = 1.23
|
|
* 5FFh 1.2V 4DCh
|
|
* 8F0h 1.8V 745h
|
|
* 9D0h 2V 7EAh
|
|
* 000h 10mV 004h
|
|
*
|
|
********************************************************************************/
|
|
|
|
#include <bcm2835.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
|
|
#define MODE_READ 0
|
|
#define MODE_WRITE 1
|
|
|
|
#define MAX_LEN 32
|
|
|
|
char wbuf[MAX_LEN];
|
|
|
|
typedef enum {
|
|
NO_ACTION,
|
|
I2C_BEGIN,
|
|
I2C_END
|
|
} i2c_init;
|
|
|
|
uint8_t init = NO_ACTION;
|
|
uint16_t clk_div = BCM2835_I2C_CLOCK_DIVIDER_148;
|
|
uint8_t slave_address = 0x00;
|
|
uint32_t len = 0;
|
|
uint8_t mode = MODE_READ;
|
|
|
|
//*******************************************************************************
|
|
// comparse: Parse the command line and return EXIT_SUCCESS or EXIT_FAILURE
|
|
// argc: number of command-line arguments
|
|
// argv: array of command-line argument strings
|
|
//*******************************************************************************
|
|
|
|
int comparse(int argc, char **argv) {
|
|
int argnum, i, xmitnum;
|
|
|
|
if (argc < 2) { // must have at least program name and len arguments
|
|
// or -ie (I2C_END) or -ib (I2C_BEGIN)
|
|
fprintf(stderr, "Insufficient command line arguments\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
argnum = 1;
|
|
while (argnum < argc && argv[argnum][0] == '-') {
|
|
|
|
switch (argv[argnum][1]) {
|
|
|
|
case 'i': // I2C init
|
|
switch (argv[argnum][2]) {
|
|
case 'b': init = I2C_BEGIN; break;
|
|
case 'e': init = I2C_END; break;
|
|
default:
|
|
fprintf(stderr, "%c is not a valid init option\n", argv[argnum][2]);
|
|
return EXIT_FAILURE;
|
|
}
|
|
break;
|
|
|
|
case 'd': // Read/Write Mode
|
|
switch (argv[argnum][2]) {
|
|
case 'r': mode = MODE_READ; break;
|
|
case 'w': mode = MODE_WRITE; break;
|
|
default:
|
|
fprintf(stderr, "%c is not a valid init option\n", argv[argnum][2]);
|
|
return EXIT_FAILURE;
|
|
}
|
|
break;
|
|
|
|
case 'c': // Clock divider
|
|
clk_div = atoi(argv[argnum]+2);
|
|
break;
|
|
|
|
case 's': // Slave address
|
|
slave_address = atoi(argv[argnum]+2);
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr, "%c is not a valid option\n", argv[argnum][1]);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
argnum++; // advance the argument number
|
|
|
|
}
|
|
|
|
// If command is used for I2C_END or I2C_BEGIN only
|
|
if (argnum == argc && init != NO_ACTION) // no further arguments are needed
|
|
return EXIT_SUCCESS;
|
|
|
|
// Get len
|
|
if (strspn(argv[argnum], "0123456789") != strlen(argv[argnum])) {
|
|
fprintf(stderr, "Invalid number of bytes specified\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
len = atoi(argv[argnum]);
|
|
|
|
if (len > MAX_LEN) {
|
|
fprintf(stderr, "Invalid number of bytes specified\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
argnum++; // advance the argument number
|
|
|
|
xmitnum = argc - argnum; // number of xmit bytes
|
|
|
|
memset(wbuf, 0, sizeof(wbuf));
|
|
|
|
for (i = 0; i < xmitnum; i++) {
|
|
if (strspn(argv[argnum + i], "0123456789abcdefABCDEFxX") != strlen(argv[argnum + i])) {
|
|
fprintf(stderr, "Invalid data: ");
|
|
fprintf(stderr, "%d \n", xmitnum);
|
|
return EXIT_FAILURE;
|
|
}
|
|
wbuf[i] = (char)strtoul(argv[argnum + i], NULL, 0);
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
//*******************************************************************************
|
|
// showusage: Print the usage statement and return errcode.
|
|
//*******************************************************************************
|
|
int showusage(int errcode) {
|
|
printf("i2c \n");
|
|
printf("Usage: \n");
|
|
printf(" i2c [options] len [rcv/xmit bytes]\n");
|
|
printf("\n");
|
|
printf(" Invoking i2c results in an I2C transfer of a specified\n");
|
|
printf(" number of bytes. Additionally, it can be used to set the appropriate\n");
|
|
printf(" GPIO pins to their respective I2C configurations or return them\n");
|
|
printf(" to GPIO input configuration. Options include the I2C clock frequency,\n");
|
|
printf(" initialization option (i2c_begin and i2c_end). i2c must be invoked\n");
|
|
printf(" with root privileges.\n");
|
|
printf("\n");
|
|
printf(" The following are the options, which must be a single letter\n");
|
|
printf(" preceded by a '-' and followed by another character.\n");
|
|
printf(" -dx where x is 'w' for write and 'r' is for read.\n");
|
|
printf(" -ix where x is the I2C init option, b[egin] or e[nd]\n");
|
|
printf(" The begin option must be executed before any transfer can happen.\n");
|
|
printf(" It may be included with a transfer.\n");
|
|
printf(" The end option will return the I2C pins to GPIO inputs.\n");
|
|
printf(" It may be included with a transfer.\n");
|
|
printf(" -cx where x is the clock divider from 250MHz. Allowed values\n");
|
|
printf(" are 150 through 2500.\n");
|
|
printf(" Corresponding frequencies are specified in bcm2835.h.\n");
|
|
printf("\n");
|
|
printf(" len: The number of bytes to be transmitted or received.\n");
|
|
printf(" The maximum number of bytes allowed is %d\n", MAX_LEN);
|
|
printf("\n");
|
|
printf("\n");
|
|
printf("\n");
|
|
return errcode;
|
|
}
|
|
|
|
char buf[MAX_LEN];
|
|
int i;
|
|
uint8_t data;
|
|
|
|
int main(int argc, char **argv) {
|
|
|
|
printf("Running ... \n");
|
|
|
|
// parse the command line
|
|
if (comparse(argc, argv) == EXIT_FAILURE) return showusage (EXIT_FAILURE);
|
|
|
|
if (!bcm2835_init())
|
|
{
|
|
printf("bcm2835_init failed. Are you running as root??\n");
|
|
return 1;
|
|
}
|
|
|
|
// I2C begin if specified
|
|
if (init == I2C_BEGIN)
|
|
{
|
|
if (!bcm2835_i2c_begin())
|
|
{
|
|
printf("bcm2835_i2c_begin failed. Are you running as root??\n");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
// If len is 0, no need to continue, but do I2C end if specified
|
|
if (len == 0) {
|
|
if (init == I2C_END) bcm2835_i2c_end();
|
|
printf("... done!\n");
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
bcm2835_i2c_setSlaveAddress(slave_address);
|
|
bcm2835_i2c_setClockDivider(clk_div);
|
|
fprintf(stderr, "Clock divider set to: %d\n", clk_div);
|
|
fprintf(stderr, "len set to: %d\n", len);
|
|
fprintf(stderr, "Slave address set to: %d\n", slave_address);
|
|
|
|
if (mode == MODE_READ) {
|
|
for (i=0; i<MAX_LEN; i++) buf[i] = 'n';
|
|
data = bcm2835_i2c_read(buf, len);
|
|
printf("Read Result = %d\n", data);
|
|
for (i=0; i<MAX_LEN; i++) {
|
|
if(buf[i] != 'n') printf("Read Buf[%d] = %x\n", i, buf[i]);
|
|
}
|
|
}
|
|
if (mode == MODE_WRITE) {
|
|
data = bcm2835_i2c_write(wbuf, len);
|
|
printf("Write Result = %d\n", data);
|
|
}
|
|
|
|
// This I2C end is done after a transfer if specified
|
|
if (init == I2C_END) bcm2835_i2c_end();
|
|
bcm2835_close();
|
|
printf("... done!\n");
|
|
return 0;
|
|
}
|
|
|