How to use I2C GPIO expander's pin instead of RTS to control the RS485 direction, in Linux AUART kernel driver? - c

I'm creating an embedded system based on i.MX287 processor from NXP(Freescale). I'm using a core processing board which is connected to my evaluation board via a mini PCIe connector.
UARTs 0,3,4 are used as RS232 and UARTs 1,2 as RS485. The core board does not provide the RTS signals in its pinout, so I have to use pins from an I2C GPIO expander to control the RS485 direction. The GPIO expander module is also used for controlling some other devices on the board.
In user-space, I can control the direction pin using libi2c, but my client asked me to put the direction pin control in the UART driver.
Questions:
1- how can I interact with an i2c device inside the auart driver? (is it possible)
2- if it is possible, then how to prevent the i2c-0 bus from being blocked by the kernel? (I also need the userspace calls to the libi2c to work properly)
I googled a lot, but most cases are about how to use the I2C driver or how to activate GPIO pins in the sysfs, and I was able to do all of those.
The libi2c is for userspace so I cannot call it here. I also know that opening a file(/dev/i2c-0) in kernel and reading or writing to it is not a good idea. I am trying to understand what is the best way to handle this problem, without causing any concurrent access issues.
I would appreciate any ideas
P.S. - I don't have a deep understanding of how Linux kernel works, so sorry if my question is a little vague.
Edit 1:
based on #0andriy 's suggestion, I edited the DTS file and added the following to /arch/arm/boot/dts/my_dts_file.dts:
/dts-v1/;
#include "imx28.dtsi"
/ {
// some definitions
apbx#80040000 {
i2c0: i2c#80058000 {
pca8575: gpio#20 {
compatible = "nxp,pca8575";
reg = <0x20>; // PCA8575PW Address -0-0-0
gpio-controller;
#gpio-cells = <2>;
};
};
auart1: serial#8006c000 {
pinctrl-names = "default";
pinctrl-0 = <&auart1_2pins_a>;
linux,rs485-enabled-at-boot-time;
rs485-rts-delay = <0 0>; // in milliseconds
rts-gpios = <&pca8575 4 GPIO_ACTIVE_LOW>;
rs485-rts-active-low;
status = "okay";
};
auart2: serial#8006e000 {
pinctrl-names = "default";
pinctrl-0 = <&auart2_2pins_b>;
linux,rs485-enabled-at-boot-time;
rs485-rts-delay = <0 0>; // in milliseconds
rts-gpios = <&pca8575 5 GPIO_ACTIVE_LOW>;
rs485-rts-active-low;
status = "okay";
};
};
// some definitions
};
and then rebuilt the kernel. I also edited the mxs_auart_init_gpios function in the mxs-auart.c driver to print out the pin description of all the auart GPIOs at boot time. but gpiod = mctrl_gpio_to_gpiod(s->gpios, i) is always NULL.
the pca8575 GPIO controller is not added under /sys/class/gpio/
root# ls /sys/class/gpio
export gpiochip128 gpiochip64 unexport
gpiochip0 gpiochip32 gpiochip96
Edit 2:
auart1_2pins_a and auart2_2pins_b from the imx28.dtsi file :
auart2_2pins_b: auart2-2pins#1 {
reg = <1>;
fsl,pinmux-ids = <
MX28_PAD_AUART2_RX__AUART2_RX
MX28_PAD_AUART2_TX__AUART2_TX
>;
fsl,drive-strength = <MXS_DRIVE_4mA>;
fsl,voltage = <MXS_VOLTAGE_HIGH>;
fsl,pull-up = <MXS_PULL_DISABLE>;
};
auart1_2pins_a: auart1-2pins#0 {
reg = <0>;
fsl,pinmux-ids = <
MX28_PAD_AUART1_RX__AUART1_RX
MX28_PAD_AUART1_TX__AUART1_TX
>;
fsl,drive-strength = <MXS_DRIVE_4mA>;
fsl,voltage = <MXS_VOLTAGE_HIGH>;
fsl,pull-up = <MXS_PULL_DISABLE>;
};
I'm using kernel 4.14.13
the figure below demonstrates what I'm trying to achieve :

