Setting GPIO status during boot process - linux

I have an Intel Galileo board with LED connected to one of GPIO pin. When I am connecting power to Galileo, LED lights up for a second and then turns off again.
Once my application start I am able to manipulate LED. But now I want my LED to turned ON during whole boot process and once my application starts it should manipulate LED after then. I guess to achieve this I have to change kernel code and build it again completely.

If possible, you can make the default state of the GPIO high/low in Boot loader. Or, Refer the following Changes in linux kernel and device tree.
:arch/xxx/boot/dts/xxxx.dts
led#4 {
label = "evmsk:green:heartbeat";
gpios = <&gpio1 7 0>;
linux,default-trigger = "heartbeat";
default-state = "off";
};
:drivers/leds/leds-gpio.c
state = of_get_property(child, "default-state", NULL);
if (state) {
if (!strcmp(state, "keep"))
led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
else if (!strcmp(state, "on"))
led.default_state = LEDS_GPIO_DEFSTATE_ON;
else
led.default_state = LEDS_GPIO_DEFSTATE_OFF;
}
ret = gpio_direction_output(led_dat->gpio, led_dat->active_low ^ state);

Related

SPI conflict between SD.h and U8x8.h (LCD)

I want to reproduce a .wav file on my Arduino Uno via an SD card while at the same time using an LCD display (Nokia 5110). I'm having trouble trying to make the double SPIs work and since I'm not really familiar with the libraries that I used I decided to ask for help
The problem is that once the program execute SD.begin() the display stops working and the SPI communication is working only on the SD.
In this setup that I'm showing I will initialize the SD in the setup() and in the loop using a button I'm supposed to write stuff on the display:
//Libraries
#include <U8x8lib.h>
#include <SPI.h>
#include "SD.h"
#include "TMRpcm.h"
//Display (pin 13, pin 11, pin A1, pin 12, pin A0 )
U8X8_PCD8544_84X48_4W_SW_SPI lcd(LCD_CLK, LCD_DIN, LCD_CE, LCD_DC, LCD_RST);
//Speaker
TMRpcm music;
void setup() {
//Serial port -> Open
Serial.begin(9600);
while (!Serial) {}
//LCD
display_setup();
//SD and Speaker
sdAndSpeaker_setup();
}
void loop() {
topButton.poll();
if (topButton.buttonClicked) {
display_something();
}
}
Ignore the function related to the button, I removed most of its code to avoid cluttering the page.
Here's when the problem occurs:
void sdAndSpeaker_setup() {
music.speakerPin = 10;
// (pin 4)
if (!SD.begin(SD_CS)) {
Serial.println("SD fail");
Serial.flush();
abort();
}
music.setVolume(4);
music.play("Daybreak.wav");
music.quality(2);
}
I know that by changing the pins of display to other value (rather than 11, 12 and 13) the problems is solved but most of the pins in my board are already used and I don't have this luxury... I think this has something to do with the U8x8.h and SD.h library but I don't have the skills to tweak those.
If anyone has a suggestion I'll appreciate!
Thanks!
EDIT:
Thanks to Juraj for the clarification, here's my new changes for the corrected constructor:
U8X8_PCD8544_84X48_4W_HW_SPI lcd(LCD_CE, LCD_DC, LCD_RST);
And for the setup_display()
void display_setup() {
lcd.begin();
pinMode(LCD_LIGHT, OUTPUT);
digitalWrite(LCD_LIGHT, HIGH);
lcd.setFont(u8x8_font_chroma48medium8_r);
lcd.drawString(1,2, "Hello!");
}
Unfortunately it still doesn't work...
The constructor
U8X8_PCD8544_84X48_4W_SW_SPI(uint8_t clock, uint8_t data, uint8_t cs, uint8_t dc, uint8_t reset = U8X8_PIN_NONE)
creates a 'driver' which uses software SPI. But you supply pins of hardware SPI as parameters. Hardware SPI is used by the SD library over the SPI library to access the SD card, so hardware SPI conflicts with the software SPI of the display library.
There is a
U8X8_PCD8544_84X48_4W_HW_SPI(uint8_t cs, uint8_t dc, uint8_t reset = U8X8_PIN_NONE)
constructor which uses the hardware SPI over the SPI library. The SPI library 'knows' the pin numbers of the SPI pins. (SPI library is part of the boards package. It can't be installed separately.)
If you want to use the software SPI, use free pins. The hardware SPI uses pins 11, 12, 13 on Uno.

Listening to only the first input of GPIO pin on a Raspberry pi with Node.js

I have a probably very simple question.
I have a Raspberry Pi 3 model B+ running Raspbian Buster hooked up to an infrared receiver on GPIO 7 (pin 26) and an LED hooked up to GPIO 4 (pin 7). What I want to do is to switch on the LED as soon as the receiver receives an infrared signal and back off again if it receives receives the signal again. This is running as a Node.js Script. It is important to mention that the infrared signal should be random and shouldn't be decoded. Any signal should do the job. In order to read the IR input on GPIO 7, I am using the rpi-gpio packet. But when I fire an IR signal from a remote, the LED turns on and off in a high frequency (I'm guessing in the frequency of the IR signal bursts).
So what I want to do is for the LED to only react to the very first input change on the GPIO pin and not to react to all of the changes provoked by the IR signal code. The simple code I have so far is as follows:
//used for IR receiver, refers to pins as pin#
var gpio = require('rpi-gpio');
//used for LED, refers to pins as GPIO#
var Gpio = require('onoff').Gpio;
var LED = new Gpio(4, 'out');
//listening for GPIO input changes
gpio.on('change', function(channel, value) {
console.log('Channel ' + channel + ' value is now ' + value);
blinkLED();
});
gpio.setup(26, gpio.DIR_IN, gpio.EDGE_BOTH);
//changing state of LED
function blinkLED() {
if (LED.readSync() === 0) {
LED.writeSync(1);
} else {
LED.writeSync(0);
}
}
Does anyone have any ideas? Is it maybe possible to stop listening for inputs after the first change? How would I do that?
I'm grateful for any help and hints!
enjoy your weekend!

