Skip to content

Commit

Permalink
[rp2040] Basic ADC support
Browse files Browse the repository at this point in the history
  • Loading branch information
cocasema committed Aug 18, 2022
1 parent cceb801 commit 0695646
Show file tree
Hide file tree
Showing 6 changed files with 273 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
<td align="center">○</td>
<td align="center">✅</td>
<td align="center">○</td>
<td align="center"></td>
<td align="center"></td>
<td align="center">○</td>
<td align="center">✅</td>
<td align="center">✅</td>
Expand Down
4 changes: 4 additions & 0 deletions src/modm/board/feather_rp2040/board.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ struct SystemClock
static constexpr uint32_t UsbPLLMul = 40;
static constexpr uint32_t RefFrequency = XOSCFrequency;
static constexpr uint32_t UsbFrequency = PllUsbFrequency;
static constexpr uint32_t AdcFrequency = PllUsbFrequency;
static constexpr uint32_t SysFrequency = Frequency;
static constexpr uint32_t PeriFrequency = SysFrequency;

Expand All @@ -59,6 +60,9 @@ struct SystemClock
// CLK USB = PLL USB (48MHz) / 1 = 48MHz
ClockControl::configureClock<ClockControl::Clock::Usb, ClockControl::ClockSrc::PllUsb,
PllUsbFrequency, UsbFrequency>();
// CLK ADC = PLL USB (48MHZ) / 1 = 48MHz
ClockControl::configureClock<ClockControl::Clock::Adc, ClockControl::ClockSrc::PllUsb,
PllUsbFrequency, AdcFrequency>();
// CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select
// and enable Normally choose clk_sys or clk_usb
ClockControl::configureClock<ClockControl::Clock::Peri, ClockControl::ClockSrc::Sys,
Expand Down
5 changes: 5 additions & 0 deletions src/modm/board/rp_pico/board.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
*
* Copyright (c) 2022, Andrey Kunitsyn
* Copyright (c) 2022, Nikolay Semenov
*
* This file is part of the modm project.
*
Expand Down Expand Up @@ -33,6 +34,7 @@ struct SystemClock
static constexpr uint32_t UsbPLLMul = 40;
static constexpr uint32_t RefFrequency = XOSCFrequency;
static constexpr uint32_t UsbFrequency = PllUsbFrequency;
static constexpr uint32_t AdcFrequency = PllUsbFrequency;
static constexpr uint32_t SysFrequency = Frequency;
static constexpr uint32_t PeriFrequency = SysFrequency;

Expand All @@ -56,6 +58,9 @@ struct SystemClock
// CLK USB = PLL USB (48MHz) / 1 = 48MHz
ClockControl::configureClock<ClockControl::Clock::Usb, ClockControl::ClockSrc::PllUsb,
PllUsbFrequency, UsbFrequency>();
// CLK ADC = PLL USB (48MHZ) / 1 = 48MHz
ClockControl::configureClock<ClockControl::Clock::Adc, ClockControl::ClockSrc::PllUsb,
PllUsbFrequency, AdcFrequency>();
// CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select
// and enable Normally choose clk_sys or clk_usb
ClockControl::configureClock<ClockControl::Clock::Peri, ClockControl::ClockSrc::Sys,
Expand Down
98 changes: 98 additions & 0 deletions src/modm/platform/adc/rp/adc.hpp.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright (c) 2022, Nikolay Semenov
*
* 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/.
*/
// ----------------------------------------------------------------------------

#pragma once

#include <modm/architecture/interface/adc.hpp>
#include <modm/platform/gpio/connector.hpp>

namespace modm::platform
{
/**
* Analog/Digital-Converter module.
*
* Not implemented yet:
* - Interrupts
* - FIFO
* - DMA
*
* \ingroup modm_platform_adc
*/
class Adc : public modm::Adc
{
public:
static constexpr uint8_t Resolution = 12;

public:
enum class Channel : uint8_t
{
%% for channel, name in channels.items() | sort
{{ name }} = {{ channel }},
%% endfor
};

public:
// start inherited documentation
template< class... Signals >
static void
connect()
{
using Connector = GpioConnector<Peripheral::Adc{{ id }}, Signals...>;
Connector::connect();
}

static inline void
initialize();

static inline void
disable();

static inline bool
setChannel(Channel);

static inline Channel
getChannel();

static inline bool
isReady();

static inline bool
hasError();

static inline uint16_t
getValue();

static inline uint16_t
readChannel(Channel);

static inline void
enableTemperatureSensor();

static inline void
disableTemperatureSensor();

static inline float
convertToVoltage(uint16_t value);

static inline float
convertToTemperature(uint16_t value);

private:
static inline void
reset();

static inline void
unreset();
};

} // namespace modm::platform

