diff --git a/examples/nucleo_f429zi/adc_ads868x/main.cpp b/examples/nucleo_f429zi/adc_ads868x/main.cpp new file mode 100644 index 0000000000..e0dfbfc5e6 --- /dev/null +++ b/examples/nucleo_f429zi/adc_ads868x/main.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021, Raphael Lehmann + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include +#include +#include + +using namespace Board; + +using SpiMaster = SpiMaster1; +using Mosi = GpioB5; +using Miso = GpioB4; +using Sck = GpioB3; +using Cs = GpioA4; +using Rst = GpioF12; + +using Ads868x = modm::Ads868x; + +Ads868x adc{}; + +int +main() +{ + Board::initialize(); + Leds::setOutput(); + + MODM_LOG_INFO << "ADS868X Demo on Nucleo-F429ZI\n" << modm::endl; + + SpiMaster::initialize(); + SpiMaster::connect(); + + Rst::setOutput(); + Cs::setOutput(true); + + MODM_LOG_INFO << "Initializing ADC..." << modm::endl; + adc.initialize(); + + uint32_t counter(0); + + while (true) + { + Leds::write(counter % ((1 << (Leds::width+1)) - 1)); + + uint16_t result = RF_CALL_BLOCKING(adc.singleConversion()); + MODM_LOG_INFO.printf("ADC Manual: %05u\n", result); + + modm::delay(Button::read() ? 1ms : 500ms); + } + + return 0; +} diff --git a/examples/nucleo_f429zi/adc_ads868x/project.xml b/examples/nucleo_f429zi/adc_ads868x/project.xml new file mode 100644 index 0000000000..365e55e9b7 --- /dev/null +++ b/examples/nucleo_f429zi/adc_ads868x/project.xml @@ -0,0 +1,11 @@ + + modm:nucleo-f429zi + + + + + modm:build:scons + modm:platform:spi:1 + modm:driver:ads868x + + diff --git a/src/modm/driver/adc/ads868x.hpp b/src/modm/driver/adc/ads868x.hpp new file mode 100644 index 0000000000..60759267ac --- /dev/null +++ b/src/modm/driver/adc/ads868x.hpp @@ -0,0 +1,285 @@ +// coding: utf-8 +// ---------------------------------------------------------------------------- +/* + * Copyright (c) 2020, Vivien Henry + * Copyright (c) 2023, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_ADS868x_HPP +#define MODM_ADS868x_HPP + + +#include +#include +#include +#include +#include + +namespace modm +{ + +/// @ingroup modm_driver_ads868x +struct ads868x +{ + enum class + Register: uint8_t + { + DEV_ID_REG = 0x00, + RST_PWRCTRL_REG = 0x04, + SDI_CTL_REG = 0x08, + SDO_CTL_REG = 0x0C, + DATAOUT_CTL_REG = 0x10, + RANGE_CTL_REG = 0x14, + ALARM_REG = 0x20, + ALARM_H_TH_REG = 0x24, + ALARM_L_TH_REG = 0x28, + }; + + + /// Device ID register + enum class + DeviceIDRegister : uint32_t + { + Addr0 = Bit16, + Addr1 = Bit17, + Addr2 = Bit18, + Addr3 = Bit19 + }; + MODM_FLAGS32(DeviceIDRegister); + + typedef modm::Value DeviceID_t; + + + // Reset and Power Control register + enum class + ResetPowerControlRegister: uint32_t + { + WKey0 = Bit8, + WKey1 = Bit9, + WKey2 = Bit10, + WKey3 = Bit11, + WKey4 = Bit12, + WKey5 = Bit13, + WKey6 = Bit14, + WKey7 = Bit15, + + VDDAlarmDisable = Bit5, + + InputAlarmDisable = Bit4, + RSTn_ApplicationReset = Bit2, + NapModeEnable = Bit1, + PowerDownEnable = Bit0 + }; + MODM_FLAGS32(ResetPowerControlRegister); + + typedef modm::Value WriteKey_t; + + // SDI Data Input Control register + enum class + SDIControlRegister: uint32_t + { + SDIMode0 = Bit0, + SDIMode1 = Bit1 + }; + MODM_FLAGS32(SDIControlRegister); + + enum class + SDIMode : uint8_t + { + Std_Pol0_Phase0 = 0, + Std_Pol0_Phase1 = 1, + Std_Pol1_Phase0 = 2, + Std_Pol1_Phase1 = 3 + }; + typedef modm::Configuration SDIMode_t; + + // SDO Data Output Control Register + enum class + SDOControlRegister: uint32_t + { + SDOMode0 = Bit0, + SDOMode1 = Bit1, + + SSyncClock = Bit6, + + SDO1Config0 = Bit8, + SDO1Config1 = Bit9, + + GPOValue = Bit12 + }; + MODM_FLAGS32(SDOControlRegister); + + enum class + SDOMode: uint8_t + { + SameAsSDI0 = 0b00, + SameAsSDI1 = 0b01, + Invalid = 0b10, + ADCMasterClk_SourcSync = 0b11 + }; + typedef modm::Configuration SDOMode_t; + + + enum class + SourceSyncClock: uint8_t + { + External = 0, + Internal = 1 + }; + typedef modm::Configuration SourceSyncClock_t; + + enum class + SDO1Config: uint8_t + { + SDO1_Tristated = 0b00, + SDO1_Alarm = 0b01, + SDO1_GPO = 0b10, + SDO1_2BitsSDO = 0b11 + }; + typedef modm::Configuration SDO1Config_t; + + + enum class + DataOutControlRegister: uint32_t + { + DataVal0 = Bit0, + DataVal1 = Bit1, + DataVal2 = Bit2, + + ParityEnable = Bit3, + + Inc_Range = Bit8, + + Inc_InActiveAlarm_High = Bit10, + Inc_InActiveAlarm1_Low = Bit11, + + Inc_VDDActiveAlarm0_High = Bit12, + Inc_VDDActiveAlarm1_Low = Bit13, + + Inc_DeviceAddr = Bit14 + }; + MODM_FLAGS32(DataOutControlRegister); + + enum class + DataValue: uint8_t + { + ConversionData = 0b000, + All0 = 0b100, + All1 = 0b101, + Seq01 = 0b110, + Seq0011 = 0b111 + }; + typedef modm::Configuration DataValue_t; + + + enum class + RangeSelectionRegister: uint32_t + { + RangeSel0 = Bit0, + RangeSel1 = Bit1, + RangeSel2 = Bit2, + RangeSel3 = Bit3, + + InternalRefDisabled = Bit6 + }; + MODM_FLAGS32(RangeSelectionRegister); + + enum class + RangeSel: uint8_t + { + Range_Bipolar_3_000_VRef = 0b0000, + Range_Bipolar_2_500_VRef = 0b0001, + Range_Bipolar_1_500_VRef = 0b0010, + Range_Bipolar_1_250_VRef = 0b0011, + Range_Bipolar_0_625_VRef = 0b0100, + Range_Unipolar_3_00_VRef = 0b1000, + Range_Unipolar_2_50_VRef = 0b1001, + Range_Unipolar_1_50_VRef = 0b1010, + Range_Unipolar_1_25_VRef = 0b1011 + }; + typedef modm::Configuration RangeSel_t; + +}; // struct ads868x + +/** + * @tparam SpiMaster SpiMaster interface + * @tparam Cs Chip-select pin + * + * @author Vivien Henry + * @ingroup modm_driver_ads868x + */ +template +class Ads868x : public ads868x, public modm::SpiDevice, protected modm::NestedResumable<3> +{ +public: + /// Call this function once before using the device + void + initialize(); + + modm::ResumableResult + singleConversion(); + + modm::ResumableResult + setDeviceAddress(uint8_t devID) + { + return writeRegister(Register::DEV_ID_REG, DeviceID_t(devID).value); + }; + + modm::ResumableResult + getDeviceAddress() + { + RF_BEGIN(); + buffer32 = RF_CALL(readRegister(Register::DEV_ID_REG)); + RF_END_RETURN(DeviceID_t::get(DeviceIDRegister_t(buffer32))); + } + + modm::ResumableResult + setOutputProtocol(SDOMode mode, SourceSyncClock syncClock, SDO1Config sdo1Config) + { + RF_BEGIN(); + { + SDOControlRegister_t sdo_reg = SDOMode_t(mode) | SourceSyncClock_t(syncClock) | SDO1Config_t(sdo1Config); + buffer32 = sdo_reg.value; + } + RF_END_RETURN_CALL(writeRegister(Register::SDO_CTL_REG, buffer32)); + } + + modm::ResumableResult + setRange(RangeSel range) + { + RF_BEGIN(); + buffer32 = RangeSel_t(range).value; + RF_END_RETURN_CALL(writeRegister(Register::RANGE_CTL_REG, buffer32)); + } + + modm::ResumableResult + getRange() + { + RF_BEGIN(); + buffer32 = RF_CALL(readRegister(Register::RANGE_CTL_REG)); + RF_END_RETURN(RangeSel_t::get(RangeSelectionRegister_t(buffer32))); + } + +private: + modm::ResumableResult + writeRegister(Register reg, uint32_t data); + + modm::ResumableResult + readRegister(Register reg); + + uint8_t buffer[6]; + uint32_t buffer32; +}; + +} // namespace modm + +#include "ads868x_impl.hpp" + +#endif // MODM_ADS868x_HPP diff --git a/src/modm/driver/adc/ads868x.lb b/src/modm/driver/adc/ads868x.lb new file mode 100644 index 0000000000..6807e5246c --- /dev/null +++ b/src/modm/driver/adc/ads868x.lb @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2018, Niklas Hauser +# +# This file is part of the modm project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# ----------------------------------------------------------------------------- + + +def init(module): + module.name = ":driver:ads868x" + module.description = """ +# ADS868x Driver. + +ADS868x 16-Bit, High-Speed, Single-Supply, SAR ADC Data Acquisition System +With Programmable, Bipolar Input Ranges + +https://www.ti.com/lit/ds/symlink/ads8685.pdf +""" + + +def prepare(module, options): + module.depends( + ":architecture:accessor", + ":architecture:gpio", + ":architecture:register", + ":processing:resumable", + ":architecture:spi.device") + return True + +def build(env): + env.outbasepath = "modm/src/modm/driver/adc" + env.copy("ads868x.hpp") + env.copy("ads868x_impl.hpp") diff --git a/src/modm/driver/adc/ads868x_impl.hpp b/src/modm/driver/adc/ads868x_impl.hpp new file mode 100644 index 0000000000..f27ff00dad --- /dev/null +++ b/src/modm/driver/adc/ads868x_impl.hpp @@ -0,0 +1,146 @@ +// coding: utf-8 +// ---------------------------------------------------------------------------- +/* + * Copyright (c) 2020, Vivien Henry + * Copyright (c) 2023, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_ADS868x_HPP +# error "Don't include this file directly! Use 'ads868x.hpp' instead." +#endif + +// ---------------------------------------------------------------------------- +namespace modm +{ + + +template +void +Ads868x::initialize() +{ + Cs::setOutput(modm::Gpio::Low); + + nReset::setOutput(modm::Gpio::Low); + modm::delay_ms(1); + nReset::set(); + + Cs::set(); +} + + +template +modm::ResumableResult +Ads868x::singleConversion() +{ + RF_BEGIN(); + + RF_WAIT_UNTIL(this->acquireMaster()); + Cs::reset(); + + RF_CALL(SpiMaster::transfer(nullptr, buffer, 2)); + + if (this->releaseMaster()) Cs::set(); + + RF_END_RETURN( uint16_t((uint16_t(buffer[0]) << 8) | buffer[1]) ); +} + + +template +modm::ResumableResult +Ads868x::writeRegister(Register reg, uint32_t data) +{ + RF_BEGIN(); + + // LSB (0-15) + buffer[0] = 0b1101'0000; + buffer[1] = uint8_t(reg); + buffer[2] = data >> 8; + buffer[3] = data; + + RF_WAIT_UNTIL(this->acquireMaster()); + Cs::reset(); + + RF_CALL(SpiMaster::transfer(buffer, nullptr, 4)); + + Cs::set(); + + // MSB (16-31) + buffer[0] = 0b1101'0000; + buffer[1] = uint8_t(reg) + 2; + buffer[2] = data >> 24; + buffer[3] = data >> 16; + + modm::delay_us(1); + Cs::reset(); + + RF_CALL(SpiMaster::transfer(buffer, nullptr, 4)); + + if (this->releaseMaster()) Cs::set(); + + RF_END(); +} + +template +modm::ResumableResult +Ads868x::readRegister(Register reg) +{ + RF_BEGIN(); + + uint32_t cmd = 0; + uint32_t data = 0; + uint8_t byte = 0; + + RF_WAIT_UNTIL(this->acquireMaster()); + Cs::reset(); + + // MSB (31-16) + buffer[0] = 0b1100'1000; + buffer[1] = uint8_t(reg) + 2; + buffer[2] = 0; + buffer[3] = 0; + + RF_CALL(SpiMaster::transfer(buffer, nullptr, 4)); + + Cs::set(); + modm::delay_us(1); + Cs::reset(); + + RF_CALL(SpiMaster::transfer(nullptr, buffer, 4)); + // cache the MSB values temporarily + buffer[4] = buffer[0]; + buffer[5] = buffer[1]; + + Cs::set(); + modm::delay_us(1); + Cs::reset(); + + // LSB (0-15) + buffer[0] = 0b1100'1000; + buffer[1] = uint8_t(reg); + buffer[2] = 0; + buffer[3] = 0; + + RF_CALL(SpiMaster::transfer(buffer, nullptr, 4)); + + Cs::set(); + modm::delay_us(1); + Cs::reset(); + + RF_CALL(SpiMaster::transfer(nullptr, buffer, 4)); + + if (this->releaseMaster()) Cs::set(); + + RF_END_RETURN((uint32_t(buffer[4]) << 24) | + (uint32_t(buffer[5]) << 16) | + (uint32_t(buffer[0]) << 8) | + uint32_t(buffer[1])); +} + +} // namespace modm