Skip to content
Snippets Groups Projects
Commit fc0dc453 authored by Joakim Lundberg's avatar Joakim Lundberg
Browse files

Working I2C sensor

parent ab54bad0
No related branches found
No related tags found
No related merge requests found
target remote 192.168.1.4:3333
monitor reset init
monitor arm semihosting enable
load target/thumbv7em-none-eabihf/debug/d7018e_coap
monitor reset init
......@@ -17,9 +17,19 @@ volatile-register = "0.2.0"
m = "0.1.1"
heapless = "0.2.4"
f4 = {git = "https://github.com/jsjolund/f4"}
stm32f40x = "0.2.0"
#stm32f40x = "0.2.0"
#xoap = { path = "../projects/xoap", version = "0.1.0", default-features = false, features = ["baremetal"] }# git = "https://github.com/Shawnshank/xoap"
#[dependencies.stm32f4x_hal]
#path = "stm32f4-hal/"
#version = "0.1.1"
#features = ["rt"]
[dependencies.stm32f40x]
git = "https://gitlab.henriktjader.com/pln/STM32F40x.git"
features = ["rt"]
version = "0.2.0"
[dependencies.cast]
default-features = false
version = "0.2.2"
......
//! Test sending messages and receiving commands using the DMA
#![deny(unsafe_code)]
#![deny(warnings)]
#![allow(dead_code)]
#![allow(non_snake_case)]
#![feature(const_fn)]
#![feature(proc_macro)]
#![feature(lang_items)]
#![no_std]
extern crate cortex_m;
extern crate cortex_m_rtfm as rtfm;
#[macro_use]
extern crate cortex_m_debug;
extern crate cortex_m_rtfm as rtfm;
extern crate f4;
extern crate heapless;
extern crate stm32f40x;
use core::fmt::Write;
use core::ops::Deref;
use f4::Serial;
use f4::U8Writer;
use core::result::Result;
use f4::prelude::*;
use f4::dma::{Buffer, Dma1Stream5, Dma1Stream6};
use f4::time::Hertz;
//use f4::Timer;
use f4::clock;
use f4::I2c;
use heapless::Vec;
use rtfm::{app, Threshold};
//use core::result::Result;
//use f4::stm32f40x::I2C1;
// Max length of a command to be parsed.
const MAX_CMD_LEN: usize = 10;
// Max length of an output string that can be sent.
const MAX_TX_LEN: usize = 100;
// Max length of input buffer. Since we are parsing for commands, we need to check each received character.
const MAX_RX_LEN: usize = 1;
// Baud rate of the serial interface
const BAUD_RATE: Hertz = Hertz(9_600);
// Temperature and humidity update frequency
const SENSOR_READ_TIME: Hertz = Hertz(1);
//const DATA_RECIEVE_SIZE: usize = 4;
use f4::{Serial, Timer, I2c, clock};
use f4::serial::Event;
use f4::time::{Hertz, Seconds};
use stm32f40x::I2C1;
use rtfm::{app, Resource, Threshold};
// CONFIGURATION
const BAUD_RATE: Hertz = Hertz(115_200);
const RX_BUFFER_SIZE: usize = core::mem::size_of::<u32>();
const SENSOR_READ_TIME: Seconds = Seconds(60); // corresponds to ~20 sec for some reson. Maybe something with the increased clockspeed.
// Errors from the HIH6030-021-001
#[derive(Debug)]
pub enum Error {
StaleData,
CommandMode,
Unknown,
}
// Command types from the serial interface
enum CmdType {
Greeting,
Data,
Unknown,
None,
}
app! {
device: f4::stm32f40x,
resources: {
static CMD_BUFFER: Vec<u8, [u8; MAX_CMD_LEN]> = Vec::new();
static RX_BUFFER: Buffer<[u8; MAX_RX_LEN], Dma1Stream5> = Buffer::new([0; MAX_RX_LEN]);
static TX_BUFFER: Buffer<[u8; MAX_TX_LEN], Dma1Stream6> = Buffer::new([0; MAX_TX_LEN]);
static CNT: u8 = 0;
// static CMD: CmdType = CmdType::None;
static TEMPERATURE: f32 = 0.0;
static HUMIDITY: f32 = 0.0;
},
tasks: {
DMA1_STREAM5: {
path: rx_done,
USART2: {
path: rx,
priority: 1,
resources: [CMD_BUFFER, RX_BUFFER, TX_BUFFER, DMA1, USART2, CNT, TEMPERATURE, HUMIDITY], // include CMD
resources: [USART2, TEMPERATURE, HUMIDITY],
},
DMA1_STREAM6: {
path: tx_done,
priority: 2,
resources: [TX_BUFFER, DMA1],
},
/*TIM2: {
EXTI1: {
path: read_sensor,
priority: 3,
resources: [TIM2, I2C1, TEMPERATURE, HUMIDITY],
},*/
resources: [I2C1, TEMPERATURE, HUMIDITY],
},
TIM2: {
path: sensor_update,
priority: 2,
resources: [TIM2],
},
},
}
fn init(p: init::Peripherals, r: init::Resources) {
fn init(p: init::Peripherals, _r: init::Resources) {
// Set clock to higher than default in order to test that it works
clock::set_84_mhz(&p.RCC, &p.FLASH);
// Start the serial port
let serial = Serial(p.USART2);
serial.init(BAUD_RATE.invert(), Some(p.DMA1), p.GPIOA, p.RCC);
serial.init(BAUD_RATE.invert(), None, p.GPIOA, p.RCC);
serial.listen(Event::Rxne);
// Start the I2C peripheral
let i2c = I2c(p.I2C1);
......@@ -104,145 +74,100 @@ fn init(p: init::Peripherals, r: init::Resources) {
i2c.enable();
// Start the timer for the update of the Temp/RH sensor.
/*let timer = Timer(&*p.TIM2);
timer.init(SENSOR_READ_TIME.invert(), p.RCC);
let timer = Timer(&*p.TIM2);
timer.init(SENSOR_READ_TIME, p.RCC);
timer.resume();
*/
// FIXME: We cannot use the uprint macro in the init since it needs Resources
// and Threshold...
// Send a welcome message by borrowing a slice of the transmit buffer and
// writing a formatted string into it.
write!(
U8Writer::new(&mut r.TX_BUFFER.borrow_mut()[..MAX_TX_LEN]),
"Hello, world! Say hi to me!\r\n",
).unwrap();
serial.write_all(p.DMA1, r.TX_BUFFER).unwrap();
// Listen to serial input on the receive DMA
serial.read_exact(p.DMA1, r.RX_BUFFER).unwrap();
}
// Interrupt for serial receive DMA
fn rx_done(t: &mut Threshold, mut r: DMA1_STREAM5::Resources) {
use rtfm::Resource;
let mut byte: u8 = 0;
let mut cmd_type: CmdType = CmdType::None;
r.RX_BUFFER.claim(t, |rx, t| {
// We need to unlock the DMA to use it again in the future
r.DMA1.claim(t, |dma, _| rx.release(dma).unwrap());
// Read the single character in the input buffer
byte = rx.deref().borrow()[0];
// Echo the character back to the sender.
// We do not need to use DMA to transmit.
r.USART2.claim(t, |usart, t| {
let serial = Serial(&**usart);
if serial.write(byte).is_err() {
//rtfm::bkpt();
}
if byte == b'\r' {
// If we got carrige return, send new line as well
while serial.write(b'\n').is_err() {
// Since we just transmitted carrige return,
// we need to wait until tx line is free
}
}
// Get ready to receive again
r.DMA1
.claim(t, |dma, _| serial.read_exact(dma, rx).unwrap());
});
fn rx(_t: &mut Threshold, r: USART2::Resources) {
// Serial interface
let serial = Serial(&**r.USART2);
// Match to the byte read
match serial.read() {
Ok(_) => {
// DEBUG
let mut _temp: f32 = 0.0;
let mut _rh: f32 = 0.0;
r.TEMPERATURE.claim(_t, |temp, _| {
_temp = **temp;
});
// Parse the user input
r.CMD_BUFFER.claim_mut(t, |cmd, _| {
if byte == b'\r' {
// End of command
cmd_type = match &***cmd {
b"hi" | b"Hi" => CmdType::Greeting,
b"data" => CmdType::Data,
_ => {
if cmd.len() == 0 {
CmdType::None // Empty string
} else {
CmdType::Unknown // Unknown string
}
}
};
cmd.clear();
} else {
// Append character to buffer
if cmd.push(byte).is_err() {
// Error: command buffer is full
cmd.clear();
}
}
r.HUMIDITY.claim(_t, |rh, _| {
_rh = **rh;
});
// If user wrote 'hi' and pressed enter, respond appropriately
match cmd_type {
CmdType::Greeting => {
// Increment 'hi' counter
**r.CNT = (**r.CNT).wrapping_add(1);
let cnt: u8 = **r.CNT;
// Print a response using DMA
uprint!(t, r.USART2, r.DMA1, r.TX_BUFFER, "Hi counter {}!\r\n", cnt);
}
CmdType::Data => {
//let test: u8 = **r.TEMPERATURE;
uprint!(t, r.USART2, r.DMA1, r.TX_BUFFER, "Temp:!\r\n");
sprintln!("Temp: {}", _temp);
sprintln!("RH: {}", _rh);
//while serial.write(byte)
}
CmdType::Unknown => {
// Unknown command
uprint!(t, r.USART2, r.DMA1, r.TX_BUFFER, "That's no greeting.\r\n");
Err(_) => {
r.USART2.dr.read(); // clear the error by reading the data register
}
_ => {}
}
}
// Interrupt for serial transmit DMA
fn tx_done(t: &mut Threshold, r: DMA1_STREAM6::Resources) {
use rtfm::Resource;
// We need to unlock the DMA to use it again in the future
r.TX_BUFFER.claim(t, |tx, t| {
r.DMA1.claim(t, |dma, _| tx.release(dma).unwrap());
});
// Clear the transmit buffer so we do not retransmit old stuff
r.TX_BUFFER.claim_mut(t, |tx, _| {
let array = &**tx.deref();
array.borrow_mut()[..MAX_TX_LEN].clone_from_slice(&[0; MAX_TX_LEN]);
});
// Interrupt for the timer for the update of the Temp/RH sensor.
fn sensor_update(_t: &mut Threshold, r: TIM2::Resources) {
r.TIM2.sr.modify(|_, w| w.uif().clear_bit());
// Sets a new sensor read as a pending task
rtfm::set_pending(f4::stm32f40x::Interrupt::EXTI1);
}
/*
// Interrupt for the timer for the update of the Temp/RH sensor
fn read_sensor(_t: &mut Threshold, r: TIM2::Resources) {
// Clear the interrupt flag
r.TIM2.sr.modify(|_, w| w.uif().clear_bit());
// Reads and interpretes the sensor data.
fn read_sensor(_t: &mut Threshold, r: EXTI1::Resources) {
// I2C interface.
let i2c = I2c(&**r.I2C1);
// Read back to check that it worked
// 4 Byte buffer to store the sensor data in.
let mut rx: [u8; RX_BUFFER_SIZE] = [0; RX_BUFFER_SIZE];
match get_data(&i2c, &mut rx) {
Err(_) => {}
Err(err) => match err {
Error::StaleData => {
sprintln!("ERROR: Sensor has Stale data");
}
Error::CommandMode => {
sprintln!("ERROR: Sensor in command mode");
}
Error::Unknown => {
sprintln!("ERROR: Sensor displayed unknown error");
}
},
Ok(_) => {
// Overly explicit but the calculation gets sad otherwise.
let mut lt: u16 = rx[3] as u16;
let mut mt: u16 = rx[2] as u16;
let mut lh: u16 = rx[1] as u16;
let mut mh: u16 = rx[0] as u16;
// Calculate temperature and store the value in the resources
let mut _temp: f32 = (((rx[2] << 6) | (rx[3] >> 2)) as f32) * 100.0 / 16382.0;
//let mut _temp: f32 = (_T as f32) * 100.0 / 16382.0;
**r.TEMPERATURE = _temp;
let mut temp_count: u16 = mt << 6 | lt >> 2;
let mut _temp: f32 = temp_count as f32 * 165.0 / 16382.0 - 40.0;
// Calculate humidity and store the value in the resources
let mut _RH: f32 = (((rx[2] << 6) | (rx[3] >> 2)) as f32) * 165.0 / 16382.0 - 40.0;
**r.HUMIDITY = _RH;
let mut rh_count: u16 = (mh & 0x3F) << 8 | lh;
let mut _rh: f32 = rh_count as f32 * 100.0 / 16382.0;
// Debugging
//sprintln!("TEMP: {}", _temp);
//sprintln!("RH: {}", _rh);
// Store the values for use in other tasks
r.TEMPERATURE.claim_mut(_t, |temp, _| {
**temp = _temp;
});
r.HUMIDITY.claim_mut(_t, |rh, _| {
**rh = _rh;
});
}
}
}
// Gets the data from the Temp/RH sensor over the I2C bus
fn get_data(i2c: &I2c<I2C1>, rx_buffer: &mut [u8; RX_BUFFER_SIZE]) -> Result<(), Error> {
while i2c.start(0x4E).is_err() {} // 0x4E - write (2*27), 0x4F - Read (2*27)+1
//while i2c.write(0).is_err() {}
// Send a Measurment Request (MR) to the sensor
while i2c.start(0x4E).is_err() {}
while i2c.stop().is_err() {}
sprintln!("READ"); // TODO: NEEDS TO BE SLOWED DOWN, HERE WITH THE USE OF SEMIHOSTING
// Read incoming bytes and ACK them
// Read incoming bytes and ACK them or nack if it is the last byte.
while i2c.start(0x4F).is_err() {}
for i in 0..RX_BUFFER_SIZE {
rx_buffer[i] = loop {
......@@ -259,8 +184,10 @@ fn get_data(i2c: &I2c<I2C1>, rx_buffer: &mut [u8; RX_BUFFER_SIZE]) -> Result<(),
}
}
}
while i2c.stop().is_err() {}
//while i2c.stop().is_err() {}
// Checks the status of the sensor and passes the information on.
let status: u8 = (rx_buffer[0] & 0xC0) >> 6;
match status {
0 => Ok(()),
......@@ -270,7 +197,6 @@ fn get_data(i2c: &I2c<I2C1>, rx_buffer: &mut [u8; RX_BUFFER_SIZE]) -> Result<(),
_ => Err(Error::Unknown),
}
}
*/
fn idle() -> ! {
loop {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment