Configure Linux I2C Speed - performance

I am using I2C on the Snowball board, running at 400KHz by default and would like to reduce this to 100KHz.
I use the api defined in and configure as follows
m_fd = open(m_filename.c_str(), O_RDWR);
if (ioctl(m_fd, I2C_SLAVE_FORCE, m_addr) < 0)
{
throw I2cError(DeviceConfigFail);
}
Does anyone know how I would go about changing the speed to standard mode.
Thanks

You can change the I2C SCL frequency in your driver's 'struct i2c_gpio_platform_data'.
static struct i2c_gpio_platform_data xyz_i2c_gpio_data = {
.sda_pin = GPIO_XYZ_SDA,
.scl_pin = GPIO_XYZ_SCL,
.udelay = 5, //#udelay: signal toggle delay. SCL frequency is (500 / udelay) kHz
....
};
Changing 'udelay' changes your 'xyz' i2c device's clock frequency.

You should change the I2C Frequency in driver source file of the corresponding peripheral (ie: Slave device to which you are communicating through I2C. Example: EEPROM/Camera etc.)
You may find some macro defined in that driver source code... like:
#define EEPROM_I2C_FREQ 400000 //400KHz
Change it to:
#define EEPROM_I2C_FREQ 100000 //100KHz
Only for that corresponding driver, I2C frequency/speed will be changed.

Related

How to find dma_request_chan() failure reason details?

