1+ #ifndef KLIB_NXP_LPC175X_ADC_HPP
2+ #define KLIB_NXP_LPC175X_ADC_HPP
3+
4+ #include < klib/klib.hpp>
5+ #include < klib/io/peripheral.hpp>
6+
7+ #include < io/power.hpp>
8+ #include < io/port.hpp>
9+ #include < io/clocks.hpp>
10+
11+ namespace klib ::core::lpc175x::io {
12+ template <typename Adc>
13+ class adc {
14+ public:
15+ // amount of bits in the ADC
16+ constexpr static uint32_t bits = 12 ;
17+
18+ /* *
19+ * @brief Start sampling the input pins
20+ *
21+ * @note conversions require 65 clock
22+ * cycles
23+ */
24+ static void sample () {
25+ // check if burst mode is enabled. If it is we
26+ // do nothing here
27+ if (Adc::port->CR & (0x1 << 16 )) {
28+ // do nothing here
29+ return ;
30+ }
31+
32+ // start a conversion now
33+ Adc::port->CR = (Adc::port->CR & (~(0b111 << 24 ))) | (0b001 << 24 );
34+ }
35+
36+ /* *
37+ * @brief Returns if the adc is busy sampling. Only valid after starting
38+ * a sample request.
39+ *
40+ * @warning Undefined value is returned if calling this function before
41+ * starting a measurement
42+ *
43+ * @note This value is cleared after reading
44+ *
45+ * @return status
46+ */
47+ static bool is_busy () {
48+ return !(Adc::port->GDR & (0x1 << 31 ));
49+ }
50+
51+ /* *
52+ * @brief Init the adc peripheral
53+ *
54+ * @tparam FreeRun
55+ * @tparam Divider
56+ */
57+ template <bool FreeRun = false , uint8_t Divider = 0 >
58+ static void init () {
59+ // enable power to the adc
60+ target::io::power_control::enable<Adc>();
61+
62+ // make the adc operational by setting the pdn
63+ // bit. FreeRun enables burst mode. That will
64+ // allow conversions up to 200 kHz
65+ Adc::port->CR = (
66+ (Divider << 8 ) | (0x1 << 21 ) | (FreeRun << 16 )
67+ );
68+ }
69+ };
70+
71+ template <typename Adc, typename Pin>
72+ class adc_channel {
73+ public:
74+ // amount of bits in the ADC
75+ constexpr static uint32_t bits = adc<Adc>::bits;
76+
77+ /* *
78+ * @brief Init the adc channel for a specific pin
79+ *
80+ */
81+ static void init () {
82+ static_assert (Pin::analog_number < 8 , " Invalid analog pin number" );
83+ using pin = std::tuple_element<klib::io::peripheral::get_index<Pin, typename Adc::pins>(), typename Adc::pins>::type;
84+
85+ // switch the gpio to adc mode
86+ target::io::detail::pins::set_peripheral<typename pin::pin, typename pin::periph>();
87+
88+ // init the channel
89+ Adc::port->CR |= (0x1 << Pin::analog_number);
90+ }
91+
92+ /* *
93+ * @brief Do a sample request.
94+ *
95+ */
96+ static void sample () {
97+ // Note: we sample all the enabled channels with this request
98+ adc<Adc>::sample ();
99+ }
100+
101+ /* *
102+ * @brief Returns if the adc is busy sampling. Only valid after starting
103+ * a sample request.
104+ *
105+ * @warning Undefined value is returned if calling this function before
106+ * starting a measurement
107+ *
108+ * @return status
109+ */
110+ static bool is_busy () {
111+ return !(Adc::port->STAT & (0x1 << Pin::analog_number));
112+ }
113+
114+ /* *
115+ * @brief Read a sampled result. If the async flag is set to false this will
116+ * sample and wait until the conversion is complete
117+ *
118+ * @tparam Async
119+ * @tparam Override
120+ * @return uint32_t
121+ */
122+ template <bool Async = true >
123+ static uint32_t get () {
124+ // check if we need to sample the data or if we only
125+ // need to read the result
126+ if constexpr (!Async) {
127+ // start a measurement
128+ sample ();
129+
130+ // wait until the current channel is done
131+ while (is_busy ()) {
132+ // do nothing
133+ }
134+ }
135+
136+ // return the result
137+ return (Adc::port->DR [Pin::analog_number] >> 4 ) & 0xfff ;
138+ }
139+ };
140+ }
141+
142+ #endif
0 commit comments