I'm not familiar at all with your board so take this answer with a pinch of salt, but I've noticed some funny things on your files.
First off, you need to define the I2C pin you want to use for toggling the direction inside the UART pinmux:
auart2_2pins_b: auart2-2pins#1 {
reg = <1>;
fsl,pinmux-ids = <
MX28_PAD_AUART2_RX__AUART2_RX
MX28_PAD_AUART2_TX__AUART2_TX
MX28_PAD_I2C0_SCL__I2C0_SCL
>;
fsl,drive-strength = <MXS_DRIVE_4mA>;
fsl,voltage = <MXS_VOLTAGE_HIGH>;
fsl,pull-up = <MXS_PULL_DISABLE>;
};
Make sure you double-check the pin name you want to use, I cannot be sure that is the right one.
Then, you seem to be missing the pinctrl for the I2C controller:
i2c0: i2c#80058000 {
pinctrl-names = "default";
pinctrl-0 = <&i2c0_pins_a>;
status = "okay";
pca8575: gpio#20 {
compatible = "nxp,pca8575";
reg = <0x20>; // PCA8575PW Address -0-0-0
gpio-controller;
#gpio-cells = <2>;
};
};
I could not confirm your reg and your pin numbers but I'm assuming you took it from your board's documentation. If you didn't, make sure you find a reliable source for your hardware.
Finally, I'm not sure why you want to have the RTS line active low, most transceivers have a DE/~RE input, which means you need to have the line active high to drive the bus. Maybe your driver is different...
What you are trying to do is documented to be working for other boards so I guess unless there is a bug you should be able to make it work.

Related

Synchronized PWMs from two different Timers on ZephyrRTOS (STM32h7)

I'm working on a C Project for University where I need to measure a Laser with a TCD1304 CCD-Sensor.
Therefore I am using Zephyr as OS on an STM32-H7(A3ZI-Q).
Since the TCD1304 has to receive different PWM signals to drive it, I am trying to generate PWMS with Zephyr.
So far I have already reached the point where I can generate two PWMs using Timer1 and Timer4.
It should be mentioned that one frequency is 2MHz and the other one is 500kHz, which are multiples of each other.
The different frequencies can be adjusted well, but I can always detect a drift of the two signals to each other.
I have to fix this drift by synchronizing the PWMs of the two timers.
Otherwise it won't be possible to drive the TCD1304.
Here I provide an overview of the used code:
prj.conf
CONFIG_PWM=y
CONFIG_SYS_CLOCK_TICKS_PER_SEC=100
main.c
#define FM_PERIOD 2000000U
#define SH_PERIOD 500000U
void main(){
/* Initialize and start PWM Signals */
const struct device *fM_device = device_get_binding("FM");
const struct device *SH_device = device_get_binding("SH");
if (!fM_device | !SH_device){
printk("TCM1304 - PWM initialize failed.");
return;
}
pwm_set(fM_device, 1, FM_PERIOD, 0.5*FM_PERIOD, 0);
pwm_set(SH_device, 4, SH_PERIOD, 0.5*SH_PERIOD, 0);
}
nucleo_h7a3zi_q.overlay
...
&timers1 {
st,prescaler = <10>;
status = "okay";
pwm1: pwm {
status = "okay";
label = "FM";
pinctrl-names = "default";
pinctrl-0 = <&tim1_ch1_pe9>;
};
};
&timers4 {
st,prescaler = <100>;
status = "okay";
pwm3: pwm {
status = "okay";
label = "SH";
pinctrl-names = "default";
pinctrl-0 = <&tim4_ch4_pd15>;
};
};
...
When generating the two signals by the same timer with different frequencies, I noticed that some periods are cut off at the 2MHz signal.
From this I conclude that different frequencies cannot be created by the same timer?
I assume that this is a common problem for which there must be a solution.
Maybe by assigning the same input clocks to the Timers in the overlay-file (clocks=<XX>-Option) or by using any kind of master-slave principle within Zephyr (Github-Issue).
It seams that my issue is closely related to this Issue, but within Zephyr.
Is there a solution that avoids interrupts in the best possible way to reduce CPU load? I would be very glad to get some help.

How to change stm32 PLL parameters in uboot device tree?