FTDI LibMPSSE SPI

I recently purchased the FTDI C232HM-DDHSL-0 USB cable for use with SPI and I2C devices; my goal is to read the memory from an SPI memory chip that I removed from a router. However, I am having issues getting the libMPSSE library & 2xx drivers to send signals to a connected device.I hooked the leads of the C232 up to a Saleae logic analyzer and saw that no signals were being output!
Useful links for this question:
FTDI C232HM-DDHSL-0 (purchase):
https://www.ftdichip.com/Products/Cables/USBMPSSE.htm
C232HM-DDHSL-0 data sheet:
https://www.ftdichip.com/Support/Documents/DataSheets/Cables/DS_C232HM_MPSSE_CABLE.PDF
Memory Chip data sheet: https://www.macronix.com/Lists/Datasheet/Attachments/7370/MX25L6406E,%203V,%2064Mb,%20v1.9.pdf
2xx drivers: https://www.ftdichip.com/Drivers/D2XX.htm
MPSSE SPI library:
https://www.ftdichip.com/Support/SoftwareExamples/MPSSE/LibMPSSE-SPI.htm
I am using a Debian x86_64 machine. The MPSSE SPI download only had the i386 version of the library, so I downloaded the MPSSE SPI source (https://www.ftdichip.com/Support/SoftwareExamples/MPSSE/LibMPSSE-SPI.htm) and built the x86_64 bit version. There were no build errors or warnings.
I copied libftd2xx.so and libMPSSE.so to /usr/local/lib.
I plugged the cable in, then removed the following kernel modules:
ftdi_sio <-- Readme says to take take out
usbserial <-- Readme says to take out
usb_wann <-- Needed to remove to take out usbserial
qcserial <-- Needed to remove to take out usbserial
At this point I was hoping that my set up was done correctly.
I connected 6x cables to the 8 pin memory chip:
VCC <--> red lead
Ground <--> black lead
Chip select <--> brown lead
Data in <--> green
serial clock <--> orange
Data out <--> yellow
I did not connect hold and write protect pins on the memory chip. Recall my goal is to read the memory out of the chip, and the waveform for the read did not show those pins were necessary.
I am using this program:
/* Standard C libraries */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include "ftd2xx.h"
#include "libMPSSE_spi.h"
FT_HANDLE ftHandle;
uint8 tx_buffer[4096] = {0};
uint8 rx_buffer[4096] = {0};
int main()
{
uint8 i = 0;
int sizeToTransfer = 0;
int sizeTransfered = 0;
FT_STATUS status = FT_OK;
FT_DEVICE_LIST_INFO_NODE devList = {0};
ChannelConfig channelConf = {0};
channelConf.ClockRate = 30000000; // 30Mhz
channelConf.LatencyTimer = 75;
channelConf.configOptions = SPI_CONFIG_OPTION_MODE0 | SPI_CONFIG_OPTION_CS_DBUS3 | SPI_CONFIG_OPTION_CS_ACTIVELOW;
channelConf.Pin = 0x00000000;/*FinalVal-FinalDir-InitVal-InitDir (for dir 0=in, 1=out)*/
//
// Open the channel and dump some information
//
status = SPI_GetChannelInfo(0,&devList);
if (status != FT_OK)
{
printf("SPI_GetChannelInfo failed, status = %d\n", status);
return -1;
}
printf("Flags=0x%x\n",devList.Flags);
printf("Type=0x%x\n",devList.Type);
printf("ID=0x%x\n",devList.ID);
printf("LocId=0x%x\n",devList.LocId);
printf("SerialNumber=%s\n",devList.SerialNumber); // TODO: Why blank?
printf("Description=%s\n",devList.Description);
printf("ftHandle=0x%p\n",devList.ftHandle);/*is 0 unless open*/
//
// Open channel 0
//
status = SPI_OpenChannel(0,&ftHandle);
if (status != FT_OK)
{
printf("SPI_OpenChannel failed, status = %d\n", status);
return -1;
}
//
// Initialize the channel: See configuration structure at top of main()
//
status = SPI_InitChannel(ftHandle,&channelConf);
if (status != FT_OK)
{
printf("SPI_InitChannel failed, status = %d\n", status);
return -1;
}
//
// Send the read command (0x03), and read what we get in our receive buffer
//
sizeToTransfer=8;
sizeTransfered=0;
tx_buffer[0] = 0x03;
status = SPI_ReadWrite(ftHandle, rx_buffer, tx_buffer, sizeToTransfer, &sizeTransfered,
SPI_TRANSFER_OPTIONS_SIZE_IN_BYTES|SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE|SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE);
if (status != FT_OK)
{
printf("SPI_ReadWrite failed, status = %d\n", status);
return -1;
}
//
// Dump the receive buffer to see if we got anything.
//
printf("Size transfered = %d\n", sizeTransfered);
i = 0;
while (i < sizeTransfered)
{
printf("%02x",rx_buffer[i]);
i++;
}
printf("\n");
//
// Cleanup
//
status = SPI_CloseChannel(ftHandle);
if (status != FT_OK)
{
printf("SPI_CloseChannel failed, ret = %d\n", status);
}
return 0;
}
To make and run the program:
To compile: gcc read_memory.c -lMPSSE -ldl
To run: sudo ./a.out
My output is:
// The driver can at least detect the cable.
Flags=0x2
Type=0x8
ID=0x4036001
LocId=0x204
SerialNumber=
Description=USB <-> Serial
ftHandle=0x(nil)
Size transfered = 8
// Junk in the receive buffer
ff037fffffffffff
Whether I keep the memory chip connected or disconnected I get the same junk in the receive buffer. I see no LEDs light up on the cable or anything to indicate it is "working." I have tried SPI_Write() and SPI_Read() and get similar behavior. I tested on an Ubuntu VM (running on Windows host) and saw the exact same behavior.
I am really excited to get this to work so if anyone can help me with what I am doing wrong it would be greatly appreciated! Thanks!
Set device first to MPSSE mode, and it should work. See quite minimum (Python) example below based on FTD2XX library only (tested in Windows). LibMPSSE is not required to run simple SPI. Data can be read by s = dev.read(nbytes), where nbytes is byte count. Find more information in nice tutorial: Driving an SPI device using MPSSE
import ftd2xx
OPS = 0x03 # Bit mask for SPI clock and data out
OE = 0x08 # CS
dev = ftd2xx.open(0) # open first available device
if dev:
dev.setTimeouts(1000, 1000)
dev.setBitMode(OPS, 2) # bit mask, MPSSE mode
dev.write(bytes((0x86, 59, 0))) # set SCK to 100kHz
dev.write(bytes((0x80, 0, OE + OPS))) # CS low
data = 0x55,
n = len(data) - 1
dev.write(bytes(((0x11, n % 256, n // 256) + data))) # write SPI data
dev.write(bytes((0x80, OE, OE + OPS))) # CS high
dev.close()

Getting Linux IRQ number from hardware IRQ number

I have a GPIO peripheral, defined in Device Tree as that:
gpio0: gpio#2300000
{
compatible = "fsl,qoriq-gpio";
reg = <0x0 0x2300000 0x0 0x10000>;
interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
I want to write an interrupt handler for this (as a kernel module). But this IRQ number (66) is a hardware one and I need a virtual, Linux IRQ number to pass it to request_irq.
How can I get this number? There is only one interrupt controller (GIC).
Is there a way to do this without writing a platform device driver (as there is probably already one working in the system and I think I cannot register another one).
As per you comment you want to register a GPIO as an interrupt.
The node of device tree that you have posted is the interrupt controller node, which wont concern us for the task we have at hand.
To register a gpio as an interrupt, you first need to find a GPIO which can be configured as an interrupt (in most modern processors all GPIOs support it) and then you have to make sure it is not used by some other device by multiplexing it (if it is used by some one like SPI or UART etc , you can disable them from device tree if you are not using that entity).
now that you have a GPIO pin that you can use. Find the GPIO number on kernel that pin corresponds to (it depends on the architecture of your processor and its carrier board).
When you have that you can just write a simple module that will export your GPIO and use it as interrupt.
Below is a snippet from http://derekmolloy.ie
gpio_request(gpioButton, "sysfs"); // Set up the gpioButton
gpio_direction_input(gpioButton); // Set the button GPIO to be an input
gpio_set_debounce(gpioButton, 200); // Debounce the button with a delay of 200ms
gpio_export(gpioButton, false); // Causes gpio115 to appear in /sys/class/gpio
// the bool argument prevents the direction from being changed
// Perform a quick test to see that the button is working as expected on LKM load
printk(KERN_INFO "GPIO_TEST: The button state is currently: %d\n", gpio_get_value(gpioButton));
// GPIO numbers and IRQ numbers are not the same! This function performs the mapping for us
irqNumber = gpio_to_irq(gpioButton);
printk(KERN_INFO "GPIO_TEST: The button is mapped to IRQ: %d\n", irqNumber);
// This next call requests an interrupt line
result = request_irq(irqNumber, // The interrupt number requested
(irq_handler_t) ebbgpio_irq_handler, // The pointer to the handler function below
IRQF_TRIGGER_RISING, // Interrupt on rising edge (button press, not release)
"ebb_gpio_handler", // Used in /proc/interrupts to identify the owner
NULL); // The *dev_id for shared interrupt lines, NULL is okay
Link to the complete code.
For whom is not trying to create a GPIO driver but still need to get Linux virtual IRQ from HW IRQ, there is a specific API for platform drivers. You can register a platform driver and then, during the probing, call
/**
* platform_get_irq - get an IRQ for a device
* #dev: platform device
* #num: IRQ number index
*
* Gets an IRQ for a platform device and prints an error message if finding the
* IRQ fails. Device drivers should check the return value for errors so as to
* not pass a negative integer value to the request_irq() APIs.
*
* Return: non-zero IRQ number on success, negative error number on failure.
*/
int platform_get_irq(struct platform_device *dev, unsigned int num);
Detailed explanation
To reach your goal, you have a lot of different options:
GPIO Driver Interface (GPIO drivers providing IRQs)
Low-level OF API for Device Trees (of_irq_get)
Device drivers' infrastructure API (platform_get_irq)
GPIO Driver Interface
If your platform has a programmable GPIO, you can use the GPIO Driver Interface. See #yashC reply. In your specific case, given that your device is part of GPIO, you should go for this approach.
Low-level Device Trees APIs
If you want to interact directly with the device tree, you can use this solution. Imho, you should follow this approach only if you are writing a specific (and non-generic) driver and you need a "dirty and clean" way to go.
static const struct of_device_id qoriq_gpio_match_table[] =
{
{ .compatible = "fsl,qoriq-gpio" },
{ }
};
np = of_find_matching_node(NULL, qoriq_gpio_match_table);
if (!np)
{
pr_err("No device tree node for qoriq-gpio\n");
return -ENODEV;
}
// decode a node's IRQ and return it as a Linux IRQ number
irq_num = of_irq_get(np, 0);
// request_irq(...)
Device drivers' infrastructure API
Basically, you have to register a platform driver.
static const struct of_device_id qoriq_gpio_match_table[] =
{
{ .compatible = "fsl,qoriq-gpio" },
{ }
};
static struct platform_driver qoriq_gpi_driver = {
.driver = {
.name = "qoriq-gpio",
.of_match_table = qoriq_gpio_match_table
},
.probe = qoriq_gpio_probe
};
static int qoriq_gpio_probe(struct platform_device *pdev)
{
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
// request_irq(...)
}
Until Kernel v5.19 you were able to use also: platform_get_resource(pdev, IORESOURCE_IRQ, 0); API that, currently, is no more available.
You should use this approach if your device is a bit more generic (eg. you are working with several boards with different DTs).

spidev cannot control the chip select signal

I use kernel 3.12.rc4 on an embedded linux device (olimex imx233 micro). My aim is to use /dev/spidev to be able to communicate with another spi device.
I edit arch/arm/boot/dts/imx23-olinuxino.dts as:
ssp1: ssp#80034000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx23-spi";
pinctrl-names = "default";
pinctrl-0 = <&spi2_pins_a>;
clock-frequency = <1000000>;
status = "okay";
spidev: spidev#0 {
compatible = "spidev";
spi-max-frequency = <1000000>;
reg = <1>;
};
};
arch/arm/boot/dts/imx23.dtsi: has this config
spi2_pins_a: spi2#0 {
reg = <0>;
fsl,pinmux-ids = <
0x0182 /* MX23_PAD_GPMI_WRN__SSP2_SCK */
0x0142 /* MX23_PAD_GPMI_RDY1__SSP2_CMD */
0x0002 /* MX23_PAD_GPMI_D00__SSP2_DATA0 */
0x0032 /* MX23_PAD_GPMI_D03__SSP2_DATA3 */
>;
fsl,drive-strength = <1>;
fsl,voltage = <1>;
fsl,pull-up = <1>;
};
Device binding looks correct. When I compile the kernel I get the /dev/spidev1.1. After that I use spidev_test.c and monitor the pins by an oscilloscope. The SCK and MOSI output signals correctly, however, the chipselect is set to the logic high even during the data transfer.
Is there any way to determine why spidev cannot set to logic low during the transmission? It seems like either additional things needs to be passed on kernel or there is an issue on spidev that cannot control the chip select . I wonder if I need to change anything on the spidev.h or spidev.c on the driver/spi directory of the kernel? or how can I solve it?
The reference manual for the processor
I never used device tree, but I try to help you anyway.
The kernel create the device /dev/spidev1.1, so spidev is connected to SPI bus 1, chip select 1. The chip select numeration start from 0, and you do not have any other device associated to SPI bus 1.
As far as I know reg = <1> tell to the SPI core that spidev is connected to chip select 1., but maybe your device is connected to the chip select 0. So, reg = <0>

Resources