How are am335x GPIOs numbered in device tree? - linux-device-driver

I am trying to use a driver with a gpio interrupt on BeagleboneBlack. My device tree has the following entry for my custom device:
&i2c1{...
mydevice: mydevice#0c {
compatible = "mydevice,mydeice";
reg = <0x0c>;
mag_irq_gpio = <&gpio1 13 0>; /* INT line */
};
...}
Its driver counterpart has this:
static int parse_dt(struct i2c_client *client)
{
struct device_node *node = client->dev.of_node;
struct mydev_data *data = i2c_get_clientdata(client);
return of_property_read_u32(node, "mag_irq_gpio", &data->gpio);
}
The driver loads and works fine, except the gpio number is completely wrong. The property read function returns success, and reads 8 as the gpio number, even if I put a different number to the device tree.
How am I supposed to pass a gpio number as generic data? The interrupt works if I manually override the gpio number inside my driver.

As per comment by #sawdust
<&gpio1 13 0>
denotes an array of three values. I solved the issue by manually calculating the GPIO number and passing it as a single number:
<14>

Related

Emonlib+ADS1115+ESP32+Arduino Function Pointer

I have tried to contact the original designer for the ADS1115->EmonLib adaptation, but unfortunately his Github page has no contact information. I have also tried asking on the OpenEnergyMonitor forum and I get "Contact the person who made the changes".
So, I've come here in the hopes that the knowledgeable folk here can help me out.
Situation:
I have an ESP32 that I'm going to be using to monitor my energy consumption of my home. Because I will be monitoring several circuits I need lots of sensors. The ESP32 only has a 12-bit ADC and I'm limited to only a few sensors. Also limits future expansion if needed.
So here comes the ADS1115, which is a 16-bit ADC, but it uses I2C. Unfortunately, the EmonLib was designed to use onboard ADC, and not through an I2C device. So after doing some digging around, I found this modification to allow the ADS1115 to be used:
https://github.com/PaulWieland/EmonLib/
However, Paul Wieland is not able to be contacted through github.
Ok, so if I wanted to use only 1 ADS1115, I could just use his code as stock, which is this:
// In this example we will use an ADS1115 breakout board instead of the Arduino's local analog inputs
// This is especially useful for nodemcu/esp8266 users who only have a single analog input
#include <Wire.h>
#include <Adafruit_ADS1015.h>
// EmonLibrary examples openenergymonitor.org, Licence GNU GPL V3
#include <EmonLib_I2C.h> // Include Emon Library
EnergyMonitor emon1; // Create an instance
Adafruit_ADS1115 ads; // Create an instance of the ADS1115 object
// Make a callback method for reading the pin value from the ADS instance
int ads1115PinReader(int _pin){
return ads.readADC_SingleEnded(_pin);
}
void setup()
{
emon1.inputPinReader = ads1115PinReader; // Replace the default pin reader with the customized ads pin reader
emon1.current(1, 111.1); // Current: input pin, calibration.
}
void loop()
{
double Irms = emon1.calcIrms(1480); // Calculate Irms only
Serial.print(Irms*230.0); // Apparent power
Serial.print(" ");
Serial.println(Irms); // Irms
}
This compiles just fine. Though I am kinda confused as to how to select the "pin" of the ADS.
In the emonlib.cpp he added this code:
//--------------------------------------------------------------------------------------
// Constructor. Set the pinReader to the default pin reader method
//--------------------------------------------------------------------------------------
EnergyMonitor::EnergyMonitor()
{
this->inputPinReader = defaultInputPinReader;
}
//--------------------------------------------------------------------------------------
// By default we just call Arduino's analogRead
//--------------------------------------------------------------------------------------
int EnergyMonitor::defaultInputPinReader(int _pin)
{
return analogRead(_pin);
}
And changed this:
startV = analogRead(inPinV);
to:
startV = (this->inputPinReader)(inPinV);
and a few other spots where he made the adjustment using "this->".
And in the emonlib.h he added this to the Public section:
EnergyMonitor();
typedef int (*inputPinReaderMethod) (int _pin);
inputPinReaderMethod inputPinReader;
static int defaultInputPinReader(int _pin);
Now, for a single ADS1115, I think this works. At least the code compiles. But I need to use 4 ADS1115's as I'll need to be able to monitor up to 16 circuits. I can't for the life of me figure out how to add an array of ADS's to an array of emonlib's.
I tried doing this:
EnergyMonitor emon1[4]; // Create an instance
Adafruit_ADS1115 ads[4]; // Create an instance of the ADS1115 object
// Make a callback method for reading the pin value from the ADS instance
int ads1115PinReader(int _channel, int _pin){
return ads[1].readADC_SingleEnded(_pin);
}
void setup()
{
emon1[1].inputPinReader = ads1115PinReader; // Replace the default pin reader with the customized ads pin reader
emon1[1].current(1, 111.1); // Current: input pin, calibration.
}
void loop()
{
double Irms = emon1[1].calcIrms(1480); // Calculate Irms only
Serial.print(Irms*230.0); // Apparent power
Serial.print(" ");
Serial.println(Irms); // Irms
}
but then I get this error:
Emon_ADS_Test:18:27: error: invalid conversion from 'int (*)(int, int)' to 'EnergyMonitor::inputPinReaderMethod {aka int (*)(int)}' [-fpermissive]
emon1[1].inputPinReader = ads1115PinReader; // Replace the default pin reader with the customized ads pin reader
I'm not very advanced in coding, and function pointers just confuse the heck outta me.
So my dilemma is I have several ADS1115's I need to match to the several instances of the EmonLib.
There has to be a separate instance for each wire monitored. On my working model that uses an Arduino Nano I have 2 emonlib variables (emon1, emon2). This works fine as it uses the internal ADC, tho I've found out over time that it is not very accurate and not fast enough or high enough resolution for my needs. (ie. when 0-current is flowing, it shows 50W usage).
What do I need to make a change to make this thing work?
Kori
[UPDATE 1]
Ok, after some testing, I do still get this:
emon[i * j].inputPinReader = ads1115PinReader;
emon[i * j].current(j++, 111.1);
Unfortunately this throws an error still:
Emon_ADS_Test:21:33: error: invalid conversion from 'int (*)(int, int)' to 'EnergyMonitor::inputPinReaderMethod {aka int (*)(int)}' [-fpermissive]
emon1[i * j].inputPinReader = ads1115PinReader;
However, it comes to the function pointer as being a problem.
I use this code:
int ads1115PinReader(int _pin){
return ads.readADC_SingleEnded(_pin);
}
It works just fine. However if I do this:
int ads1115PinReader(int _channel, int _pin){
return ads[_channel].readADC_SingleEnded(_pin);
}
I get the error above. In the EmonLib_I2C library, the only thing I can think of is in this part:
typedef int (*inputPinReaderMethod) (int _pin);
inputPinReaderMethod inputPinReader;
static int defaultInputPinReader(int _pin);
not matching up. But even that doesn't make sense. In this line:
emon.inputPinReader = ads1115PinReader; // Replace the default pin reader with the customized ads pin reader
does that mean that when I call inputPinReader, it "actually" grabs the value from ads1115PinReader instead of the one in the library?