I'm new to u-boot and I have a custom board based on the Waveshare CoreH7 board. my crystal(HSE) frequency is 8MHz and I want to have a 400MHZ core frequency using the PLL1. according to this documentation I should add the below code to my board.dts file.
rcc {
vco1#58024430 {
#clock-cells = <0>;
compatible = "stm32,pll";
reg = <0>;
st,clock-div = <4>; // 8MHz divided by 4 = 2MHz
st,clock-mult = <400>; // (2MHz * 400)/2 = 400MHz
st,frac-status = <0>;
st,frac = <0>;
//st,vcosel = <1>;
st,pllrge = <1>;
};
};
so, now my frequency should be 400MHz. but it's not!
I set the uboot logging level to 8 to see all logs. and after checking the output logs on the serial terminal, I realized that u-boot even won't care about those "vco1" parameters in my device tree. so I dicided to take a look at the rcc driver, and when I saw the code, I realized that pll parameters are defined in the driver itself, and the driver even won't care about those parameters that I wrote them in my device tree.
this is a part of stm32h7 driver at 329th line:
/*
* OSC_HSE = 25 MHz
* VCO = 500MHz
* pll1_p = 250MHz / pll1_q = 250MHz pll1_r = 250Mhz
*/
struct pll_psc sys_pll_psc = {
.divm = 4,
.divn = 80,
.divp = 2,
.divq = 2,
.divr = 2,
};
I can't understand what's happening here! As I know, the driver should read these parameters from device tree. So, if the user should write these parameters in the device tree, why the driver has these values in it when it can read them from device tree?

Tiva C cannot change value of MDR register

