Kernel driver i2c Develop - linux-device-driver

On my board I have an I2C device that sets some register.
g_I2cDevFd = open("/dev/" UMAP_DEVNAME_I2C, O_RDWR, 0);
if (g_I2cDevFd < 0)
{
HI_FATAL_I2C("open I2C err.\n");
HI_I2C_UNLOCK();
return HI_ERR_I2C_OPEN_ERR;
}
How can I do that?
Best Regards

Your question is not clear much. But for I2C communication in Linux Os, Please refer this link Interfacing_with_I2C_Devices
Please use your device path in define UMAP_DEVNAME_I2C itself. ie, #define UMAP_DEVNAME_I2C "/dev/your_i2c_device"
Or use sprintf if you cant edit UMAP_DEVNAME_I2C ie,
char buff[100] = {0}; // size you can change according to your requirement
sprintf(buff,"/dev/%s",UMAP_DEVNAME_I2C);
g_I2cDevFd = open(buff, O_RDWR, 0);
/* Error check for open here*/
int addr = 0xFF; // 0xFF is Invalid, Give I2C address of your device
if (ioctl(g_I2cDevFd, I2C_SLAVE, addr) < 0) {
printf("Failed to acquire bus access and/or talk to slave.\n");
/* ERROR HANDLING; you can check errno to see what went wrong */
exit(1);
}
/* Write or Read*/

Related

Port Expander Read via i2c in uboot

I have a customized hardware with a port expander (PCA9555) that is connected via i2c to my SOM. The port expander is on bus 2 and is device 23. Now I need to read the value of Port P0 in a part of the u-boot environment. So i need to use the i2c commands. I have the following code but i can first test it on the hardware on Monday so my question is if somebody uses the same port expander and can tell me if my code is correct?! Thanks
const int bus = 2;
const int device = 0x23;
int ret = i2c_set_bus_num(bus);
if (ret) {
return 0;
}
ret = i2c_probe(device);
if (!ret) {
uint8_t value;
i2c_read(0x23, 0x00, 1, &value, 1);
}

Linux socket hardware timestamping

I'm working on a project researching about network synchronisation. Since I want to achieve the best performance I'm trying to compare software timestamping results with hardware timestamping ones.
I have followed this previously commented issue: Linux kernel UDP reception timestamp but after several tests I got some problems when trying to get hardware reception timestamps.
My scenario is composed of 2 devices, a PC and a Gateworks Ventana board, both devices are supposed to be waiting for packets to be broadcasted in their network and timestamping their reception times, I have tried this code (some parts have been omitted):
int rc=1;
int flags;
flags = SOF_TIMESTAMPING_RX_HARDWARE
| SOF_TIMESTAMPING_RAW_HARDWARE;
rc = setsockopt(sock, SOL_SOCKET,SO_TIMESTAMPING, &flags, sizeof(flags));
rc = bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
struct msghdr msg;
struct iovec iov;
char pktbuf[2048];
char ctrl[CMSG_SPACE(sizeof(struct timespec))];
struct cmsghdr *cmsg = (struct cmsghdr *) &ctrl;
msg.msg_control = (char *) ctrl;
msg.msg_controllen = sizeof(ctrl);
msg.msg_name = &serv_addr;
msg.msg_namelen = sizeof(serv_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
iov.iov_base = pktbuf;
iov.iov_len = sizeof(pktbuf);
//struct timeval time_kernel, time_user;
//int timediff = 0;
FILE *f = fopen("server.csv", "w");
if (f == NULL) {
error("Error opening file!\n");
exit(1);
}
fprintf(f, "Time\n");
struct timespec ts;
int level, type;
int i;
for (i = 0; i < 10; i++) {
rc = recvmsg(sock, &msg, 0);
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg))
{
level = cmsg->cmsg_level;
type = cmsg->cmsg_type;
if (SOL_SOCKET == level && SO_TIMESTAMPING == type) {
//ts = (struct timespec *) CMSG_DATA(cmsg);
memcpy(&ts, CMSG_DATA(cmsg), sizeof(ts));
printf("HW TIMESTAMP %ld.%09ld\n", (long)ts.tv_sec, (long)ts.tv_nsec);
}
}
}
printf("COMPLETED\n");
fclose(f);
close(sock);
return 0;
}
In both devices the output I get after receiving a packet:
HW TIMESTAMP 0.000000000
On the other hand if with the same code my flags are:
flags = SOF_TIMESTAMPING_RX_HARDWARE
| SOF_TIMESTAMPING_RX_SOFTWARE
| SOF_TIMESTAMPING_SOFTWARE;
I get proper timestamps:
HW TIMESTAMP 1551721801.970270543
However, they seem to be software-timestamping ones. What would be the correct solution / method to handle hardware timestamping for packets received?
First of all, use ethtool -T "your NIC" to make sure your hardware supports the hardware timestamping feature.
You need to explicitly tell the Linux to enable the hardware timestamping feature of your NIC. In order to to that, you need to have a ioctl() call.
What you have to do is to call it with SIOCSHWTSTAMP, which is a device request code to indicate which device you want to handle as well as what you want to do. For example, there is a code called CDROMSTOP to stop the cdrom drive.
You also need to use a ifreq struct to configure your NIC.
You need something like this:
struct ifreq ifconfig;
strncpy(config.ifr_name, "your NIC name", sizeof(ifconfig.ifr_name));
ioctl("your file descriptor" , SIOCSHWTSTAMP, &ifconfig);
Here are some pages that you can look up to:
ioctl manual page,
ifreq manual page,
Read part 3.