Unable to use PC15 as GPIO input on stm32f030rc

I'm working on a project using an stm32f030rc. I need to use PC15 as a GPIO input but it appears I'm unable to.
I understand the couple PC14/PC15 is shared with the LFE oscillator, but of course I'm not using that function. Moreover, I am able to read the correct pin level on the PC14 GPIO. In the datasheed regarding my model the PC15 pin is marked as a I/O with OSC32_OUT as additional function: can it be used as input at all?
For reference, this is the C code I'm using to test the functionality; I'm using libopencm3 for initialization.
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
static void clock_setup(void)
{
rcc_clock_setup_in_hsi_out_48mhz();
/* Enable GPIOA, GPIOB, GPIOC clock. */
rcc_periph_clock_enable(RCC_GPIOA);
rcc_periph_clock_enable(RCC_GPIOB);
rcc_periph_clock_enable(RCC_GPIOC);
rcc_periph_clock_enable(RCC_DBGMCU);
/* Enable clocks for GPIO port B and C*/
gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO5);
gpio_mode_setup(GPIOC, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO15);
gpio_mode_setup(GPIOC, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO14);
}
int main(void)
{
unsigned long long i = 0;
clock_setup();
/* Blink the LED (PA8) on the board with every transmitted byte. */
while (1)
{
gpio_toggle(GPIOA, GPIO5); /* LED on/off */
for (i = 0; i < 400000; i++) /* Wait a bit. */
__asm__("nop");
// This conditional is never entered
if (gpio_get(GPIOC, GPIO15) == 0) {
__asm__("nop");
__asm__("nop");
__asm__("nop");
}
// This one works
if (gpio_get(GPIOC, GPIO14) == 0) {
__asm__("nop");
__asm__("nop");
__asm__("nop");
}
}
return 0;
}
PC14 & PC15 have the same configuration properties. Of course, there are some limitations regarding using these pins as outputs (including PC13), but it should be okay to use them as inputs as long as you don't activate LSE functionality.
PC14 & PC15 are GPIO inputs after power-up and considering that LSE is disabled by default, you should be able to use them directly even without any configuration.
As you don't have any problems with PC14, I suspect 3 possible causes:
1) A bug in the GPIO code the library provides. Although it's very unlikely, it's easy to test. You can remove the configuration code for PC14 & PC15, as they are GPIO inputs after power-up by default. This eliminates the possibility of having a bug in gpio_mode_setup() function. To avoid using gpio_get() function, you can use the following code:
if (GPIOC->IDR & (1 << 15) == 0)
2) A bug in the clock config code the library provides. Again, this one is very unlikely, but you can test it by removing the rcc_clock_setup_in_hsi_out_48mhz() function. MCU uses HSI running at 8 MHz after power-up.
3) This can be a hardware problem. I suggest checking the voltage on PC15. Test it by physically connecting it to GND. Also measure PC14 for comparison. Among these 3 possible causes I can think of, this is the most probable one.

