Implementing AD7606 driver for Raspberry Pi 4b - linux-device-driver

I am trying to implement ADC AD7606 device driver for Linux on Raspberry Pi 4B+ to make possible to read data from all channels as fast as it possible via SPI. The kernel version is v5.10.83 and the kernel was compiled and installed with support both AD7606 and AD7606_SPI as modules (make menuconfig).
The device tree overlay was created by using example on Analog Devices https://wiki.analog.com/resources/tools-software/linux-drivers/iio-adc/ad7606 and now it is like this:
/dts-v1/;
/plugin/;
#include <../include/dt-bindings/gpio/gpio.h>
#include <../include/dt-bindings/interrupt-controller/irq.h>
/ {
compatible = "brcm,bcm2835";
fragment#0 {
target = <&spi0>;
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;
adc0: adc#0 {
compatible = "adi,ad7606-8";
reg = <0>;
spi-max-frequency = <1000000>;
spi-cpol;
spi-cpha;
interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
interrupt-parent = <&gpio>;
adi,conversion-start-gpios = <&gpio 17 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio 27 GPIO_ACTIVE_HIGH>;
adi,first-data-gpios = <&gpio 22 GPIO_ACTIVE_HIGH>;
adi,oversampling-ratio-gpios = <&gpio 18 GPIO_ACTIVE_HIGH>,
<&gpio 23 GPIO_ACTIVE_HIGH>,
<&gpio 26 GPIO_ACTIVE_HIGH>;
standby-gpios = <&gpio 24 GPIO_ACTIVE_LOW>;
adi,sw-mode;
};
};
};
};
The overlay is activated in /boot/config.txt and reverted DT section for SPI is like this:
spi#7e204000 {
compatible = "brcm,bcm2835-spi";
clocks = < 0x08 0x14 >;
status = "okay";
#address-cells = < 0x01 >;
interrupts = < 0x00 0x76 0x04 >;
cs-gpios = < 0x07 0x08 0x01 0x07 0x07 0x01 >;
#size-cells = < 0x00 >;
dma-names = "tx\0rx";
phandle = < 0x33 >;
reg = < 0x7e204000 0x200 >;
pinctrl-0 = < 0x0e 0x0f >;
dmas = < 0x0c 0x06 0x0c 0x07 >;
pinctrl-names = "default";
adc#0 {
spi-cpol;
compatible = "adi,ad7606-8";
adi,conversion-start-gpios = < 0x07 0x11 0x00 >;
spi-cpha;
adi,first-data-gpios = < 0x07 0x16 0x00 >;
adi,oversampling-ratio-gpios = < 0x07 0x12 0x00 0x07 0x17 0x00 0x07 0x1a 0x00 >;
interrupt-parent = < 0x07 >;
interrupts = < 0x19 0x02 >;
reset-gpios = < 0x07 0x1b 0x00 >;
phandle = < 0xe9 >;
standby-gpios = < 0x07 0x18 0x01 >;
reg = < 0x00 >;
adi,sw-mode;
spi-max-frequency = < 0xf4240 >;
};
spidev#1 {
compatible = "spidev";
#address-cells = < 0x01 >;
#size-cells = < 0x00 >;
phandle = < 0xb0 >;
reg = < 0x01 >;
spi-max-frequency = < 0x7735940 >;
};
spidev#0 {
compatible = "spidev";
#address-cells = < 0x01 >;
#size-cells = < 0x00 >;
phandle = < 0xaf >;
reg = < 0x00 >;
spi-max-frequency = < 0x7735940 >;
};
};
During kernel boot with activated this overlay I have kernel message:
[ 5.171792] spi-bcm2835 fe204000.spi: chipselect 0 already in use
[ 5.171827] spi_master spi0: spi_device register error /soc/spi#7e204000/spidev#0
[ 5.171861] spi_master spi0: Failed to create SPI device for /soc/spi#7e204000/spidev#0
How to resolve this problem or make troubleshooting?

Related

STM32L4 I2C LED Driver