In an external kernel module, using DMA Engine, when calling dma_request_chan() returns an error pointer of value -19, i.e. ENODEV or "No such device".
Now, in the active device tree, I do find a dma-names entry with what I'm trying to get a channel for, so my suspicion is that something else deeper in the forest is already not found.
How do I find out what's wrong?
Background:
I have a Zynq MP Ultrascale+ board here, with an FPGA design which uses AXI VDMA block to provide one channel of data to be received on the Cortex A's Linux, where the data is written to DDR4 by the FPGA and to be read from Linux.
I found that there is a Xilinx DMA driver included in the kernel, in the Xilinx source repo anyway, currently kernel version 5.6.0.
And that that driver has no user space interface, such that an intermediate kernel driver is needed.
This is depicted, and they have an example here: Section "4 DMA Proxy Design". I modified the code in the dma-proxy.c of the zip file linked there such that it uses only the RX channel, i.e. also only tries to request it.
The code for that is here, to not make this post huge:
Modified dma-proxy.c at onlinegdb.com
Line 407 has the function create_channel(), which used to use dma_request_slave_channel() which ditches the error code of the function it wraps, so to see the error, I am using that one instead: dma_request_chan().
The function create_channel() is called in function dma_proxy_probe() # line 470 (the occurences before that are deactivated by compile switch).
So by way of this call, dma_request_chan() will be called with the parameters:
create_channel(pdev, &channels[RX_CHANNEL], "dma_proxy_rx", DMA_DEV_TO_MEM);
The Device Tree for my board has an added node for dma-proxy driver as is shown at the top of the dma-proxy.c
dma_proxy {
compatible ="xlnx,dma_proxy";
dmas = <&axi_dma_0 0>;
dma-names = "dma_proxy_rx";
};
The name "axi_dma_0" matches with the name in the axi DMA device tree node:
axi_dma_0: dma#a0000000 {
#dma-cells = <0x1>;
clock-names = "s_axi_lite_aclk", "m_axi_s2mm_aclk";
clocks = <0x3 0x47 0x3 0x47>;
compatible = "xlnx,axi-dma-7.1", "xlnx,axi-dma-1.00.a";
interrupt-names = "s2mm_introut";
interrupt-parent = <0x1d>;
interrupts = <0x0 0x2>;
reg = <0x0 0xa0000000 0x0 0x1000>;
xlnx,addrwidth = <0x28>;
xlnx,sg-length-width = <0x1a>;
phandle = <0x1e>;
dma-channel#a0000030 {
compatible = "xlnx,axi-dma-s2mm-channel";
dma-channels = <0x1>;
interrupts = <0x0 0x2>;
xlnx,datawidth = <0x40>;
xlnx,device-id = <0x0>;
};
If I now look here:
% cat /proc/device-tree/dma_proxy/dma-names
dma_proxy_rx
Looks like my dma_proxy_rx, that I'm trying to request the channel for, is in there.
Edit:
In the boot log, I see this:
xilinx-vdma a0000000.dma: Please ensure that IP supports buffer length > 23 bits
irq: no irq domain found for interrupt-controller#a0010000 !
xilinx-vdma a0000000.dma: unable to request IRQ 0
xilinx-vdma a0000000.dma: WARN: Device release is not defined so it is not safe to unbind this driver while in use
xilinx-vdma a0000000.dma: Xilinx AXI DMA Engine Driver Probed!!
There are warnings - but in the end, the Xilinx AXI DMA Engine got "probed", meaning the lowest level driver loaded and is ready, right?
So it looks to me like there should be my device, but the kernel disagrees.
I've got the same problem with similar configuration. After digging a lot of kernel source code (especially drivers/dma/xilinx/xilinx_dma.c) I've solved this problem by changing channel number in dmas parameter from 0 to 1 in dma-proxy device tree entry like this:
dma_proxy {
compatible ="xlnx,dma_proxy";
dmas = <&axi_dma_0 1>;
dma-names = "dma_proxy_rx";
};
It seems that dma-proxy example is written for AXI DMA block with both mm2s (channel #0) and s2mm (channel #1) channels. And if we remove mm2s channel from AXI DMA block, the s2mm channel stays #1.

How to connect multiple RC522 RFID Reader to Raspberry PI?

I want to know if it is possible to connect multiple RC522 to one Raspberry PI, if yes how do we do it? I read in some articles that with CS(chip select) pin we can control Readers, but the CS pin is not present on RC522 I have.
Can you please help me?
Chip Select (CS) is referred to as SS or NSS in some of the RFC522 documentation, is that the issue? If so, connect your CS0 to NSS on one module, CS1 to the NSS on the other, and you can use both modules on the same SPI bus.
If you need more devices than that, you could switch to the I2C interface. In I2C mode, a number of the pins on the chip become address selects, so you can put a whole bunch of them on the same I2C bus. The data sheet for the chip is always helpful with this sort of information. Section 8.1, "Digital interfaces" may be particularly helpful.
In I2C mode, you have 5 bits of addressing, so you can put up to 32 devices on the bus.
Posted this answer before on the raspberry pi stackexchange.
I'll just repost it here for visibility.
You can use the RST Pins to select the reader you want to use. Connect all the other pins in parallel (see schematic below). Just set all the RST pins to low, except the one on the pin you want to use. Set that one to high. Then initialize SPI, read/write, and close SPI again.
I wrote a more detailed explanation here.
This is the schematics and code I made:
Schematic for 2 readers
Schematic for 4 readers
Code to run it all (using pimylifeup's MFRC522-Python Library):
import RPi.GPIO as GPIO
from mfrc522 import SimpleMFRC522
import spidev
class NFC():
def __init__(self, bus=0, device=0, spd=1000000):
self.reader = SimpleMFRC522()
self.close()
self.bus
self.boards = {}
self.bus = bus
self.device = device
self.spd = spd
def reinit(self):
self.reader.READER.spi = spidev.SpiDev()
self.reader.READER.spi.open(self.bus, self.device)
self.reader.READER.spi.max_speed_hz = self.spd
self.reader.READER.MFRC522_Init()
def close(self):
self.reader.READER.spi.close()
def addBoard(self, rid, pin):
self.boards[rid] = pin
def selectBoard(self, rid):
if not rid in self.boards:
print("readerid " + rid + " not found")
return False
for loop_id in self.boards:
GPIO.output(self.boards[loop_id], loop_id == rid)
return True
def read(self, rid):
if not self.selectBoard(rid):
return None
self.reinit()
cid, val = self.reader.read_no_block()
self.close()
return val
def write(self, rid, value):
if not self.selectBoard(rid):
return False
self.reinit()
self.reader.write_no_block(value)
self.close()
return True
if __name__ == "__main__":
nfc = NFC()
nfc.addBoard("reader1",5)
nfc.addBoard("reader2",6)
data = nfc.read("reader1")
nfc.write("reader2",data)

Setting GPIO in MLO does not work

I want to know how to pin multiplex pins in initial phase of boot i.e is in spl(MLO).
What I am trying to do is change the default pin configuration to gpio one, so that I can see high or low on the pin.
On P8 header I tried to change the mode 0 from default 'TIMER4' to gpio2[2] i.e mode 7. So I did this
static struct module_pin_mux gpio2_2_pin_mux[] = {
{OFFSET(gpmc_wen), (MODE(7) | PULLUDEN)},
{-1},
};
and called this function
configure_module_pin_mux(gpio2_2_pin_mux);
in board/ti/am335x/mux.c
I didn't saw any voltage on 7th pin of P8 header?
What is the correct way to do this?
file link : http://textuploader.com/5eh6u
you can search with '?' in the file to see what I added.
P.S
I checked with checking pin mux setting on uart0 and tried to read it if that is same.
So I wrote this in
./arch/arm/cpu/armv7/omap-common/boot-common.c
void spl_board_init(void)
{
/*
* Save the boot parameters passed from romcode.
* We cannot delay the saving further than this,
* to prevent overwrites.
*/
save_omap_boot_params();
unsigned int *mfi;
//control revision register
/* Prepare console output */
mfi = *(unsigned int *)(0x44E10980);
printf("1======> %x\n",mfi);
preloader_console_init();//it will print uboot version date and time information
mfi = *(unsigned int *)(0x44E10980);
printf("2======> %x\n",mfi);
more init code.....
}
I wanted to see this setting done in board/ti/am335x/mux.c
static struct module_pin_mux uart0_pin_mux[] = {
{OFFSET(uart0_rxd), (MODE(0) | PULLUP_EN | RXACTIVE)}, /* UART0_RXD */
{OFFSET(uart0_txd), (MODE(0) | PULLUDEN)}, /* UART0_TXD */
{-1},
}
But it printed value as 37. that means the pin is in GPIO mode.
How is this possible that the pin that should be in mode 0 is in 7th mode?

atmega8 UART- doesn't show character in realterm

Hi i'm new to this and i need help. It's suppose to just show the 'S' in the realterm instead it gives 'null'. What would be the problem? could it be the register? or the code itself?
#include <avr/io.h>
#include <util/delay.h>
void UART_Init(unsigned int ubrr)
{
UBRRH=(unsigned int)(ubrr>>8);
UBRRL=(unsigned int)ubrr;
UCSRA=0x00;
UCSRB=(1<<TXEN)|(1<<RXEN);
UCSRC=(0<<USBS)|(1<<UCSZ0)|(1<<UCSZ1);
}
void UART_Tx(unsigned char chr)
{
while (bit_is_clear(UCSRA,UDRE)){}
UDR=chr;
}
int main(void)
{
UART_Init(95);
DDRD|=0B11111111;
PORTD|=0B11111111;
while(1){
_delay_ms(10);
UART_Tx('S');
}
}
System is running on xtal with 14745600 Hz. Speed on host is 9600 baud. all settings should be 8N1.
You need to set the URSEL when writing to the UCSRC register.
Change
UCSRC=(0<<USBS)|(1<<UCSZ0)|(1<<UCSZ1);
to
UCSRC=(1<<URSEL)|(0<<USBS)|(1<<UCSZ0)|(1<<UCSZ1);
From the data sheet:
The UBRRH Register shares the same I/O location as the UCSRC Register. Therefore some
special consideration must be taken when accessing this I/O location. When doing a write access of this I/O location, the high bit of the value written, the USART Register Select (URSEL) bit, controlswhich one of the two registers that will be written. If URSEL is
zero during a write operation, the UBRRH value will be updated. If URSEL is one, the UCSRC
setting will be updated.
The rest of the code looks fine to me.
change UART_Tx('S'); using UART_Tx("S");

PIC16F877 + 24LC64 via i2c

My task is to copy first 255 bytes from external EEPROM (24LC64) to internal (PIC16F877) via i2c bus. I've read AN1488, all datasheets, MikroC gide (oh, yes, I'm using MikroC), but hopeless.. Meaning that my code trys to read smtng but then, reading my PIC's eeprom at programmer (which can't read 24LC64, so I don't even know what's on it, but there is smtng defenately and it is different from what i'm getting), and I'm getting all EEPROM filled by "A2" or "A3". My guess is that it's that first addr, by which I'm addressing to 24LC64. Could you pls inspect my code (it's quite small =)) and point me at my misstakes.
char i;
unsigned short Data;
void main(){
PORTB = 0;
TRISB = 0;
I2C1_Init(100000);
PORTB = 0b00000010;
for (i = 0x00; i<0xFF; i++) {
I2C1_Start();
I2C1_Wr(0xA2); //being 1010 001 0
//I'm getting full internal EE filled with what's in brackets from above
I2C1_Wr(0b00000000);
I2C1_Wr(i);
I2C1_Repeated_Start();
I2C1_Wr(0xA3); //being 1010 001 1
Data = I2C1_Rd(0);
I2C1_Stop();
EEPROM_write(i, Data); //How could that 1010 001 0 get into here???
Delay_100ms();
}
PORTB = 0b00000000;
while (1) {
}
}
P.S. I've tryed this with sequantial read, but it "reads" (again that "A2"..) only 1st byte.. So i've posted this one..
P.S.S. I`m working in "hardware", no Proteus involved..
P.S.S.S. I can't test writing, because I have only one 24LC64 with important info on it, so it's even pulld up to Vcc on it's WP pin...
This isn't a specific answer but more of a checklist for I2C comms, since it's difficult to help with your problem without looking at a scope and without delving into the API calls that you've provided.
Check the address of your EEPROM. I2C uses a 7-bit address with a R/W bit appended to the end, so it's easy to make a mistake here.
Check the command sequence that your EEPROM expects to receive for a "data read"
Check how the I2C_ API that you're using deals with acks from the EEPROM. They need to be handled somewhere (usually in an ISR) and it's not obvious where they're dealt with from your example.
Check that you've got the correct pull-ups on SDA and SCL as per the requirements of your design - they're needed for I2C to work.

Resources