STM32F7 + FatFs = FR_NOT_READY

I am now using CubeMx 4.23.0 and FW package for STM32F7 1.8.0
MCU is STM32F746 on Core746i board.
Everything is generated by CubeMx automatically.
main.c:
SCB_EnableICache();
SCB_EnableDCache();
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SDMMC1_SD_Init();
MX_USB_DEVICE_Init();
MX_FATFS_Init();
HAL_Delay(3000);
DebugString("start OK");
uint8_t res = 0;
FATFS SDFatFs;
FIL MyFile; /* File object */
char SD_Path[4];
res = f_mount(&SDFatFs, (TCHAR const*)SD_Path, 0);
sprintf(DebugStr, "f_mount = 0x%02X", res);
DebugString(DebugStr);
res = f_open(&MyFile, "test.txt", FA_READ);
sprintf(DebugStr, "f_open = 0x%02X", res);
DebugString(DebugStr);
sdmmc.c:
void MX_SDMMC1_SD_Init(void)
{
hsd1.Instance = SDMMC1;
hsd1.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING;
hsd1.Init.ClockBypass = SDMMC_CLOCK_BYPASS_DISABLE;
hsd1.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE;
hsd1.Init.BusWide = SDMMC_BUS_WIDE_1B;
hsd1.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE;
hsd1.Init.ClockDiv = 7;
//HAL_SD_Init(&hsd1);
// ^^^^^ I also tried this here
//HAL_SD_ConfigWideBusOperation(&hsd1, SDMMC_BUS_WIDE_4B)
//^^^^ and this
}
In case of f_mount(&SDFatFs, (TCHAR const*)SD_Path, 0) <- with 1 here (forced mount), output is:
f_mount = 0x03
f_open = 0x01
With 0 (do not mount now) output is:
f_mount = 0x00
f_open = 0x03
0x03 value is FR_NOT_READY, but official info is pretty vague about it
Things I've tried:
Adding HAL_SD_Init(&hsd1) to MX_SDMMC1_SD_Init() since i didnt find where is SD card GPIO init happening.
2 GB noname SD card, 1 GB Transcend card.
Different hsd1.Init.ClockDiv 3 to 255.
Resoldering everything completely.
Switching to 4-bit wide bus using HAL_SD_ConfigWideBusOperation(&hsd1, SDMMC_BUS_WIDE_4B);
Turn on and off pullups.
But card still does not mount. It's formatted in FAT, working on a PC, files i've tried to open are exist, but empty.
How to get it to mount?
Thanks in advance!
there was the problem with exact version of cubemx.
updating stm32cubemx helped.
You can try
`f_mount(0, "path", 0);
` after the f_open call . it may work.
If the function with forced mounting failed with FR_NOT_READY, it means that the filesystem object has been registered successfully but the
volume is currently not ready to work
. The volume mount process will be attempted on subsequent file/directroy function.
If implementation of the disk I/O layer lacks asynchronous media change detection, application program needs to perform f_mount function after each media change to force cleared the filesystem object.
Changing all SDIO pins except SDIO_CK to pull-up according to This Topic works for me
Try commenting MX_USB_DEVICE_Init(), see what happens.