i have a tiva c micro controller the tm4c123gxl and i have been trying for a while now to use the I2C module on the board with a digital accelrometer with no result , i have been trying to set the MDR register with a certain value to send but it stays as 0
here is the code i am using for intialization till reaching part where i set the MDR register im using step by step debugging i run the code initially to the assignment step of I2C3_MDR_R = 0x2D;
void PortDInit(void)
{
volatile unsigned long delay=0;
SYSCTL_RCGCI2C_R|=0x8; //1-set clock of I2C of module 3
delay = SYSCTL_RCGC2_R; //2-delay to allow clock to stabilize
SYSCTL_RCGC2_R |= 0x00000008; //3-port D clock
delay = SYSCTL_RCGC2_R; //4-delay to allow clock to stabilize
GPIO_PORTD_AFSEL_R |= 0x03; //5-alternate function set for I2C mode
GPIO_PORTD_DEN_R |=0x03; //6-enable digital functionality for PA6 and PA7
GPIO_PORTD_ODR_R|=0x02; //7-enable open drain mode for I2CSDA register of port A
GPIO_PORTD_PCTL_R = 0x00000033; //8-set PCTL to I2C mode
I2C3_MCR_R= 0x00000010; // 9-intialize the i2c master
I2C3_MTPR_R = 0x00000007; // 10-number of system clock cycles in 1 scl period
I2C3_MSA_R = 0x3A // set slave address and read write bit
I2C3_MDR_R = 0x2D; // data to be sent BREAK POINT HERE using single step here yields MDR with same value = 0
I2C3_MCS_R = 0x00000003; // follow transmit condition
while(I2C3_MCS_R &= 0x40 == 1); // wait bus is busy sending data
if(I2C3_MCS_R&=0x04 ==1)
{
//handle error in communication
}
else
{
//success in transmission
}
what i have done to reach this code
carefully understood the I2C protocol how it works etc.
check the data sheet and follow the initalization steps mentioned there step by step which got me to this code
i know i should use tivaware library which will be easier but using
the registers helps me understand more of how everything is working ,
im still a student
at first i didnt have the digital enable line as it wasnt mentioned
to be activated for the I2C but its only logical it should be there
as we are using digital values i tried with both yielded the same
output mdr=0
i am using keil 4 as my IDE and im viewing the values of registers of
I2C module 3 to know whether data is placed in MDR or not
hope any one helps
thanks.
This is a long shot, but here goes:
in your comments, step 6 says
//6-enable digital functionality for PA6 and PA7
but it appears you are working on GPIO_PORTD...
maybe its a comment typo (you meant PD6 and PD7)
but just double check you are looking at the right pins...
Good luck!

pic32 only receives 1 byte in spi

I'm struggling with, probably, a very simple problem.
I have a Cypress CY8 controller acting as SPI master, which should communicate with a PIC32mx in slave mode to exchange data packets.
However i cannot even fix simple transmission of multiple bytes from the master to the slave. I've set up the cypress to transmit a char of increasing value (0-255) with a pause (and slave select toggle) in between. The pic should read the incoming byte and then print it over uart to my pc (the uart connection works).
But the pic only prints the first character it receives continuously instead of it being updated.
If i check my logic sniffer, the cypress does send incrementing values and the pic relays them back over the MISO line (looks like the shift buffer isn't cleared).
What could this be?
The cypress without the pic attached gives proper output:
https://dl.dropboxusercontent.com/u/3264324/Schermafdruk%202015-07-28%2015.43.28.png
With the pic attached it relays the data over MISO:
https://dl.dropboxusercontent.com/u/3264324/Schermafdruk%202015-07-28%2015.43.45.png
And this is my (now) extremely basic code to test it:
TRISBbits.TRISB2 = 1; // make Ra2 pin input (SDI)
TRISBbits.TRISB5 = 0; // make Ra2 pin output (SDO)
TRISBbits.TRISB15 = 1; //make RB14 output (SCK)
ANSELA = 0; // all ports digital
ANSELB = 0; // all ports digital
SYSKEY = 0x00000000;
SYSKEY = 0xAA996655;
SYSKEY = 0x556699AA;
CFGCONbits.IOLOCK=0; // unlock configuration
CFGCONbits.PMDLOCK=0;
SDI2R = 0b0100; //SDI2 on pin RB2
SS2R = 0b0011; //SS2 on pin rb10
RPB5R = 0b0100; //SDO2 on pin RB5
// SCLK is connected to pin RB14 (SCK) by default
SYSKEY = 0x00000000;
SPI2CON = 0; // Stops and resets the SPI1.
rData=SPI2BUF; // clears the receive buffer
SPI2BRG=207; // use FPB/4 clock frequency <-- not important in slave mode right?
SPI2STATCLR=0x40; // clear the Overflo
SPI2CON=0x8180;
unsigned char t;
while(1){
t = SpiChnReadC(2);
//t = SPI2BUF; <== i've tried this also
sendData(t); <== uart routine
}
As i do receive a character and the spi data is relayed back to the cypress constantly i think something goed wrong with reading/clearing the spi data structure in the PIC. But i can't figure out why.
As i read in the datasheet, reading from SPI2BUFF gives me the received data, and clears the read flags so new data can be received, but it looks like that doesn't happen...
Can someone shine a light on this for me?
Thanks in advance
Timberleek
You should try making you SPI handler ISR driven to keep you from constantly polling, can also help the debugging since you'll only get notifications when the SPI is actually transacting.
NOTE: I'm bringing this from my FreeRTOS impl, so my ISR definition is not XC32 exactly...
/* Open SPI */
SPI1CON = 0;
spi_flags = SPICON_MODE32 | SPICON_ON;
SpiChnOpen(1,spi_flags,BRG_VAL);
SpiChnGetRov(1,TRUE);
mSPI1ClearAllIntFlags();
mSPI1SetIntPriority(priority + 1);
mSPI1SetIntSubPriority(0);
mSPI1RXIntEnable(1);
void vSPI1InterruptHandler(void)
{
unsigned long data;
if (IFS0bits.SPI1EIF == 1)
{
mSPI1EClearIntFlag();
}
if (IFS0bits.SPI1RXIF == 1)
{
data = SPI1BUF;
//sendData(data);
}
mSPI1RXClearIntFlag();
}

Raspberry Pi spidev.h SPI Communication

I try to establish a spi communication from RPi (Master) to an EtherCAT Device (Slave).
The transfer of data got a scheme.
I have to transfer 2 bytes which address registers and the following bytes transfer data till a chip select terminates the communication.
This is my created attempt. With cs_change, i can tell my spi communication to deselect Chip Select before the next transfer starts.
char transfer(UINT8 data, char last)
{
char last_transfer = last;
int ret;
uint8_t tx[] = { data };
uint8_t rx[ARRAY_SIZE(tx)] = { };
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = ARRAY_SIZE(tx),
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
.cs_change = 0,
};
if (last_transfer)
tr.cs_change = 1;
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1)
printf("can't send spi message");
return rx[tr.len-1];
}
First problem:
I think it is too late to deselect Chip Select first at new transfer.
So my first question: Is there another way to control my Chip Select signal, maybe another library i can use?!
Second problem:
I want to read from spi without writing on it, how can i realise that ( with a simple read(fd...) ?!)
I hope you guys can support me :)
Now this is spidev_test.c application you are referring to. The implementation seems to work as slave SPI device would be deselected after your last transfer. It stays selected until after the last transfer in a message.
Each SPI device is deselected when it's not in active use, allowing other drivers to talk to other devices as may be your SPI bus is shared among other slave SPI devices.
And moreover you are going for full-duplex.
Standard read() operation is obviously only half-duplex.
So whenever your SPI slave device wants to send data to the SPI controller(Master), then it must have some interrupt mechanism so that your driver/app can set the SPI controller to "SPI_IOC_RD_MODE" and can make "SPI_IOC_MESSAGE" providing only rx_buf OR you can go for only simple read() operation as the transfer would be half_duplex after setting the SPI controller to read mode. -Sumeet
You can use SPI_NO_CS mode and toggle the CS pins as GPIO using the wiringPi library... (from http://wiringpi.com/ )

Resources