ASCII Serial Com
Serial communication library between computers, microcontrollers, FPGAs, etc. Uses only ASCII. Not the most efficient protocol, but meant to be easy to read
Loading...
Searching...
No Matches
arduino_uno_adc_streaming.c
Go to the documentation of this file.
1/** \file
2 *
3 * \brief Uses timer to set interval between ADC single conversions that are
4 * then sent as stream messages to host.
5 *
6 * On receiving 'n' message, begins streaming ADC values to host (by default the
7 * ADC is connected to ground) Stops when 'f' message is received.
8 *
9 * ADC channel selection and time between ADC conversions are configurable with
10 * registers.
11 *
12 * You can also turn on and off the user LED with register 0, bit 5
13 *
14 * Register map is documented at \ref register_map
15 *
16 */
17
18#include "asc_exception.h"
19#include "asc_helpers.h"
20#include "ascii_serial_com.h"
23#include "avr/avr_uart.h"
24#include "circular_buffer.h"
25#include "millisec_timer.h"
26
27#include <avr/interrupt.h>
28#include <avr/io.h>
29#include <util/atomic.h>
30
31#define F_CPU 16000000L
32#define BAUD 9600
33#define MYUBRR (F_CPU / 16 / BAUD - 1)
34#define UART_NO 0
35
36bool have_started_ADC_conversion = false;
37#define have_finished_ADC_conversion (!(ADCSRA & (1 << ADSC)))
38
39millisec_timer adc_timer;
40uint32_t adc_sample_period_ms = 1000;
41
42#define nRegs 10
43
44/** \brief Register Map
45 *
46 * Lower byte of the 16 bit variables is in lower register number
47 *
48 * |Register Number | Description | r/w | Default |
49 * | -------------- |------------ | --- | ------- |
50 * | 0 | PORTB, bit 5 is LED | r, bit 5 is w | 0 |
51 * | 1 | ADMUX | r, lower 4 bits w | 0x4F (ground) |
52 * | 2 | adc_sample_period_ms lowest 8 bits | r/w | 0xe8 (1000)|
53 * | 3 | adc_sample_period_ms bits 8-15 | r/w | 0x03 |
54 * | 4 | adc_sample_period_ms bits 16-23 | r/w | 0 |
55 * | 5 | adc_sample_period_ms bits 24-21 | r/w | 0 |
56 * | 6 | MILLISEC_TIMER_NOW lowest 8 bits | r | counting |
57 * | 7 | MILLISEC_TIMER_NOW bits 8-15 | r | counting |
58 * | 8 | MILLISEC_TIMER_NOW bits 16-23 | r | counting |
59 * | 9 | MILLISEC_TIMER_NOW bits 24-21 | r | counting |
60 *
61 * ADMUX: the upper half of the register is the read only ADC reference
62 * selection. The bottom 4 bits are r/w and are the channel selection.
63 * values of 0-7 select the ADC channel to read from and 0xF is GND. **Don't
64 * write any values besides 0-7 and 0xF, as it could cause issues.**
65 *
66 * adc_sample_period_ms: the time between ADC conversion
67 * in milliseconds. default: 1000 ms = 1 s
68 *
69 * @see register_write_masks
70 *
71 */
72volatile REGTYPE *register_map[nRegs] = {
73 &PORTB,
74 &ADMUX,
75 &((uint8_t *)(&adc_sample_period_ms))[0],
76 &((uint8_t *)(&adc_sample_period_ms))[1],
77 &((uint8_t *)(&adc_sample_period_ms))[2],
78 &((uint8_t *)(&adc_sample_period_ms))[3],
79 &((uint8_t *)(&MILLISEC_TIMER_NOW))[0],
80 &((uint8_t *)(&MILLISEC_TIMER_NOW))[1],
81 &((uint8_t *)(&MILLISEC_TIMER_NOW))[2],
82 &((uint8_t *)(&MILLISEC_TIMER_NOW))[3],
83};
84
85/** \brief Write masks for \ref register_map
86 *
87 * These define whether the given register in register_map is writable or not
88 *
89 */
90REGTYPE register_write_masks[nRegs] = {
91 1 << 5, // PORTB
92 0x0F, // ADMUX
93 0xFF, // adc_sample_period_ms
94 0xFF, // adc_sample_period_ms
95 0xFF, // adc_sample_period_ms
96 0xFF, // adc_sample_period_ms
97 0, // MILLISEC_TIMER_NOW
98 0, // MILLISEC_TIMER_NOW
99 0, // MILLISEC_TIMER_NOW
100 0, // MILLISEC_TIMER_NOW
101};
102
104
105CEXCEPTION_T e;
106
107uint16_t nExceptions;
108int main(void) {
109
110 DDRB |= 1 << 5;
111
113
114 // ADC
115 // ADMUX top 2 bits select reference. Default (0b00xxxxxx) is AREF--I think
116 // that means whatever is applied to the AREF pin
117 // 0b01xxxxxx selects AVCC (which is then connected to AREF so be careful
118 // with what's hooked there) 0b11xxxxxx selects internal 1.1V reference
119 // (which is then connected to AREF so be careful with what's hooked there)
120 // ADMUX bottom 4 bits select channels 0 through 8 as just ints
121 // ADMUX bottom 4 bits should be 0xF for ground and 0xE for bandgap (don't
122 // use) ADCSRA Status and control reg A bits:
123 // ADEN: enable
124 // ADSC: start conversion; in single shot mode stays 1 until conversion
125 // complete ADIE: interrupt enable for conversion complete: ADC_vect Bottom
126 // 2 bits are the ADC prescaler, they should divide the main clock to be
127 // between 50kHz and 200kHz
128 // for us at 16MHz, that means we need the /128 which is 0b111 = 7
129 // ADC data for 16 bit reads is at "ADC"
130 // DIDR0 diables digial inputs for ADC0-5 if they are being used
131 ADMUX = 0x40; // select AVCC as reference
132 ADMUX |= 0xF; // select ADC input
133 ADCSRA = 7; // set ADC clock prescaler
134 ADCSRA |= 1 << ADEN; // enable ADC
135
136 nExceptions = 0;
137
138 Try {
140 nRegs);
141 }
142 Catch(e) { return e; }
143
144 UART_Init(UART_NO, MYUBRR, 1);
145
146 sei();
147
148 while (true) {
149 Try {
150 const bool stream_state_before_receiving = streaming_is_on;
152 // just started streaming so start this timer
153 if (streaming_is_on && !stream_state_before_receiving) {
155 adc_sample_period_ms);
156 }
157
158 if (streaming_is_on && !have_started_ADC_conversion &&
160 ADCSRA |= 1 << ADSC; // start ADC conversion
161 have_started_ADC_conversion = true;
162 }
163 if (have_started_ADC_conversion && have_finished_ADC_conversion &&
165 const uint16_t adc_val = ADC;
166 char adc_val_buffer[4];
167 convert_uint16_to_hex(adc_val, adc_val_buffer, true);
169 have_started_ADC_conversion = false;
170 }
171 }
172 Catch(e) { nExceptions++; }
173 }
174
175 return 0;
176}
177
178def_usart_isr_push_rx_to_circ_buf(USART_RX_vect, UDR0, &extraInputBuffer)
179
REGTYPE register_write_masks[nRegs]
Write masks for register_map.
volatile REGTYPE * register_map[nRegs]
Register Map.
#define HANDLE_ASC_COMM_IN_POLLING_LOOP(uart_no)
Polling for ascii_serial_com_device and ascii_serial_com_register_pointers.
#define STREAM_TO_HOST_ASC_DEVICE_W_REGISTER_POINTERS(data, data_size)
Stream data to the host.
#define READY_TO_STREAM_ASC_DEVICE_W_REGISTER_POINTERS
Check if you should stream a message to the host.
#define DECLARE_ASC_DEVICE_W_REGISTER_POINTERS()
Declarations for ascii_serial_com_device and ascii_serial_com_register_pointers.
#define SETUP_ASC_DEVICE_W_REGISTER_POINTERS(register_map, register_write_masks, nRegs)
Setup for ascii_serial_com_device and ascii_serial_com_register_pointers.
void convert_uint16_to_hex(uint16_t num, char *outstr, bool caps)
convert uint16 to hex string
ASCII Serial Com Device.
ASCII Serial Com Register Pointers.
#define UART_Init(uart_no, ubrr, rxIntEnable)
Initialize USART.
Definition avr_uart.h:20
void millisec_timer_set_rel(millisec_timer *timer, const millisec_timer_unit_t now, const millisec_timer_unit_t rel)
Set timer to expire in the future.
bool millisec_timer_is_expired_repeat(millisec_timer *timer, const millisec_timer_unit_t now)
Check if timer has expired & if so, re-enable for the same interval.
Portable millisecond timer.
static uint32_t MILLISEC_TIMER_NOW
A counter that increments every millisecond.
#define MILLISEC_TIMER_AVR_TIMER0_ISR
Millisecond timer SysTick interrupt.
#define millisec_timer_avr_timer0_setup_16MHz()
Setup the systick timer.
#define def_usart_isr_push_rx_to_circ_buf(isr_name, usart, circular_buffer)
Define the ISR for a USART to push rx bytes to a circular buffer.
Definition stm_usart.h:95
portable millisecond timer