As to the GPIOTE function(External Interrupt) of nRF52832

I have some trouble to control GPIOTE function with nRF52832 sdk,
when using 14.01 version(SDK), it seems that the GPIOTE function can't be used with BLE function, I used the code below, it made hang-up issue of system, why?
I wonder whether GPIOTE function can't be used with BLE function or not,
and another method to use the function with BLE function,
thankful for your support and kindness in advance,
#define PIN_IN BUTTON_4
//#define PIN_OUT BSP_LED_3
void in_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
printf("love %d: %d\n", (int)pin, (int)action);
// nrf_drv_gpiote_out_toggle(PIN_OUT);
}
/**
* #brief Function for configuring: PIN_IN pin for input, PIN_OUT pin for output,
* and configures GPIOTE to give an interrupt on pin change.
*/
void gpio_external_int_init(void)//love_1108
{
uint32_t err_code;
err_code = nrf_drv_ppi_init();
APP_ERROR_CHECK(err_code);
//
err_code = nrf_drv_gpiote_init();
APP_ERROR_CHECK(err_code);
//
// (void)nrf_drv_gpiote_init();
// nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_SIMPLE(false);
// err_code = nrf_drv_gpiote_out_init(PIN_OUT, &out_config);
// APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(false);
in_config.pull = NRF_GPIO_PIN_PULLUP;
err_code = nrf_drv_gpiote_in_init(PIN_IN, &in_config, in_pin_handler);
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_in_event_enable(PIN_IN, true);
}
While you don't provide much detail, such as what is meant by "used with BLE function", I have found an issue with the SDK example ble_app_template. In my case the cause was that the file bsp_btn_ble.c demands that there are more buttons than my board_custom.h defines. So the function startup_event_extract() wants to check the state of BTN_ID_WAKEUP_BOND_DELETE, which does not exist on my hardware, causes an assertion. It is disturbing that BTN_ID_WAKEUP_BOND_DELETE and other buttons are defined in the c file, rather than being derived from custom_board.h.
So, trace the board initialization and you may find something like ASSERT(button_idx < BUTTONS_NUMBER), which caused a hang in my case.

