I've found a few other people ask about this, but all of them are coding in Arduino, and I'm struggling to translate it into my project.
I'm on a research team developing a Cube Satellite for launch, and my role is to communicate with the peripherals, including the IMU (MPU-9250). I'm cross compiling with uClinux, coding in C.
So far, I can successfully read the accelerometer, gyroscope, and temperature, but cannot get the readings from the magnetometer. The magnetometer (AK8963) has its own address (0x0C), and I've been trying to communicate with it by writing to I2C_SLV0_ADDR(0x25), I2C_SLV0_REG(0x26), and I2C_SLV0_CTRL(0x27). Once I didn't appear to get any results, I tried to resolve it by writing to FIFO Enable(0x23) and I2C Master Control(0x24)
The datasheet and register map imply that the values taken from the magnetometer are stored in registers External Sensor Data(0x49-0x60), but I'm not getting anything in those registers when I try this.
Here's some code that shows the data I'm writing to the registers:
write_register(0x23, 0x04);
write_register(0x24, 0b11110000);
write_register(0x25,0x8c);
write_register(0x26,0x00);
write_register(0x27,0b11110001);
So my questions are:
1. Am I going about this process the right way, or am I completely off?
If I am on the right track, am I reading from the correct registers?
Thanks for your help everyone! Let me know if there's anything I need to clarify!
Alright! I figured out my own question :)
1) I was indeed going about the process the wrong way. There is a much easier way to do that and I will show how in detail.
2) I was not reading from the correct registers. I'll show what to do below.
So, connecting to the Magnetometer is super easy, but not intuitive at all. It does act like its own slave with its own slave address, but that address is inaccessible initially.
Since I'm cross compiling with uClinux, I can use the bash command i2cdetect 0 (when screening into my SmartFusion2) to check all the slaves on my i2C 0 bus. When I execute this command after resetting my board, I get the following address map printed out:
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
So you can see that only the address for the IMU(0x68) is showing. To get the magnetometer showing up properly, you have to enable a bypass bit on INT_PIN_CFG(0x37). I've attached my code below in case anyone needs to replicate this.
#include <stdio.h>
//#include <linux/i2c-dev.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#define IMU_ADDR 0x68
#define IMU_WHO_AM_I 0x75
int file;
void i2c_init();
void write_register(uint8_t register_address, uint8_t value);
uint8_t read_register(uint8_t register_address);
void i2c_init(address){
int adapter_nr = 0;
char filename[20];
snprintf(filename, 19, "/dev/i2c-%d", adapter_nr);
file = open(filename, O_RDWR);
if(file < 0)
printf("Error: Failed to open file %s\n", filename);
int success= (ioctl(file, I2C_SLAVE, address));
if(file < 0) {
printf("Error: IMU I2C Failed to Open\n");
//return -1;
}
}
void write_register(uint8_t register_address, uint8_t value){
uint8_t data[]={register_address,value};
write(file, data,ARRAY_SIZE(data));
}
uint8_t read_register(uint8_t register_address){
uint8_t value;
if(write(file, ®ister_address, sizeof(register_address)) !=1)
{
printf("%d\n",write(file, ®ister_address, sizeof(register_address)));
printf("Failed to send data\n");
}
read(file, &value, sizeof(value));
return value;
}
int main(){
i2c_init(IMU_ADDR);
printf("\nNow testing the 'Who am I?' IMU register. Output should be 0x71\n");
printf("Register 0x75: 0x%02X \n", read_register(IMU_WHO_AM_I));
write_register(0x37, 0x22);
i2c_init(0x0C);
printf("\nNow testing the 'Who am I?' Magnetometer register. Output should be 0x48\n");
printf("Register 0x00: 0x%02x\n", read_register(0x00));
return 0;
}
Once the code is compiled and run, the command i2cdetect 0 returns the following datamap.
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- 0c -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
The program also returns 0x48, the correct value when reading from the magnetometer's 'who am I?' register. The magnetometer can now be read just like any other register. Hope this helps!!
Related
How to check if a variable in snowflake stored procedure is null or not. I am using snowflake scripting.
My Code:
begin
select id into :id from REQUEST where id = 1;
if(id = null) then
return 0;
The syntax for branch construct:
BEGIN
-- ...
IF (id IS NULL) THEN
RETURN 0;
END IF;
END;
It is also possible to use COALESCE if it is just a return statement:
BEGIN
-- ...
RETURN COALESCE(id, 0);
END;
Tried to cover the bases here, primarily when you have no results from the query as well as when the actual result is a NULL returned value.
--
-- create test table
--
CREATE or replace TABLE REQUEST (id integer, val INTEGER);
INSERT INTO REQUEST VALUES (1,1),(2,null),(3,3);
--
-- create test proc using SQL Script
--
CREATE OR REPLACE PROCEDURE TEST_NULLS(input integer)
returns INTEGER
LANGUAGE SQL
AS
DECLARE
val INTEGER;
rc INTEGER;
input_id INTEGER;
BEGIN
input_id := input;
-- get the row count to determine if the query criteria produces a result
SELECT count(*) into :rc
FROM REQUEST
WHERE id = :input_id;
-- if empty result set, return 0.
IF (rc = 0) then return (rc);
ELSE
-- otherwise, return the value retrieved from the query, unless it is NULL in which case return -1
SELECT nvl(val,-1) into :val
FROM REQUEST where id = :input_id;
return (val);
END IF;
END;
--
-- Test examples
--
call test_nulls(1); -- Returns 1
call test_nulls(2); -- Returns -1, since returned value is NULL
call test_nulls(3); -- Returns 3
call test_nulls(4); -- Returns 0, since there is no result set
I am trying to fetch the columns from a previously returned into a table of records. However, when i run the code in the package body I get the following error:
ORA-06504: PL/SQL: Return types of Result Set variables or query do not match
However, when i try to do this in a PL/SQL anonymous block window, I can successfully fetch the records using Bulk Collect instruction.
Here is my successful try as I stated:
DECLARE
--
O_ref SYS_REFCURSOR;
-- Variable and types declaration.
TYPE REC_TYP is record (
column_1 number(8),
column_2 varchar2(13)
);
TYPE TAB_TYP is table of REC_TYP;
L_tab_typ TAB_TYP;
--
BEGIN
--
open O_ref for
select sku,
upc
from upc_ean
where sku = 2004030;
--
LOOP
--
FETCH O_ref BULK COLLECT into L_tab_typ;
EXIT WHEN L_tab_typ.COUNT = 0;
--
FOR indx IN 1 .. L_tab_typ.COUNT
LOOP
--
dbms_output.put_line('SKU: ' || L_tab_typ(indx).column_1);
dbms_output.put_line('UPC: ' || L_tab_typ(indx).column_2);
--
END LOOP;
--
END LOOP;
--
CLOSE O_ref;
--
END;
When I run this code I get the following output:
SKU: 2004030
UPC: 5601126003439
SKU: 2004030
UPC: 5601126039056
In the package body I have the following:
Why doesn't this work in a regular package?
FUNCTION GET_STORE_ITEMS(I_store IN number
----------- output ------------
O_item_data OUT NB_TAB_ITEM_DETAIL, -- i want to return a table type after I get the info from the sys_ref
----------- error -------------
O_error_message OUT VARCHAR2)
RETURN BOOLEAN IS
--
L_tab_type NB_TAB_ITEM_DETAIL;
L_sys_ref SYS_REFCURSOR;
L_test_sku number(8);
--
CURSOR C_GET_ITEMS IS
--
SELECT a.sku
FROM win_store a
WHERE a.store = I_store;
--
BEGIN
--
-- Loop over the fashion skus.
FOR R_items IN C_GET_ITEMS LOOP
--
BEGIN
--
IF GET_ITEM_DETAIL(I_store => I_store,
I_sku => R_items.sku,
O_item_data => L_sys_ref, -- returns a sys_refcursor with the same structure as the type
O_error_message => L_error_message) = FALSE THEN
--
O_error_message := NB_MESSGE40_SQL.EMESSAGE(L_error_message);
RETURN FALSE;
--
END IF;
--
LOOP
--
FETCH L_sys_ref BULK COLLECT into L_tab_type; -- It jumps to when others exception
EXIT WHEN L_tab_type.COUNT = 0;
--
FOR indx IN 1 .. L_tab_type.COUNT
LOOP
--
L_test_sku := L_tab_type(indx).sku;
--
END LOOP;
--
END LOOP;
--
END;
--
END LOOP;
--
RETURN TRUE;
--
EXCEPTION
--
WHEN OTHERS THEN
--
-- ...
RETURN FALSE;
--
END GET_STORE_ITEMS;
Thank you!
I actually happened to solve the problem. It was necessary to declare a package-spec level type before fetching the sys_refcursor, just like it was done in the anonymous block.
At first, the record and table types were created in the database:
CREATE OR REPLACE TYPE NB_REC_ITEM_DETAIL AS OBJECT
(
column_1 number(8),
column_2 varchar2(13)
);
CREATE OR REPLACE TYPE NB_TAB_ITEM_DETAIL AS TABLE OF NB_REC_ITEM_DETAIL;
However, this structure is unknown for the PL/SQL engine and so it is necessary to create these data structures in the package spec as follows:
TYPE REC_TYP is record (
column_1 number(8),
column_2 varchar2(13)
);
TYPE TAB_TYP is table of REC_TYP;
Now it is possible to fetch the data from the sys_refcursor.
FUNCTION GET_STORE_ITEMS(I_store IN NUMBER,
----------- output ------------
O_item_data OUT NB_TAB_ITEM_DETAIL,
----------- error -------------
O_error_message OUT VARCHAR2)
RETURN BOOLEAN IS
--
L_error_message VARCHAR2(255);
L_item_data_rec REC_TYP;
L_item_detail_ref SYS_REFCURSOR;
--
CURSOR C_GET_STORE_ITEMS IS
--
SELECT a.sku
FROM win_store a
WHERE a.store = I_store;
--
BEGIN
--
-- Instantiate output structure object.
--
O_item_data := NB_TAB_ITEM_DETAIL();
--
-- Loop over the items in the store range.
--
FOR R_items IN C_GET_STORE_ITEMS LOOP
--
BEGIN
--
IF NB_ITEM_INFO_SQL.GET_ITEM_DETAIL(I_store => I_store,
I_sku => R_items.sku,
I_ean => NULL,
O_item_data => L_item_detail_ref,
O_error_message => L_error_message) = FALSE THEN
--
O_error_message := NB_MESSGE40_SQL.EMESSAGE(L_error_message);
RETURN FALSE;
--
END IF;
--
-- Fetch info from item_data sys_refcursor to record type.
--
FETCH L_item_detail_ref INTO L_item_data_rec;
EXIT WHEN L_item_detail_ref%NOTFOUND;
--
CLOSE L_item_detail_ref;
--
O_item_data.extend();
O_item_data(O_item_data.last) := nb_rec_item_detail(column_1 => L_item_data_rec.column_1 ,
column_2 => L_item_data_rec.column_2);
--
END;
--
END LOOP;
--
RETURN TRUE;
--
EXCEPTION
--
WHEN OTHERS THEN
-- ...
RETURN FALSE;
--
--
END GET_STORE_ITEMS;
I have here a card that fits in an PCIe slot, with some SMBus controllable chips on it. The card is self layouted and works fine in some older mainboard. In a newer mainboard (ASUS PRIME Z370-A) it is not working.
The card is not using the PCIe Interface only the PCIe Slot. There is no connection between PRSNT1 and 2 and moreover no connection to the differential data lines. The PCIe Slot is only used for power supply (Pins A9, A19, B8 (+3.3V) - A4, A12, A15, A18, B4, B7, B13, B26, B18(GND)) and SMBus interface (B5 (SMCLK), B6 (SMDAT)).
With not working I mean that I can not talk to it via SMBus. I'm trying this with the i2c-dev kernel module. The output of i2cdetect -l is: (shortened, I removed the i2c devs of the nvidia card)
i2c-3 i2c i915 gmbus dpd I2C adapter
i2c-1 i2c i915 gmbus dpc I2C adapteradapter
i2c-4 i2c DPDDC-B I2C adapter
i2c-2 i2c i915 gmbus dpb I2C adapter
i2c-0 smbus SMBus I801 adapter at f040 SMBus adapter
In my opinion the i2c-0 device is the right one. When I run i2cdetect on Interface 0 some chips are detected, but not the right one (they have addr 0x70 and 0x72). To sum up I'm trying to talk via the SMBus master in the Intel 200 PCH with my SMBus slave chips.
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- 08 -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: 30 31 -- -- 34 35 -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- 44 -- -- -- -- -- -- -- -- -- -- --
50: -- 51 -- 53 -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
I tried also bus 1-4 but the same result. On the other mainboard the detection is working, as you see below.
Furthermore I measured with my oscilloscope on pin B5 and B6 of the PCIe connector. There is no clock or data signal on one of these pins on the new mainboard.
I think I have to configure somehow the kernel module that it also tries to communicate to SMBus devices on the PCIe Slots. Maybe somebody can give me a hint.
Although no warnings about overlapping memory regions have occurred, I set the kernel option acpi_enforce_resources=lax to ensure that no ACPI memory binding conflicts with the SMBus Controller.
Some outputs
dmesg modprobe i2c-i801 (Output of kernel module, that abstracts the Intel SMBus Controller)
[ 81.667581] i801_smbus 0000:00:1f.4: SPD Write Disable is set
[ 81.667639] i801_smbus 0000:00:1f.4: SMBus using PCI interrupt
i2cdetect 0 (Working mainboard, SMBus Controller on i2c-0)
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- 38 -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: 50 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: 70 -- 72 -- -- -- -- --
uname -a
Linux XY 4.16.0-1-amd64 #1 SMP Debian 4.16.5-1
I've got an answer from Asus. The ASUS PRIME Z370-A has no SMBus connection on the PCIe Slots.
According to PCIe Card Electromechanical Specification, chapter 2 "Auxiliary Signals", the SMBus interface pins are collectively optional for both the add-in card and the system board. On the other hand PCIe M.2 specification mentiones (this ECN is incorporated in the standard) SMBus wires on boards to be helpful for sensors located on SSD devices.
So, in your case obviously old motherboard is more advanced than the newer one, it has those wirings done.
using select statement we can call functions inside the stored procedure, is there another ways to call functions inside the stored procedure.
getdate() you can call inside a proc
-- ================================================
-- Template generated from Template Explorer using:
-- Create Procedure (New Menu).SQL
--
-- Use the Specify Values for Template Parameters
-- command (Ctrl-Shift-M) to fill in the parameter
-- values below.
--
-- This block of comments will not be included in
-- the definition of the procedure.
-- ================================================
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
CREATE PROCEDURE Proc1
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
declare #date datetime
set #date= getdate()
END
GO
try
SET #yourvariable = yourfunctionname('your params');
Function
create function fn_FunctionSum(#Number1 int , #Number2 int)
returns int
as
begin
declare #result int
select #result = #Number1 + #Number2;
return #result
end
Store Procedure
create proc sp_Sum(#first int, #second int )
as
begin
-------Call Function in store procedure ------
select [dbo].[fn_FunctionSum](#first, #second)
end
execute sp_Sum 2,5
Once again.. i have the trigger below which has the function to keep/set the value in column esb for maximum 1 row to value 0 (in each row the value cycles from Q->0->R->1)
When i insert more than 1 row the trigger fails with an "Subquery returned more than 1 value. This is not permitted when the subquery follows" error on row 38, the "IF ((SELECT esb FROM INSERTED) in ('1','Q'))" statment.
I understand that 'SELECT esb FROM INSERTED' will return all rows of the insert, but do not know how to process one row at a time. I also tried it by creating a temporary table and iterating through the resultset, but subsequently found out that temporary tables based on the INSERTED table are not allowed.
any suggestions are welcome (again)
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
ALTER TRIGGER [TR_PHOTO_AIU]
ON [SOA].[dbo].[photos_TEST]
AFTER INSERT,UPDATE
AS
DECLARE #MAXCONC INT -- Maximum concurrent processes
DECLARE #CONC INT -- Actual concurrent processes
SET #MAXCONC = 1 -- 1 concurrent process
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON
-- If column esb is involved in the update, does not necessarily mean
-- that the column itself is updated
If ( Update(ESB) )
BEGIN
-- If column esb has been changed to 1 or Q
IF ((SELECT esb FROM INSERTED) in ('1','Q'))
BEGIN
-- count the number of (imminent) active processes
SET #CONC = (SELECT COUNT(*)
FROM SOA.dbo.photos_TEST pc
WHERE pc.esb in ('0','R'))
-- if maximum has not been reached
IF NOT ( #CONC >= #MAXCONC )
BEGIN
-- set additional rows esb to '0' to match #MAXCONC
UPDATE TOP(#MAXCONC-#CONC) p2
SET p2.esb = '0'
FROM ( SELECT TOP(#MAXCONC-#CONC) p1.esb
FROM SOA.dbo.photos_TEST p1
INNER JOIN INSERTED i ON i.Value = p1.Value
AND i.ArrivalDateTime > p1.ArrivalDateTime
WHERE p1.esb = 'Q'
ORDER BY p1.arrivaldatetime ASC
) p2
END
END
END
Try to rewrite your IF as:
IF EXISTS(SELECT 1 FROM INSERTED WHERE esb IN ('1','Q'))
...