Not able to read from an external EEPROM using the STM32F103C8

I'm trying to write and read from an external EEPROM. There is a start bit (SB) followed by an opcode, then a 6-bit address and then the actual data. I've combined the SB and opcode into one byte that I can send as a start condition. I'm able to enable, erase and then write to the EEPROM. I'm assuming this is working since the HAL functions return HAL_OK and I can see the valid waveforms on the scope.
What I can't seem to do is read the data back. For the READ operation I don't see any waveforms on the scope. The number of clock cycles required is odd-numbered and not in multiples of 8. I don't know how I can send odd number of clock cycles since all the data is either 8, 16 or 32-bit. Wherever there are 25 or 29 clock cycles need, I seem to be sending 32 and where the required cycles are 9, I seem to be sending 16. I'm really hoping to avoid bit-banging as suggested in this thread.
Here is the main code:
int main(void)
{
HAL_Init();
MX_GPIO_Init();
MX_SPI1_Init();
__HAL_SPI_ENABLE(&hspi1);
// pull the CS pin high to select the EEPROM (active HIGH)
HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET);
HAL_Delay(10);
// Enable the EEPROM
enable_status = Enable_EEPROM(&EEPROM_SPI_PORT);
HAL_Delay(10);
// Erase the value at address 0x00
erase_status = Erase_EEPROM(&EEPROM_SPI_PORT, addr);
HAL_Delay(10);
// Write data 0xABCD at addr 0x00
write_status = Write_EEPROM(&EEPROM_SPI_PORT, addr, tx_data);
HAL_Delay(10);
// Disabling the EEPROM (with an EWDS) after a WRITE as described in the datasheet
disable_status = Disable_EEPROM(&EEPROM_SPI_PORT);
HAL_Delay(10);
// Re-enabling it
enable_status = Enable_EEPROM(&EEPROM_SPI_PORT);
HAL_Delay(10);
// Read from the EEPROM. This part isn't working.
read_status = Read_EEPROM(&EEPROM_SPI_PORT, addr, rx_data);
HAL_Delay(10);
// Pull the CS pin low to deselect the chip again.
HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET);
while (1)
{
}
}
The SPI is initialized to handle 16-bit data values
SPI_HandleTypeDef hspi1;
/* SPI1 init function */
void MX_SPI1_Init(void)
{
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_16BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
These are the EEPROM functions
#define ERASE 0x07 // erase specific memory location. This is followed by the 8-bit address and then by the 16-bit data.
#define READ 0x06 // read the memory location.
#define WRITE 0x05 // write to the memory location
#define EEPROM_SPI_PORT hspi1
extern SPI_HandleTypeDef EEPROM_SPI_PORT;
//Enable the EEPROM
//Accepts: SPI handle
//Returns: Success or failure of the enable operation
uint8_t Enable_EEPROM (SPI_TypeDef *spi_handle) {
uint16_t ewen = (0x04 << 8) | 0b00110000;
if (HAL_SPI_Transmit(spi_handle, &ewen, 1, HAL_MAX_DELAY) == HAL_OK) return TRUE;
else return FALSE;
}
//Disable the EEPROM
//Accepts: SPI handle
//Returns: Success or failure of the disable operation
uint8_t Disable_EEPROM (SPI_TypeDef *spi_handle) {
uint16_t ewds = (0x04 << 8) | 0b00000000;
if (HAL_SPI_Transmit(spi_handle, &ewds, 1, HAL_MAX_DELAY) == HAL_OK) return TRUE;
else return FALSE;
}
//Read from the EEPROM
//Accepts: SPI handle, memory address and data buffer where the read value will be stored
//Returns: Success or failure of read operation
uint8_t Read_EEPROM (SPI_TypeDef *spi_handle, uint8_t addr, uint16_t data) {
uint16_t write_package;
write_package = (READ << 8 | addr);
// if (HAL_SPI_Transmit(spi_handle, &write_package, 1, HAL_MAX_DELAY) == HAL_OK) {
// HAL_Delay(10);
// if (HAL_SPI_Receive(spi_handle, &data, 1, HAL_MAX_DELAY) == HAL_OK) return TRUE;
// else return FALSE;
// }
if (HAL_SPI_TransmitReceive(spi_handle, &write_package, &data, 1, HAL_MAX_DELAY) == HAL_OK) return TRUE;
else return FALSE;
}
//Write to the EEPROM
//Accepts: SPI handle, memory address and data to be written
//Returns: Success or failure of write operation
uint8_t Write_EEPROM (SPI_TypeDef *spi_handle, uint8_t addr, uint16_t data) {
uint16_t write_package[2];
write_package[0] = (WRITE << 8 | addr);
write_package[1] = data;
if (HAL_SPI_Transmit(spi_handle, write_package, 2, HAL_MAX_DELAY) == HAL_OK) return TRUE;
else return FALSE;
}
//Erase a specific memory address from the EEPROM
//Accepts: SPI handle and the memory address to be erased
//Returns: Success or failure of erase operation
uint8_t Erase_EEPROM (SPI_TypeDef *spi_handle, uint8_t addr) {
uint16_t write_package;
write_package = (ERASE << 8 | addr);
if (HAL_SPI_Transmit(spi_handle, &write_package, 1, HAL_MAX_DELAY) == HAL_OK) return TRUE;
else return FALSE;
}
EDIT: I’ve attached waveforms here as well.
Enable
Erase
Write
Without looking through your code in detail, I've spotted a possible problem: In order to complete an SPI operation, the chip select (CS) line usually needs to be pulled low before and set high again after every operation.
So, the EEPROM functions in your driver code probably need to first set the CS pin low, do some SPI operation, and set it high again after that.
For convenience, I usually add some simple helper functions to the driver source file:
static GPIO_TypeDef *_cs_port;
static uint16_t _cs_pin;
static void _chip_select(void)
{
HAL_GPIO_WritePin(_cs_port, _cs_pin, GPIO_PIN_RESET);
}
static void _chip_deselect(void)
{
HAL_GPIO_WritePin(_cs_port, _cs_pin, GPIO_PIN_SET);
}
In that case, I usually intialize the driver and and keep track of the peripheral instance and chip select GPIO, similar to this:
static SPI_HandleTypeDef *_spi;
static uint8_t _init = 0;
int8_t eeprom_init(
SPI_HandleTypeDef *spi,
GPIO_TypeDef *gpio_cs_port,
uint16_t gpio_cs_pin)
{
if (_init)
return -1;
_spi = spi;
_cs_port = gpio_cs_port;
_cs_pin = gpio_cs_pin;
/* do initialization here */
_chip_deselect();
_init = 1;
return 0;
}
int8_t eeprom_clear(void)
{
if (!_init)
return -1;
/* do de-initialization here */
_spi = 0;
_cs_port = 0;
_cs_pin = 0;
_init = 0;
return 0;
}
int8_t eeprom_op_x(void)
{
if (!_init)
return -1;
_chip_select();
op_x(); /* todo */
_chip_deselect();
return 0;
}
I hope this helps :) ! There might be other issues in your hardware/software; this is probably not the full solution to your problem.
BTW: There are also ways to use hardware chip select (STM32 SPI peripheral), which I've never used (SPI / NSS in the reference manual). As far as I can tell, you also used SPI_NSS_SOFT in your SPI configuration, which requires you to manually set the chip select line.
BTW: Unrelated, but maybe of interest: ST provides simple HAL functions to access external I2C flash (HAL_I2C_Mem_*() functions).
edit 0 (more findings by skimming through code / datasheet):
Read_EEPROM() will not work like this, the data read from the bus isn't accessible outside the function's scope (C issue). Instead, a pointer to a read buffer could be passed to the function (or the read data could be returned as return value). For example like this: uint8_t Read_EEPROM (SPI_TypeDef *spi_handle, uint8_t addr, uint8_t *data, uint8_t byte_count)
In Read_EEPROM(): HAL_SPI_TransmitReceive() won't read the incoming bytes, when used like this. It receives and transmits at the same time. So it would make sense to first write the read / address command, and then start reading the incoming bytes (like in your code that has been commented out).
In Enable_/Disable_/Read_/Erase_EEPROM(): The number of bytes (size) seems to be wrong, it should be 2 instead of 1, in order to make HAL_SPI_Transmit() / HAL_SPI_TransmitReceive() transmit/receive the right number of bytes.
This IC does not seem to be well suited to be used with normal
SPI, since it requires a very specific bit sequence which is
not byte aligned (like you said). It might make sense to bit bang
the communication (like you've mentioned), and pay attention to every
little bit stated in the datasheet...
Since this seems to be an early test, I'd try to keep it as simple as possible, and get a first enable/write/read operation going, by bit-twiddling the same SPI pins by hand (reconfigured as normal GPIOs), so that the problems with the STM32's byte oriented SPI HAL functions won't get in your way. And then work towards a nice little driver... Maybe the STM32's SPI can still be used in some way, it's hard to tell for me right now...

Raspberry error write in i2c socket

I'm trying to control my domotic house by smartphone, the smartphone sends 3-4 byte to Raspberry via Internet(Wi-Fi) and Raspberry send all those bytes to the corresponding Arduino throught I2C bus(I've got two Arduinos).
When I send the commands to Raspberry it shows "Failed to write to the i2c bus"
Anyone can help me please?
int i2csend(msg_t *pmsg)
{
int fd;
/* Open I2C device */
if ((fd = open(device, O_RDWR)) < 0) error ("Can't open I2C device");
if (ioctl(fd, I2C_SLAVE, arduino_addr) < 0) error ("Can't talk to slave");
if (write(fd, (char *)pmsg, n) < n ) printf ("Failed to write to the i2c bus [1]\n");
else
{
read(fd, (char *)pmsg, n);
printf("Ricevuto il messaggio: %c%c %d %d\n", pmsg->tipo, pmsg->gruppo, pmsg->dato[0], pmsg->dato[1]);
}
close(fd);
return 0;
}
When I've used I2C on the raspi, I've never used the 'open' function in an if statement (like you have in the i2csend() function). Here's a sample from a (working) project of mine:
//open file for i2c interface
int fh=open("/dev/i2c-1",O_RDWR);
if (ioctl(fh, I2C_SLAVE, UIBC_ADDR) < 0){
printf("Couldn't establish contact with the UIBC\n");
}