How to find the device id of a STM32F103RET

I have been told that each STM32F103RET devices carry a unique device id.If so what is the procedure to retrieve the ID?I am running a system wherein I communicate with more than one STM borads. To differentiate each STM board I can use this unique Id.
thanks
You can find this information in STM32F103 Reference Manual. More specifically, you want to read chapter 30.2 Unique device ID register (96 bits).
So your device ID contained in read-only register, called U_ID. This register address is 0x1ffff7e8. It's 96 bits long, so it can be read (for example) using 3 read operations, each 32-bit long. Of course you can't hold it in one variable. So you should come up with some way of storing it in memory. It can be array, or structure, etc.
I would probably use structure for this purpose, using offsets described in Reference Manual:
#include <stdint.h>
struct u_id {
uint16_t off0;
uint16_t off2;
uint32_t off4;
uint32_t off8;
};
How to read it is up to you (depends on which framework you are using, e.g. in libopencm3 you have MMIO32 macro for this). In general case I would do something like that:
#define MMIO16(addr) (*(volatile uint16_t *)(addr))
#define MMIO32(addr) (*(volatile uint32_t *)(addr))
#define U_ID 0x1ffff7e8
/* Read U_ID register */
void uid_read(struct u_id *id)
{
id->off0 = MMIO16(U_ID + 0x0);
id->off2 = MMIO16(U_ID + 0x2);
id->off4 = MMIO32(U_ID + 0x4);
id->off8 = MMIO32(U_ID + 0x8);
}
You will also need a function to compare two IDs. You can either use memcmp() or some custom function for that matter. I'd prefer a custom one in this case:
#include <stdbool.h>
/* Returns true if IDs are the same */
bool uid_cmp(struct u_id *id1, struct u_id *id2)
{
return id1->off0 == id2->off0 &&
id1->off2 == id2->off2 &&
id1->off4 == id2->off4 &&
id1->off8 == id2->off8;
}
You can use it like that:
int main(void)
{
struct u_id id1 = { 0x0, 0x1, 0x2, 0x3 };
struct u_id id2;
bool same_id;
uid_read(&id2);
same_id = uid_cmp(&id1, &id2);
printf("%s\n", same_id ? "equal" : "not equal");
return 0;
}
UPDATE
As I understand your situation:
Each of your boards should send some interrupt to your master board (more specifically, to GPIO pin on master board).
Once master board received interrupt, it initiates transfer (request) to board where interrupt came from (probably using I2C, SPI, UART or something like that to communicate between boards).
Then client board responds with it's device ID.
Interrupts are handled sequentially, so device IDs will be obtained one by one.
Picture below illustrates case with I2C bus used for ID transmission.
For board 1 you will have GPIO1 interrupt handler executed, for board 2 -- GPIO 2 interrupt handler. Hence you know which one to ask for device ID. So device ID will be obtained one at a time. From here you can figure out how to store those IDs. I propose to use regular array:
struct u_id device_ids[2];
Here is pseudo-code showing how to populate this array:
void gpio1_isr(void)
{
uint8_t i2c_buf[12];
i2c_get(i2c_buf);
memcpy(&device_ids[0], i2c_buf, 12);
}
void gpio2_isr(void)
{
uint8_t i2c_buf[12];
i2c_get(i2c_buf);
memcpy(&device_ids[1], i2c_buf, 12);
}
(isr stands for Interrupt Service Routine, which is the same as interrupt handler).
In case you are sure that your two client boards are ready for I2C transfer in a time when master board is gonna ask them for IDs, you can get rid of 2 GPIO lines and just use I2C transactions from master board to clients whenever you want.
Open a notepad and write this:
openocd -f interface/jlink.cfg -f board/olimex_stm32_h103.cfg -c "init" -c "stm32f1x.cpu mdw 0x1FFFF7E8 3" -c "exit"
#pause
Then save it as ".bat" file then run it!