I am trying to control LEDs using IS31FL3236A LED driver from LUMISSIL Microsystem which communicates with I2C.
LED driver datasheet
I use the HAL communication protocol of the STM32, I am able to turn on all the LEDs but I don't understand why I can't control the LEDs one by one or choose the intensity of the LEDs.
Here is my code.
IS31FL3236A.h to configure the address of all channels and the device address.
#define Single 0
#define RGB 1
#define Addr_GND 0x78 // P7 datasheet 0x78 = 1111000 en bit (addr 1)
#define PWM 0x04
#define All_EN 0x4A
#define IS3236_REG_PWM_OUT1 0x01 //LED PWM
#define IS3236_REG_PWM_OUT2 0x02
#define IS3236_REG_PWM_OUT3 0x03
#define IS3236_REG_PWM_OUT4 0x04
#define IS3236_REG_PWM_OUT5 0x05
#define IS3236_REG_PWM_OUT6 0x06
#define IS3236_REG_PWM_OUT7 0x07
#define IS3236_REG_PWM_OUT8 0x08
#define IS3236_REG_PWM_OUT9 0x09
#define IS3236_REG_PWM_OUT10 0x0A
#define IS3236_REG_PWM_OUT11 0x0B
#define IS3236_REG_PWM_OUT12 0x0C
#define IS3236_REG_PWM_OUT13 0x0D
#define IS3236_REG_PWM_OUT14 0x0E
#define IS3236_REG_PWM_OUT15 0x0F
#define IS3236_REG_PWM_OUT16 0x10
#define IS3236_REG_PWM_OUT17 0x11
#define IS3236_REG_PWM_OUT18 0x12
#define IS3236_REG_PWM_OUT19 0x13
#define IS3236_REG_PWM_OUT20 0x14
#define IS3236_REG_PWM_OUT21 0x15
#define IS3236_REG_PWM_OUT22 0x16
#define IS3236_REG_PWM_OUT23 0x17
#define IS3236_REG_PWM_OUT24 0x18
#define IS3236_REG_PWM_OUT25 0x19
#define IS3236_REG_PWM_OUT26 0x1A
#define IS3236_REG_PWM_OUT27 0x1B
#define IS3236_REG_PWM_OUT28 0x1C
#define IS3236_REG_PWM_OUT29 0x1D
#define IS3236_REG_PWM_OUT30 0x1E
#define IS3236_REG_PWM_OUT31 0x1F
#define IS3236_REG_PWM_OUT32 0x20
#define IS3236_REG_PWM_OUT33 0x21
#define IS3236_REG_PWM_OUT34 0x22
#define IS3236_REG_PWM_OUT35 0x23
#define IS3236_REG_PWM_OUT36 0x24
#define IS3236_REG_CTRL_OUT1 0x26 //LED ONOFF
#define IS3236_REG_CTRL_OUT2 0x27
#define IS3236_REG_CTRL_OUT3 0x28
#define IS3236_REG_CTRL_OUT4 0x29
#define IS3236_REG_CTRL_OUT5 0x2A
#define IS3236_REG_CTRL_OUT6 0x2B
#define IS3236_REG_CTRL_OUT7 0x2C
#define IS3236_REG_CTRL_OUT8 0x2D
#define IS3236_REG_CTRL_OUT9 0x2E
#define IS3236_REG_CTRL_OUT10 0x2F
#define IS3236_REG_CTRL_OUT11 0x30
#define IS3236_REG_CTRL_OUT12 0x31
#define IS3236_REG_CTRL_OUT13 0x32
#define IS3236_REG_CTRL_OUT14 0x33
#define IS3236_REG_CTRL_OUT15 0x34
#define IS3236_REG_CTRL_OUT16 0x35
#define IS3236_REG_CTRL_OUT17 0x36
#define IS3236_REG_CTRL_OUT18 0x37
#define IS3236_REG_CTRL_OUT19 0x38
#define IS3236_REG_CTRL_OUT20 0x39
#define IS3236_REG_CTRL_OUT21 0x3A
#define IS3236_REG_CTRL_OUT22 0x3B
#define IS3236_REG_CTRL_OUT23 0x3C
#define IS3236_REG_CTRL_OUT24 0x3D
#define IS3236_REG_CTRL_OUT25 0x3E
#define IS3236_REG_CTRL_OUT26 0x3F
#define IS3236_REG_CTRL_OUT27 0x40
#define IS3236_REG_CTRL_OUT28 0x41
#define IS3236_REG_CTRL_OUT29 0x42
#define IS3236_REG_CTRL_OUT30 0x43
#define IS3236_REG_CTRL_OUT31 0x44
#define IS3236_REG_CTRL_OUT32 0x45
#define IS3236_REG_CTRL_OUT33 0x46
#define IS3236_REG_CTRL_OUT34 0x47
#define IS3236_REG_CTRL_OUT35 0x48
#define IS3236_REG_CTRL_OUT36 0x49
#define Imax 0x11
#define update 0x25
#define freq 0x4B
#define mode 0x01
main.c to turn on all LEDs
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
//I2C_GPIO_Init();
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
MX_USART2_UART_Init();
MX_TIM1_Init();
/* USER CODE BEGIN 2 */
//I2C_GPIO_Init();
IS31FL3236A_Init();
static const uint16_t LED19 = 0x13;
static const uint16_t LED19PWM = 0x38;
static const uint16_t deviceAddr = 0x78;
static const uint16_t Update = 0x25;
static const uint16_t Freq = 0x4B;
static const uint16_t intensityPWM = 0x02;
static const uint16_t CurrentMax = 0x00;
static const uint16_t Mode = 0x01;
uint8_t pwm[bufSize];
uint8_t current[bufSize];
uint8_t mode1[bufSize];
pwm[0] = 0x7B;
current[0] = 0x00;
mode1[0] = 0x01;
uint8_t i=0;
/*for(i=0;i<=0x49;i++){
HAL_I2C_Mem_Write(&hi2c1, Addr_GND, i, I2C_MEMADD_SIZE_16BIT, (uint8_t*)PWM, 1, HAL_MAX_DELAY);
}
for(i=0;i<=0x24;i++){
HAL_I2C_Mem_Write(&hi2c1, Addr_GND, i, sizeof(i), (uint8_t*)Imax, sizeof(Imax), 10);
}*/
HAL_I2C_Mem_Write(&hi2c1, deviceAddr, LED19PWM, I2C_MEMADD_SIZE_8BIT, pwm, bufSize, 10);
HAL_I2C_Mem_Write(&hi2c1, deviceAddr, LED19, I2C_MEMADD_SIZE_8BIT, current, bufSize, 10);
HAL_I2C_Mem_Write(&hi2c1, deviceAddr, Freq, I2C_MEMADD_SIZE_8BIT, current, bufSize, 10);
HAL_I2C_Mem_Write(&hi2c1, deviceAddr, Mode, I2C_MEMADD_SIZE_8BIT, current, bufSize, 10);
HAL_I2C_Mem_Write(&hi2c1, deviceAddr, Update, I2C_MEMADD_SIZE_8BIT, mode1, bufSize, 10);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/*uint8_t i=0;
for(i=0;i<=0x49;i++){
HAL_I2C_Mem_Write(&hi2c1, Addr_GND, i, sizeof(i), (uint8_t*)PWM, sizeof(PWM), 10);
}
for(i=0;i<=0x24;i++){
HAL_I2C_Mem_Write(&hi2c1, Addr_GND, i, sizeof(i), (uint8_t*)Imax, sizeof(Imax), 10);
}
HAL_I2C_Mem_Write(&hi2c1, Addr_GND, freq, sizeof(freq), (uint8_t*)Imax, sizeof(Imax), 10);
HAL_I2C_Mem_Write(&hi2c1, Addr_GND, mode, sizeof(mode), (uint8_t*)Imax, sizeof(Imax), 10);
HAL_I2C_Mem_Write(&hi2c1, Addr_GND, RESET, sizeof(RESET), (uint8_t*)mode, sizeof(mode), 10);
HAL_I2C_Mem_Write(&hi2c1, Addr_GND, update, sizeof(update), (uint8_t*)mode, sizeof(mode), 10);*/
//Swith();
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
To select LED number 19 for example, I just removed the two for loops and added this but the LED does not light up.
HAL_I2C_Mem_Write(&hi2c1, GND, IS3236_REG_PWM_OUT19, sizeof(IS3236_REG_PWM_OUT19), (uint8_t*)PWM, sizeof(PWM), 10);
HAL_I2C_Mem_Write(&hi2c1, GND, IS3236_REG_CTRL_OUT19, sizeof(IS3236_REG_CTRL_OUT19), (uint8_t*)Imax, sizeof(Imax), 10);
HAL_I2C_Mem_Write(&hi2c1, GND, update, sizeof(update), (uint8_t*)mode, sizeof(mode), 10);
I2C configuration
/**
* #brief I2C1 Initialization Function
* #param None
* #retval None
*/
static void MX_I2C1_Init(void)
{
/* USER CODE BEGIN I2C1_Init 0 */
/* USER CODE END I2C1_Init 0 */
/* USER CODE BEGIN I2C1_Init 1 */
/* USER CODE END I2C1_Init 1 */
hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x00702991;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}
/** Configure Analogue filter
*/
if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
{
Error_Handler();
}
/** Configure Digital filter
*/
if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN I2C1_Init 2 */
/* USER CODE END I2C1_Init 2 */
}
stm32l4xx_hal_msp.c
/**
* #brief I2C MSP Initialization
* This function configures the hardware resources used in this example
* #param hi2c: I2C handle pointer
* #retval None
*/
void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(hi2c->Instance==I2C1)
{
/* USER CODE BEGIN I2C1_MspInit 0 */
/* USER CODE END I2C1_MspInit 0 */
__HAL_RCC_GPIOA_CLK_ENABLE();
/**I2C1 GPIO Configuration
PA9 ------> I2C1_SCL
PA10 ------> I2C1_SDA
*/
GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* Peripheral clock enable */
__HAL_RCC_I2C1_CLK_ENABLE();
/* USER CODE BEGIN I2C1_MspInit 1 */
/* USER CODE END I2C1_MspInit 1 */
}
}
/**
* #brief I2C MSP De-Initialization
* This function freeze the hardware resources used in this example
* #param hi2c: I2C handle pointer
* #retval None
*/
void HAL_I2C_MspDeInit(I2C_HandleTypeDef* hi2c)
{
if(hi2c->Instance==I2C1)
{
/* USER CODE BEGIN I2C1_MspDeInit 0 */
/* USER CODE END I2C1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_I2C1_CLK_DISABLE();
/**I2C1 GPIO Configuration
PA9 ------> I2C1_SCL
PA10 ------> I2C1_SDA
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9);
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_10);
/* USER CODE BEGIN I2C1_MspDeInit 1 */
/* USER CODE END I2C1_MspDeInit 1 */
}
}
Here is what STM32 expects
HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c,
uint16_t DevAddress,
uint16_t MemAddress,
uint16_t MemAddSize,
uint8_t *pData,
uint16_t Size,
uint32_t Timeout);
Here is what you give:
HAL_I2C_Mem_Write(&hi2c1, GND, IS3236_REG_PWM_OUT19, sizeof(IS3236_REG_PWM_OUT19), (uint8_t*)PWM, sizeof(PWM), 10);
HAL_I2C_Mem_Write(&hi2c1, GND, IS3236_REG_CTRL_OUT19, sizeof(IS3236_REG_CTRL_OUT19), (uint8_t*)Imax, sizeof(Imax), 10);
HAL_I2C_Mem_Write(&hi2c1, GND, update, sizeof(update), (uint8_t*)mode, sizeof(mode), 10);
Do not take a sizeof(#define)
Rebind register #defines into static const. Or #define the MemAddSize if desired
Don't use a pointer to a #define
Change your MemAddSize to either: I2C_MEMADD_SIZE_16BIT, I2C_MEMADD_SIZE_8BIT
My blind guess as to why the system is able to turn the leds all on or off and at max brightness is due to address auto increment.
Try using the I2C_MEM_WRITE() using magic numbers and local variables, buffers, and pointers. If this works then it is likely your usage of #defines within your driver file.
Check the return type of HAL_StatusTypeDef between each call. Make sure that it is OK.
Here are two example for stm32 i2c:
Example using mem_write()
Intro to stm32 i2c
------------ Reply 1 ------------
Dont use I2C_MEMADD_SIZE_16BIT for the Size param. You need to specify the num of of the buffer you are pointing to. Go to your local source code and look at the instructions for how to use I2C_mem_write(). Based on the data sheet, I believe you should also be using I2C_MEMADD_SIZE_8BIT instead of 16.
Suppose you have a buffer of size one. You want to only read one byte then.
#define bufSize 1
uint8_t myBuf[bufSize];
myBuf[0] = someDataVal;
HAL_I2C_Mem_Write(&hi2c1, deviceAddr, LED19, I2C_MEMADD_SIZE_8BIT, myBuf, bufSize , 10);
------------ Reply 2 ------------
Is bufSize defined? If so, what number is it?
You need to be assigning to the first index of each buf if you are only writing one byte from each buf. pwm[0], current[0] (not 1), mode1[0] (not 2).
uint8_t pwm[bufSize];
uint8_t current[bufSize];
uint8_t mode1[bufSize];
pwm[0] = 0x7B;
current[1] = 0x00;
mode1[2] = 0x01;
------------ Reply 3 ------------
// First lets discuss device address
// What is your AD pin connected to (PG 7)
// Currently you have it configured to 0x78. Bit shift it for STM32
// 0x78 << 1
uint8_t myBuf[1];
// Turn on the device by writing to register 0. Shutdown register
myBuf[0] = 0x01;
HAL_I2C_Mem_Write(&hi2c1, ((uint16_t) 0x78) << 1, 0x00, I2C_MEMADD_SIZE_8BIT, myBuf, 1, 10;
// Then lets assign the pwm to the first LED (OUT1). Register 1. Reuse prior buffer.
myBuf[0] = 0x80;
// Lets toggle LED 19
HAL_I2C_Mem_Write(&hi2c1, ((uint16_t) 0x78) << 1, 0x01, I2C_MEMADD_SIZE_8BIT, myBuf, 1, 10;
// Enable OUT1 via Control register 26
myBuf[0] = 0x01; // This would have maximum current settings (Pg. 9)
HAL_I2C_Mem_Write(&hi2c1, ((uint16_t) 0x78) << 1, 0x26, I2C_MEMADD_SIZE_8BIT, myBuf, 1, 10;
// Tell system to update the registers. Register 25
myBuf[0] = 0x00;
HAL_I2C_Mem_Write(&hi2c1, ((uint16_t) 0x78) << 1, 0x25, I2C_MEMADD_SIZE_8BIT, myBuf, 1, 10;
I always would encourage you to look at the HAL_STATUS of each of these returns. Can you tell me if it is OK?
The offset changes the device address, you should not do the offset, it works like this:
// First lets discuss device address
// What is your AD pin connected to (PG 7)
uint8_t myBuf[1];
// First lets turn on the device by writing to register 0. Shutdown register
myBuf[0] = 0x01;
HAL_I2C_Mem_Write(&hi2c1, ((uint16_t) 0x78), 0x00, I2C_MEMADD_SIZE_8BIT, myBuf, 1, 10);
// Then lets assign the pwm to the first LED (OUT1). Register 1. Reuse prior buffer.
myBuf[0] = 0xff;
// Lets toggle LED 19
HAL_I2C_Mem_Write(&hi2c1, ((uint16_t) 0x78), 0x13, I2C_MEMADD_SIZE_8BIT, myBuf, 1, 10);
// Enable OUT1 via Control register 26
myBuf[0] = 0x01; // This would have maximum current settings (Pg. 9)
HAL_I2C_Mem_Write(&hi2c1, ((uint16_t) 0x78), 0x38, I2C_MEMADD_SIZE_8BIT, myBuf, 1, 10);
// Tell system to update the registers. Register 25
myBuf[0] = 0x00;
HAL_I2C_Mem_Write(&hi2c1, ((uint16_t) 0x78), 0x25, I2C_MEMADD_SIZE_8BIT, myBuf, 1, 10);
Thanks for your help #Frebreeze now it works.

How to access DAO enumeration constants in PowerShell?

In PowerShell, how do you access DAO enumeration constants, such as RecordsetTypeEnum dbOpenTable (1) or DataTypeEnum adVarNumeric (139)? Right now, I'm simply putting them in as magic numbers, but it would be much cleaner and easier to read to refer to the proper constants.
You could define a custom enum type with the names needed:
enum DAODataType {
adBigInt = 0x14
adBinary = 0x80
adBoolean = 0x0B
adBSTR = 0x08
adChapter = 0x88
adChar = 0x81
adCurrency = 0x06
adDate = 0x07
adDBDate = 0x85
adDBTime = 0x86
adDBTimeStamp = 0x87
adDecimal = 0x0E
adDouble = 0x05
adEmpty = 0x00
adError = 0x0A
adFileTime = 0x40
adGUID = 0x48
adIDispatch = 0x09
adInteger = 0x03
adIUnknown = 0x0D
adLongVarBinary = 0xCD
adLongVarChar = 0xC9
adLongVarWChar = 0xCB
adNumeric = 0x83
adPropVariant = 0x8A
adSingle = 0x04
adSmallInt = 0x02
adTinyInt = 0x10
adUnsignedBigInt = 0x15
adUnsignedInt = 0x13
adUnsignedSmallInt = 0x12
adUnsignedTinyInt = 0x11
adUserDefined = 0x84
adVarBinary = 0xCC
adVarChar = 0xC8
adVariant = 0x0C
adVarNumeric = 0x8B
adVarWChar = 0xCA
adWChar = 0x82
}
Then pass [DAODataType]::adVarNumeric instead of 139
For RecordsetTypeEnum dbOpenTable (which comes from Access), you can use:
[Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Interop.Access.Dao") | Out-null
[int][Microsoft.Office.Interop.Access.Dao.RecordsetTypeEnum]::dbOpenTable
For DataTypeEnum adVarNumeric (which comes from ADODB, not Access) you can use:
[Reflection.Assembly]::LoadWithPartialName("ADODB") | Out-null
[int][ADODB.DataTypeEnum]::adVarNumeric

When approaching the gpio register address of RaspberryPi, why is the result different between unsigned int* and char*?

Using mmap(), I am going to write a value to the GPIO register address of the Raspberry Pi.
I thought the register value would have the same when reading mapped GPIO address in unsigned int * or char *, but it was not. I compared the results for both cases.
This is my code.
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#define GPIO_BASE 0x3F200000
#define GPFSEL1 0x04
#define GPSET0 0x1C
#define GPCLR0 0x28
int main()
{
int fd = open("/dev/mem", O_RDWR|O_SYNC);
// Error Handling
if (fd < 0) {
printf("Can't open /dev/mem \n");
exit(1);
}
// Map pages of memory
char *gpio_memory_map = (char*)mmap(0, 4096, PROT_READ|PROT_WRITE,
MAP_SHARED, fd, GPIO_BASE);
// Error Handling
if (gpio_memory_map == MAP_FAILED) {
printf("Error : mmap \n");
exit(-1);
}
// GPIO18
//volatile unsigned int *gpio = (volatile unsigned int*)gpio_memory_map;
//gpio[GPFSEL1/4] = (1<<24);
volatile char *gpio = (volatile char *)gpio_memory_map;
int i;
for (i = 0; i < 16; i++)
printf("gpio[%d](%#x) = %#0x\n", i, &gpio[i], gpio[i]);
/*
for (i = 0; i < 5; i++) {
gpio[GPCLR0 / 4] = (1 << 18);
sleep(1);
gpio[GPSET0 / 4] = (1 << 18);
sleep(1);
}
*/
// Unmap pages of memory
munmap(gpio_memory_map, 4096);
return 0;
}
And those below are the results.
volatile unsigned int *gpio = (volatile unsigned int *)gpio_memory_map;
gpio[0](0x76f12000) = 0x1
gpio[1](0x76f12004) = 0x1000000
gpio[2](0x76f12008) = 0
gpio[3](0x76f1200c) = 0x3fffffc0
gpio[4](0x76f12010) = 0x24000924
gpio[5](0x76f12014) = 0x924
gpio[6](0x76f12018) = 0
gpio[7](0x76f1201c) = 0x6770696f
gpio[8](0x76f12020) = 0x6770696f
gpio[9](0x76f12024) = 0x6770696f
gpio[10](0x76f12028) = 0x6770696f
gpio[11](0x76f1202c) = 0x6770696f
gpio[12](0x76f12030) = 0x6770696f
gpio[13](0x76f12034) = 0x2ffbbfff
gpio[14](0x76f12038) = 0x3ef4ff
gpio[15](0x76f1203c) = 0
volatile char *gpio = (volatile char *)gpio_memory_map;
As the result #1 above, I thought gpio[1], gpio[2], gpio[3] should be 0. But it was different. And even if I try to write a new value on gpio[1] or gpio[2] or gpio[3], it stays the same. Why are the results different when approaching char * and unsigned char *?
gpio[0](0x76f47000) = 0x1
gpio[1](0x76f47001) = 0x69
gpio[2](0x76f47002) = 0x70
gpio[3](0x76f47003) = 0x67
gpio[4](0x76f47004) = 0
gpio[5](0x76f47005) = 0x69
gpio[6](0x76f47006) = 0x70
gpio[7](0x76f47007) = 0x67
gpio[8](0x76f47008) = 0
gpio[9](0x76f47009) = 0x69
gpio[10](0x76f4700a) = 0x70
gpio[11](0x76f4700b) = 0x67
gpio[12](0x76f4700c) = 0xc0
gpio[13](0x76f4700d) = 0x69
gpio[14](0x76f4700e) = 0x70
gpio[15](0x76f4700f) = 0x67

How to read data from MPU6050 using STM32F4

I need to monitorate the acelleration of a object. I'm using the MPU6050(accelerometer and gyroscope) and the controller STM32F401RBT6. The code below is the solution that i'm using for this.
#define MPU6050_ADDR 0xD0
#define SMPLRT_DIV_REG 0x19
#define GYRO_CONFIG_REG 0x1B
#define ACCEL_CONFIG_REG 0x1C
#define ACCEL_XOUT_H_REG 0x3B
#define TEMP_OUT_H_REG 0x41
#define GYRO_XOUT_H_REG 0x43
#define PWR_MGMT_1_REG 0x6B
#define WHO_AM_I_REG 0X75
uint16_t Accel_X_RAW,Accel_Y_RAW,Accel_Z_RAW;
uint16_t Ax,Ay,Az;
char buffer[10];
void MPU6050_Init(void)
{
uint8_t check, data;
HAL_I2C_Mem_Read(&hi2c3,MPU6050_ADDR,WHO_AM_I_REG,1,&check,1,100);
if(check == 104)
{
data = 0x07;
HAL_I2C_Mem_Write(&hi2c3,MPU6050_ADDR,SMPLRT_DIV_REG,1,&data,1,50);
HAL_Delay(50);
data = 0x00;
HAL_I2C_Mem_Write(&hi2c3,MPU6050_ADDR,ACCEL_CONFIG_REG,1,&data,1,50);
HAL_Delay(50);
data = 0x00;
HAL_I2C_Mem_Write(&hi2c3,MPU6050_ADDR,GYRO_CONFIG_REG,1,&data,1,50);
HAL_Delay(50);
data = 0;
HAL_I2C_Mem_Write(&hi2c3,MPU6050_ADDR,PWR_MGMT_1_REG,1,&data,1,50);
HAL_Delay(50);
}
}
void MPU6050_Read_Accel(void)
{
uint8_t recData[6];
for(int i=0;i<6;i++) recData[i] = 0;
HAL_I2C_Mem_Read(&hi2c3,MPU6050_ADDR,ACCEL_XOUT_H_REG,I2C_MEMADD_SIZE_8BIT,recData,6,100);
HAL_Delay(50);
uint16_t dataConvert1,dataConvert2;
dataConvert1 = (uint16_t)(0x0000 | recData[0]) << 8;
dataConvert2 = (uint16_t)(0x0000 | recData[1]);
Accel_X_RAW = dataConvert1 | dataConvert2;
dataConvert1 = (uint16_t)(0x0000 | recData[2]) << 8;
dataConvert2 = (uint16_t)(0x0000 | recData[3]);
Accel_Y_RAW = dataConvert1 | dataConvert2;
dataConvert1 = (uint16_t)(0x0000 | recData[4]) << 8;
dataConvert2 = (uint16_t)(0x0000 | recData[5]);
Accel_Z_RAW = dataConvert1 | dataConvert2;
Ax = (uint16_t)(Accel_X_RAW / 16384);
Ay = (uint16_t)(Accel_Y_RAW / 16384);
Az = (uint16_t)(Accel_Z_RAW / 16384);
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_I2C3_Init();
MX_GPIO_Init();
MX_USB_DEVICE_Init();
MPU6050_Init();
while (1)
{
MPU6050_Read_Accel();
sprintf(buffer, "%d / ", Accel_X_RAW);
CDC_Transmit_FS((char*)buffer,10);
}
}
I already did it on ATMEL Controler (Arduino) and it worked, but not on STM32.
I am trying to read the value of X Axis and show it using the USB CDC. This code sets a value for the `` `Accel_X_RAW```` variable between 0 and 65535. In Arduino, the reference value was 32768 when the object was stopped, but reading with STM32 remains at the maximum value (65535) if don't have movement. I don't know what's wrong with this code, I tried many options, but it still doesn't work. Can you help me please.
According to the MPU6050 datasheet, the 16-bit values for acceleration and gyroscope are returned in the signed 2's complement form (it detects acceleration values in the range +-g). As you are receiving signed data in the unsigned variables, the result is not what you expect. Therefore, replace all uint16_t datatypes with int16_t.
The reason why you are getting 65535 value; the hex value of -1 in signed int16_t form is 0xFFFF. However, if you store it in the uint16_t variable, it will be read as 65535. I am assuming that the default acceleration value at rest is -1g.
#include <stdlib.h> /* For using memset */
#define MPU6050_ADDR 0xD0
#define SMPLRT_DIV_REG 0x19
#define GYRO_CONFIG_REG 0x1B
#define ACCEL_CONFIG_REG 0x1C
#define ACCEL_XOUT_H_REG 0x3B
#define TEMP_OUT_H_REG 0x41
#define GYRO_XOUT_H_REG 0x43
#define PWR_MGMT_1_REG 0x6B
#define WHO_AM_I_REG 0X75
int16_t Accel_X_RAW,Accel_Y_RAW,Accel_Z_RAW;
int16_t Ax,Ay,Az;
char buffer[10];
void MPU6050_Init(void)
{
uint8_t check, data;
HAL_I2C_Mem_Read(&hi2c3,MPU6050_ADDR,WHO_AM_I_REG,1,&check,1,100);
if(check == 104)
{
data = 0x07;
HAL_I2C_Mem_Write(&hi2c3,MPU6050_ADDR,SMPLRT_DIV_REG,1,&data,1,50);
HAL_Delay(50);
data = 0x00;
HAL_I2C_Mem_Write(&hi2c3,MPU6050_ADDR,ACCEL_CONFIG_REG,1,&data,1,50);
HAL_Delay(50);
data = 0x00;
HAL_I2C_Mem_Write(&hi2c3,MPU6050_ADDR,GYRO_CONFIG_REG,1,&data,1,50);
HAL_Delay(50);
data = 0;
HAL_I2C_Mem_Write(&hi2c3,MPU6050_ADDR,PWR_MGMT_1_REG,1,&data,1,50);
HAL_Delay(50);
}
}
void MPU6050_Read_Accel(void)
{
uint8_t recData[6];
//for(int i=0;i<6;i++) recData[i] = 0;
memset(recData, 0, sizeof(recData));
HAL_I2C_Mem_Read(&hi2c3,MPU6050_ADDR,ACCEL_XOUT_H_REG,I2C_MEMADD_SIZE_8BIT,recData,6,100);
HAL_Delay(50);
Accel_X_RAW = (int16_t)(recData[0] << 8 | recData[1]);
Accel_Y_RAW = (int16_t)(recData[2] << 8 | recData[3]);
Accel_Z_RAW = (int16_t)(recData[4] << 8 | recData[5]);
Ax = (int16_t)(Accel_X_RAW / 16384);
Ay = (int16_t)(Accel_Y_RAW / 16384);
Az = (int16_t)(Accel_Z_RAW / 16384);
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_I2C3_Init();
MX_GPIO_Init();
MX_USB_DEVICE_Init();
MPU6050_Init();
while (1)
{
MPU6050_Read_Accel();
sprintf(buffer, "%d / ", Accel_X_RAW);
CDC_Transmit_FS((char*)buffer,10);
}
}

zynq linux IRQ from PL

I am working with a zynq board , z-turn, and I added two interruptions coming from PL, I edited my device tree with :
amba_pl {
#address-cells = <0x1>;
#size-cells = <0x1>;
compatible = "simple-bus";
ranges;
gpio1#43c20000 {
#gpio-cells = <0x2>;
#interrupt-cells = <0x2>;
compatible = "generic-uio";
gpio-controller;
interrupt-controller;
interrupt-parent = <&intc>;
interrupts = <0x0 0x20 0x4>;
reg = <0x43c20000 0x10000>;
xlnx,all-inputs = <0x1>;
xlnx,all-inputs-2 = <0x0>;
xlnx,all-outputs = <0x0>;
xlnx,all-outputs-2 = <0x0>;
xlnx,dout-default = <0x0>;
xlnx,dout-default-2 = <0x0>;
xlnx,gpio-width = <0x3>;
xlnx,gpio2-width = <0x20>;
xlnx,interrupt-present = <0x1>;
xlnx,is-dual = <0x0>;
xlnx,tri-default = <0xffffffff>;
xlnx,tri-default-2 = <0xffffffff>;
};
gpio2#43c30000 {
#gpio-cells = <0x2>;
#interrupt-cells = <0x2>;
compatible = "generic-uio";
gpio-controller;
interrupt-controller;
interrupt-parent = <&intc>;
interrupts = <0x0 0x21 0x4>;
reg = <0x43c30000 0x10000>;
xlnx,all-inputs = <0x1>;
xlnx,all-inputs-2 = <0x0>;
xlnx,all-outputs = <0x0>;
xlnx,all-outputs-2 = <0x0>;
xlnx,dout-default = <0x0>;
xlnx,dout-default-2 = <0x0>;
xlnx,gpio-width = <0x3>;
xlnx,gpio2-width = <0x20>;
xlnx,interrupt-present = <0x1>;
xlnx,is-dual = <0x0>;
xlnx,tri-default = <0xffffffff>;
xlnx,tri-default-2 = <0xffffffff>;
};
};
and it is mapped to the interruptions , cat /proc/interrupts
57: 52 0 GIC 57 cdns-i2c
61: 1 0 GIC 61 SII902x_det
62: 0 0 GIC 62 logicvc
63: 0 0 GIC 63 0-0053
64: 2 0 GIC 64 gpio1
65: 2 0 GIC 65 gpio2
then I created a module with my interruption and I register them to 96 and 97
(64 + 32) and (65 + 32), the code is :
#include <linux/init.h>
#include <linux/module.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/irqdomain.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <asm/exception.h>
#include <asm/mach/irq.h>
//#define INTERRUPT 96
#define INTERRUPT 96
MODULE_LICENSE("GPL");
static irqreturn_t interrupt_hook(int irq, void *dev_id, struct pt_regs *regs) {
printk("interrupt_hook!!\n");
return IRQ_HANDLED;
}
static int __init clcdint_init(void) {
unsigned int irq;
unsigned int irqflags;
int ret;
irq = INTERRUPT;
irqflags = IRQF_TRIGGER_RISING;
ret = request_irq(irq, interrupt_hook, irqflags, "int-test", NULL);
if(ret != 0) {
printk("ERROR: IRQ request failed %d", irq);
printk(" - code %d , EIO %d , EINVAL %d\n", ret, EIO, EINVAL);
}
printk("clcdint_init\n");
return 0;
}
module_init(clcdint_init);
static void __exit clcdint_exit(void) {
unsigned int irq;
irq=INTERRUPT;
free_irq(irq, NULL);
printk("clcdint_exit\n");
}
module_exit(clcdint_exit);
then it is mapped , I am generating the IRQ via a counter and a switch in the board with leds indicating the correct operation, the functions are mapped to:
96: 0 0 zynq-gpio 0 int-test
97: 1 0 zynq-gpio 1 int-test2
right after the insmod I get
\0x1b[0;31mZ-turn#\0x1b[m insmod driverirq146.ko
SUCCESS: Registered IRQ 97
clcdint_init2
but the IRQ is not happening frequently , it happens once or twice as you see in the counter of the /proc/interrupts , maybe it is in the software side , if anybody has any tip.