TI TivaC Launchpad I2C Errors

I am trying to communicate over I2C with a Pololu MinIMU9v2 from a TM4C123GXL Launchpad, but every time I try to write to the bus, I am getting I2C_MASTER_ERR_ADDR_ACK and I2C_MASTER_ERR_DATA_ACK. Printing out the slave address shows that it looks right, so I'm thinking this may be something I may be doing wrong with the use of the TI Launchpad driver library.
Here's the initialization routine:
void
InitI2CBus(void)
{
// Initialize the TM4C I2C hardware for I2C0
SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |
SYSCTL_XTAL_16MHZ);
SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
GPIOPinConfigure(GPIO_PB2_I2C0SCL);
GPIOPinConfigure(GPIO_PB3_I2C0SDA);
GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3);
// Initialize the bus
I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), false);
}
Here is the code that attempts to read a byte from the device:
uint8_t
ReadByte(uint8_t slaveAddr, uint8_t subAddr)
{
// Write SUB
slaveAddr |= 1; // Set LSB to writemode
I2CMasterSlaveAddrSet(I2C0_BASE, slaveAddr, false);
I2CMasterDataPut(I2C0_BASE, subAddr);
I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_SEND);
while(I2CMasterBusy(I2C0_BASE)) { }
if (CheckError())
{
return 0;
}
// Read data
slaveAddr &= ~1; // Set LSB to readmode
I2CMasterSlaveAddrSet(I2C0_BASE, slaveAddr, true);
I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
while(I2CMasterBusy(I2C0_BASE)) { }
I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
while(I2CMasterBusy(I2C0_BASE)) { }
uint8_t response = I2CMasterDataGet(I2C0_BASE);
if (CheckError())
{
return 0;
}
return response;
}
Any ideas what I may be doing wrong?
I was having a heck of a time getting my I2C bus working on this board. I'm not sure if this is your issue, but here's the initialization code I'm using (I'm on I2C2):
1. SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C2);
2. SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
**3. GPIOPinTypeI2CSCL(GPIO_PORTF_BASE, GPIO_PIN_6);**
4. GPIOPinTypeI2C(GPIO_PORTF_BASE, GPIO_PIN_7);
5. GPIOPinConfigure(GPIO_PF6_I2C2SCL);
6. GPIOPinConfigure(GPIO_PF7_I2C2SDA);
7. I2CMasterInitExpClk(I2C2_BASE, SysCtlClockGet(), false);
8. I2CMasterSlaveAddrSet(I2C2_BASE, 0x48, false);
Line 3 was missing from most of the examples I could find, and I noticed it's also missing from your code. Before I added this line, I couldn't get my I2C bus to do anything; after adding it it's at least transferring data.
I'm not sure if this is the source of your issue or not, but thought I'd pass it along in case it helps.