I want to measure temperature using lm74 temperature sensor:
https://html.alldatasheet.com/html-pdf/9026/NSC/LM74/113/3/LM74.html
I have connected it to my stm32L476 by SPI as shown on diagram:
Conection stm temperature sensor
Code to recive temperature (it is triggled by interuption every 1 s):
float TEMP_readTemp() {
uint16_t temp_binary;
HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET);
HAL_SPI_Receive(&hspi1, &temp_binary, 1, 10);
HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET);
float temp;
temp = TEMP_ConvertTemp2(temp_binary);
if (LOG_UART_ENAIBLE)
TEST_UART_LogSTLink("Temp: %s \n\r", FLOAT, &temp);
return temp;
}
It shows a positive temperature ok. But when I have the temperature below 0 it shows -256.0625 every second record:
Temp: -4.187500
Temp: -256.062500
Temp: -4.125000
Temp: -256.062500
Temp: -4.187500
Temp: -256.062500
Temp: -4.250000
Temp: -256.062500
Temp: -4.250000
Temp: -256.062500
Temp: -4.312500
Temp: -256.062500
Temp: -4.312500
Temp: -256.062500
Temp: -4.312500
Temp: -256.062500
Any idea whats happening?
converting function:
float TEMP_ConvertTemp2(uint16_t arrayBoth) {
uint16_t bitShift;
int bitShift_int;
uint16_t division;
float temperature;
if (arrayBoth & 0x8000) {
division = arrayBoth - 1;
division = ~division;
bitShift = division >> 3;
bitShift_int = (int) bitShift;
temperature = (float) -1 * ((bitShift_int +1 )* LSB_CONST);
} else {
bitShift = arrayBoth >> 3;
bitShift_int = (int) bitShift;
temperature = bitShift_int * LSB_CONST;
}
return temperature;
}
Update:
Ok, I looked by oscilloscope to see it (today it was strange, every second record was wrong, no matter what temperature was (positive or negative)).
CS and CLOCK osciloscope
MISO and CLOCK osciloscope
Those photos show two measurements done one right after the another. The first one is -256.0625, secend is correct and is about +31.
Your TEMP_ConvertTemp2 is working as expected. as can be seen here.
Your error:
According to the datasheet the temperature packet it returns 2 bytes big. (Eg. 0x4B07) You only tell the SPI to return 1 byte. So change
HAL_SPI_Receive(&hspi1, &temp_binary, 1, 10); // Receive 1 byte
to
HAL_SPI_Receive(&hspi1, &temp_binary, 2, 10); // Receive two bytes
Update:
Seems like the sensor is not really returning anything, as it's only returning a lot of 1s with a 0 here and there. The two values you show only has one 0 each, and the idle value of the line is probably 1. This seems erroneous. A few things you can look at:
What does the HAL_SPI_Receive return? It should return HAL_OK.
Do you have a oscilloscope? If you do, show a picture of what you observe. Is there a clock signal? Is the frequencies correct? Is the CS working as expected?
Are you sure you are turning on the sensor correctly? Maybe it needs some configuration settings first?
I solved this bug. You have to make a software pulldown on MOSI in my case it was configuring STM PIN correctly.
Related
I am trying to build a 3D FPS game in unity. A friend of mine bought a (replica) gun and modified it to add an ESP32 and an MPU-9250 gyroscope/accelerometer in it to track and send the rotation of the gun (using quaternions) to unity. The problem is that each time I "power on" the gun and start the game the initial rotation of the weapon in the game is different. (I don't want to use euler angles because of the gimbal lock problem.) Any ideas where the problem might be and how to fix it?
I am currently using the MPU-9250 DMP Arduino Library as in here.
I have started thinking that the problem lies on the fact that each time I turn on the power of the gun, the gyroscope axes are reinitialized. According to another library - Calibration - accel/gyro/mag offsets are NOT stored to register if the MPU has powered off. You need to set all offsets at every bootup by yourself (or calibrate at every bootup). I do not want to do that every time the ESP32 is restarted. If only I could use a fixed coordinate system no matter what the position of the gun is, when the ESP32 reboots.
Here is the code I have written so far:
#include "src\lib\SparkFunMPU9250-DMP.h"
#include <WiFi.h>
#include <WiFiUdp.h>
// -------------------------------------------------------------------------------------------
// Enter: WEAPON_* OR Handgun_*
const String gun = "Handgun_4";
char * udpAddress;
int udpPort;
int Trigger;
int Mag;
int Shutter;
// -------------------------------------------------------------------------------------------
// --- Trigger ---
//const int Trigger = 4;
int button_state_trigger = 0;
int button_state_mag = 0;
int button_state_shutter = 0;
// --- MPU9250 ---
MPU9250_DMP imu;
// --- WiFi ---
WiFiUDP udp;
// WIFI credentials
const char * networkName = "...";
const char * networkPswd = "...";
// IP address to send UDP data to:
// either use the ip address of the server or
// a network broadcast address
//const char * udpAddress = ...;
// UDP port
//const int udpPort = ...;
// payload to sent
String payload = "";
//Connection state?
boolean connected = false;
// Timers
unsigned long timer1, timer2;
// -------------------------------------------------------------------------------------------
void setup() {
GunSelect(gun);
// Initialize Serial Com
Serial.begin(115200);
// Initialize Trigger
pinMode(Trigger, INPUT);
pinMode(Mag, INPUT);
pinMode(Shutter, INPUT);
// Initialize MPU9250
while (imu.begin() != INV_SUCCESS) {
Serial.println("Unable to communicate with MPU-9250");
delay(2000);
}
imu.setSensors(INV_XYZ_GYRO | INV_XYZ_ACCEL | INV_XYZ_COMPASS); // Enable all sensors
imu.setGyroFSR(2000); // Set Gyro dps, options are +/- 250, 500, 1000, or 2000 dps
imu.setAccelFSR(16); // Set Accel g, options are +/- 2, 4, 8, or 16 g
imu.setSampleRate(100); // Set sample rate of Accel/Gyro, range between 4Hz-1kHz
imu.setLPF(5); // Set LPF corner frequency of Accel/Gyro, acceptable cvalues are 188, 98, 42, 20, 10, 5 (Hz)
imu.setCompassSampleRate(100); // Set Mag rate, range between: 1-100Hz
imu.dmpBegin(DMP_FEATURE_6X_LP_QUAT | // 6-axis (accel/gyro) quaternion calculation
DMP_FEATURE_GYRO_CAL , // Gyroscope calibration (0's out after 8 seconds of no motion)
200);
delay(10000);
// Initialize WiFi
connectToWiFi(networkName, networkPswd);
}
// -------------------------------------------------------------------------------------------
void loop() {
// Timer Start
// timer1 = micros();
if ( imu.fifoAvailable() ) {
if ( imu.dmpUpdateFifo() == INV_SUCCESS ) {
// Switches Read
button_state_trigger = buttonState(Trigger);
if (gun == "WEAPON_1" || gun == "WEAPON_2" || gun == "WEAPON_3" || gun == "WEAPON_4" || gun == "WEAPON_5" || gun == "WEAPON_Test") {
button_state_mag = buttonState(Mag);
button_state_shutter = buttonState(Shutter);
}
double x = imu.calcQuat(imu.qx);
double y = imu.calcQuat(imu.qy);
double z = imu.calcQuat(imu.qz);
double w = imu.calcQuat(imu.qw);
Serial.println();
Serial.println(button_state_trigger);
Serial.println(button_state_mag);
Serial.println(button_state_shutter);
Serial.println("x: " + String(x));
Serial.println("y: " + String(y));
Serial.println("z: " + String(z));
Serial.println("w: " + String(w));
Serial.println();
//*/
// Send data via WiFi
payload = "ACC|" + String(x,4) + "|" + String(y,4) + "|" + String(z,4) + "|" + String(w,4) + "|" + String(button_state_trigger) + "|" + String(button_state_mag) + "|" + String(button_state_shutter);
sendData(payload);
}
}
// Timer End
// timer2 = micros();
// Serial.println(timer2 - timer1);
}
In Unity I receive the data and parse it as 4 floats. Then I set the gun's rotation in the game as (y,w,-x,z) because the coordinate system of the gun is different from the one that Unity uses. So the code is like:
// Receiving data...
float x = float.Parse(data[1]);
float y = float.Parse(data[2]);
float z = float.Parse(data[3]);
float w = float.Parse(data[4]);
gun.rotation = new Quaternion(y,w,-x,z);
----------------Edit--------------------
I read about Madgwick filter (alternatively Kalman filter and Mahony filter) which is said to be able to produce a quaternion-based estimate of absolute device orientation (based on the earth's reference system ie. North etc.) If I understood correctly . But I am not really sure if that solves my problem.
Currently, I'm using an arduino to read a joystick position, which outputs 3 values. A switch button output (1 or 0), x coord (0 - 1023), and y coord (0 - 1023). I use Serial.print to print the values to the serial monitor and using Raspberry Pi's grabserial, I get the serial data to the pi. However, I'm using ser.readline().decode('utf-8')[:-2] and I can't seem to be able to assign data to a variable. I'm trying to store the 3 most recent data values (switch, x coord, y coord) into 3 separate variables so that I can say if 'switch' is less than [something] and greater than [something] then play some command. How do I store the 3 most recent data values into 3 variables?
I have tried to use something like 'switch' = ser.readline()
if 'switch' == 1: then print("switch is not pressed") which should be printing but it says 'switch' is not equal to 1 so the data is not correctly assigned into a variable.
#Arduino
// Arduino pin numbers
const int SW_pin = 2; // connected to digital pin 2
const int X_pin = 0; // connected to analog pin 0
const int Y_pin = 1; // connected to analog pin 1
void setup() {
pinMode(SW_pin, INPUT);
digitalWrite(SW_pin, HIGH);
Serial.begin(9600);
}
void loop() {
Serial.println(digitalRead(SW_pin));
Serial.println(analogRead(X_pin));
Serial.println(analogRead(Y_pin));
delay(500);
}
# Raspberry Pi
import serial
ser = serial.Serial("/dev/ttyACM0", 9600, timeout = 0.5)
While True:
Switch = ser.readline().decode('utf-8')[:-2]
if Switch == 1:
print ("Switch is not pressed")
I expected that it would print "switch is not pressed" every 3 values, but it just prints "1". Right now I'm trying to make one reading work which it does not, but I need all three of them working at the same time.
You are comparing a string and an int. Try using
Switch == "1"
I need to generate two opposite PWM signals (when one is high the other one is low) using timers in STM32. I have read several examples and this is the code I came up with:
void TM_PINS_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
/* Alternating functions for pins */
GPIO_PinAFConfig(GPIOD, GPIO_PinSource12, GPIO_AF_TIM4);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource13, GPIO_AF_TIM4);
/* Set pins */
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOD, &GPIO_InitStruct);
}
void TM_TIMER_Init(void) {
TIM_TimeBaseInitTypeDef TIM_BaseStruct;
/* Enable clock */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
uint16_t Period;
Period = 1000000 / 200000; // 200 KHz from 1MHz
TIM_BaseStruct.TIM_Prescaler = (SystemCoreClock / 1000000) - 1; // 1MHz
TIM_BaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_BaseStruct.TIM_Period = Period - 1;
TIM_BaseStruct.TIM_ClockDivision = 0;
TIM_BaseStruct.TIM_RepetitionCounter = 0;
/* Initialize TIM4 */
TIM_TimeBaseInit(TIM4, &TIM_BaseStruct);
/* Start count on TIM4 */
TIM_Cmd(TIM4, ENABLE);
}
void TM_PWM_Init(void) {
TIM_OCInitTypeDef TIM_OCStruct;
/* PWM mode 2 = Clear on compare match */
/* PWM mode 1 = Set on compare match */
TIM_OCStruct.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OC1Init(TIM4, &TIM_OCStruct);
TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);
TIM_OCStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OC2Init(TIM4, &TIM_OCStruct);
TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable);
TIM_OCStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCStruct.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OCStruct.TIM_Pulse = Period/2; /* 50% duty cycle */
}
I have several questions:
Since I need frequencies in terms of tens of kHz, do I need 100MHz setting for the GPIO speed? Is there any benefit to make it slower?
Would my PWM function generate what I need? I think the opposite PWM signals are actually achieved using PWM modes 1 and 2, but I am afraid I may be missing something there.
I want to be able to stop the timers, and to set high/low state to the same pins. Would the following code work?
GPIOD-> MODER |= (5 << 24); //set pin 12 and 13 to GPIO, 5 gives us 101, shifted 23 bits so that '1's are in pos. 24 and 26
GPIOD->regs->BSRR = (3<<12); // 3 gives us 11 pattern, shifted 12 bits so that pins 12 and 13 are set to high
GPIOD-> MODER |= (5 << 24); //set pin 12 and 13 to AF, 5 gives us 101, shifted 24 bits so that '1's are in pos. 25 and 27
When the AF is restored, are the previous pwm settings restored or must be set again.
Would it be better to use MODER as above to change pins to GPIO and then set to high/low, or I can use 100% duty cycle for high and 0% duty cycle for low?
Q1: No, you do not need to. Faster settings means more power consumed by the peripheral and sharper edges of the generated signal which causes more EMI. If you do not care about the both - it does not matter.
Q2: You need to check it yourself if you use some kind of "magic" numbers.
Q3: Same as above. Why don't you use human readable CMSIS definitions only "magic" functions. If you correct set the register values it will work
Q4: If you stopped the timer you need to start/configure it again. If not it should work.
Q5: It does not matter. But running timer will generate noise and consume some power.
I would like to connect the Concept2 rower to my iPhone.
The corresponding Bluetooth data sheet can be found here : http://www.concept2.com/files/pdf/us/monitors/PM5_BluetoothSmartInterfaceDefinition.pdf
I would like to get back the different data: ElapsedTime, Distance, Split/interval, etc..
From the UUID adress 0X0031 I get a 19 bytes data in the following order:Elapsed Time Lo (0.01 sec lsb), Elapsed Time Mid, Elapsed Time High, Distance Lo (0.1 m lsb), Distance Mid, Distance Hi, ...
So 1 byte corresponds to 1 attribute.
I need to extract the bytes corresponding to the attribute and convert them.
I think all bytes data are unsigned types (for extraction).
The ElapsedTime variable is on 3 bytes. SO to build the ElapsedTime variable I was computing like this:
class func dataToUnsignedBytes8(value:NSData) -> [UInt8]{
let count = value.length
var array = [UInt8](count: count, repeatedValue: 0)
value.getBytes(&array, length: count * sizeof(UInt8))
return array
}
class func getElapsedTime(value : NSData) -> Double {
let dataFromSensor = dataToSignedBytes8(value)
let elapsedTime = Double(dataFromSensor[2] * 65536 + dataFromSensor[1]*256 + dataFromSensor[0])
return elapsedTime
}
But I'm not sure about what I'm doing.
Does the ElapsedTime_Hi byte is at index 3 of dataFromSensor, ElapsedTime_Mid is at index 2 of dataFromSensor and ElapsedTime_Lo at index 0 dataFromSensor ?
What is the best way to extract the corresponding byte for other attributes ?
Thank you in advance
Regards
I am currently in the process of building an application that reads in audio from my iPhone's microphone, and then does some processing and visuals. Of course I am starting with the audio stuff first, but am having one minor problem.
I am defining my sampling rate to be 44100 Hz and defining my buffer to hold 4096 samples. Which is does. However, when I print this data out, copy it into MATLAB to double check accuracy, the sample rate I have to use is half of my iPhone defined rate, or 22050 Hz, for it to be correct.
I think it has something to do with the following code and how it is putting 2 bytes per packet, and when I am looping through the buffer, the buffer is spitting out the whole packet, which my code assumes is a single number. So what I am wondering is how to split up those packets and read them as individual numbers.
- (void)setupAudioFormat {
memset(&dataFormat, 0, sizeof(dataFormat));
dataFormat.mSampleRate = kSampleRate;
dataFormat.mFormatID = kAudioFormatLinearPCM;
dataFormat.mFramesPerPacket = 1;
dataFormat.mChannelsPerFrame = 1;
// dataFormat.mBytesPerFrame = 2;
// dataFormat.mBytesPerPacket = 2;
dataFormat.mBitsPerChannel = 16;
dataFormat.mReserved = 0;
dataFormat.mBytesPerPacket = dataFormat.mBytesPerFrame = (dataFormat.mBitsPerChannel / 8) * dataFormat.mChannelsPerFrame;
dataFormat.mFormatFlags =
kLinearPCMFormatFlagIsSignedInteger |
kLinearPCMFormatFlagIsPacked;
}
If what I described is unclear, please let me know. Thanks!
EDIT
Adding the code that I used to print the data
float *audioFloat = (float *)malloc(numBytes * sizeof(float));
int *temp = (int*)inBuffer->mAudioData;
int i;
float power = pow(2, 31);
for (i = 0;i<numBytes;i++) {
audioFloat[i] = temp[i]/power;
printf("%f ",audioFloat[i]);
}
I found the problem with what I was doing. It was a c pointer issue, and since I have never really programmed in C before, I of course got them wrong.
You can not directly cast inBuffer->mAudioData to an int array. So what I simply did was the following
SInt16 *buffer = malloc(sizeof(SInt16)*kBufferByteSize);
buffer = inBuffer->mAudioData;
This worked out just fine and now my data is of correct length and the data is represented properly.
I saw your answer, there also is an underlying issue which gives wrong sample data bytes which is because of an endian issue of bytes being swapped.
-(void)feedSamplesToEngine:(UInt32)audioDataBytesCapacity audioData:(void *)audioData {
int sampleCount = audioDataBytesCapacity / sizeof(SAMPLE_TYPE);
SAMPLE_TYPE *samples = (SAMPLE_TYPE*)audioData;
//SAMPLE_TYPE *sample_le = (SAMPLE_TYPE *)malloc(sizeof(SAMPLE_TYPE)*sampleCount );//for swapping endians
std::string shorts;
double power = pow(2,10);
for(int i = 0; i < sampleCount; i++)
{
SAMPLE_TYPE sample_le = (0xff00 & (samples[i] << 8)) | (0x00ff & (samples[i] >> 8)) ; //Endianess issue
char dataInterim[30];
sprintf(dataInterim,"%f ", sample_le/power); // normalize it.
shorts.append(dataInterim);
}