#include "adc_impl.hpp"
120 changes: 120 additions & 0 deletions src/modm/platform/adc/rp/adc_impl.hpp.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* Copyright (c) 2022, Nikolay Semenov
*
* 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/.
*/
// ----------------------------------------------------------------------------

#pragma once

#include <modm/platform/core/resets.hpp>

#include <hardware/structs/adc.h>

namespace modm::platform
{

void
Adc::initialize()
{
reset();
unreset();

adc_hw->cs = ADC_CS_EN_BITS;

while (!isReady()) { __NOP(); }
}

void
Adc::disable()
{
// TODO: wait for the current conversion to finish
adc_hw->cs = ADC_CS_EN_RESET;
}

bool
Adc::setChannel(Channel channel)
{
if (static_cast<uint32_t>(channel) > {{channels | max}}) return false;

hw_write_masked(&adc_hw->cs, static_cast<uint32_t>(channel) << ADC_CS_AINSEL_LSB, ADC_CS_AINSEL_BITS);
return true;
}

Adc::Channel
Adc::getChannel()
{
return static_cast<Adc::Channel>((adc_hw->cs & ADC_CS_AINSEL_BITS) >> ADC_CS_AINSEL_LSB);
}

bool
Adc::isReady()
{
return !!(adc_hw->cs & ADC_CS_READY_BITS);
}

bool
Adc::hasError()
{
return !!(adc_hw->cs & ADC_CS_ERR_BITS);
}

uint16_t
Adc::getValue()
{
// START_ONCE is ignored if START_MANY is set
// and isReady() will always return false.
hw_clear_bits(&adc_hw->cs, ADC_CS_START_MANY_BITS);
hw_set_bits(&adc_hw->cs, ADC_CS_START_ONCE_BITS);
while (!isReady()) { __NOP(); }
return static_cast<uint16_t>(adc_hw->result);
}

uint16_t
Adc::readChannel(Channel channel)
{
if (!setChannel(channel)) return 0;
return getValue();
}

void
Adc::enableTemperatureSensor()
{
hw_set_bits(&adc_hw->cs, ADC_CS_TS_EN_BITS);
}

void
Adc::disableTemperatureSensor()
{
hw_clear_bits(&adc_hw->cs, ADC_CS_TS_EN_BITS);
}

float
Adc::convertToVoltage(uint16_t value)
{
return value * (3.3f / (1 << 12));
}

float
Adc::convertToTemperature(uint16_t value)
{
return 27.f - (convertToVoltage(value) - 0.706f) * 581;
}

void
Adc::reset()
{
Resets::reset(RESETS_RESET_ADC_BITS);
}

void
Adc::unreset()
{
Resets::unresetWait(RESETS_RESET_ADC_BITS);
}

} // namespace modm::platform
45 changes: 45 additions & 0 deletions src/modm/platform/adc/rp/module.lb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (c) 2022, Nikolay Semenov
#
# 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 = ":platform:adc"
module.description = "Analog-to-Digital Converter (ADC)"

def prepare(module, options):
device = options[":target"]
if not device.has_driver("adc:rp20*"):
return False

module.depends(
":architecture:adc",
":cmsis:device",
":platform:gpio")

return True

def build(env):
device = env[":target"]
target = device.identifier
driver = device.get_driver("adc")

properties = {}
properties["target"] = target
properties["id"] = driver.get("instance", [""])[0]

channels = {ch["id"]:ch["name"] for ch in driver.get("channel", [])}
properties["channels"] = channels

env.substitutions = properties
env.outbasepath = "modm/src/modm/platform/adc"

env.template("adc.hpp.in")
env.template("adc_impl.hpp.in")

0 comments on commit 0695646

Please sign in to comment.