diff --git a/.gdbinit b/.gdbinit index 4220eee7531bcd83fb6572eee3d7e243a4171bb6..65612d6bd1124aecd639a4cc1b4f7dd52c1f77d6 100644 --- a/.gdbinit +++ b/.gdbinit @@ -1,5 +1,6 @@ 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 diff --git a/Cargo.toml b/Cargo.toml index 479081e4197e7ccb11ed8b322582c4046f4fe5b9..211507c5a7b8b254c8f6a9a9e1a6a67576f322c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/main.rs b/src/main.rs index f61c50916173d6eadf55fbc4631ffb64ef1bae5e..7fbf1ce8cfd72b297495596053220e4d3a5f109e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,102 +1,72 @@ -//! 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 - }, - DMA1_STREAM6: { - path: tx_done, - priority: 2, - resources: [TX_BUFFER, DMA1], + resources: [USART2, TEMPERATURE, HUMIDITY], }, - /*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()); - }); - }); - // 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(); - } - } - }); - // 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"); +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; + }); + r.HUMIDITY.claim(_t, |rh, _| { + _rh = **rh; + }); + + 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 {