Commit 01c302af authored by Pedro Henrique Kopper's avatar Pedro Henrique Kopper

Add bare support for ADS1115

parent 62ecb0ee
......@@ -124,8 +124,9 @@ CSRC = $(ALLCSRC) \
$(TESTSRC) \
$(CHIBIOS)/os/various/syscalls.c \
$(wildcard ./lib/ssd1306/*.c) \
$(wildcard ./lib/libscpi/*.c) \
$(wildcard ./threads/*.c) \
$(wildcard ./lib/libscpi/*.c) \
$(wildcard ./lib/ads1115/*.c) \
$(wildcard ./threads/*.c) \
./usb/usbcfg.c \
main.c
......@@ -140,7 +141,7 @@ ASMSRC = $(ALLASMSRC)
ASMXSRC = $(ALLXASMSRC)
# Inclusion directories.
INCDIR = $(CONFDIR) $(ALLINC) $(TESTINC) ./lib/libscpi ./lib/ssd1306 ./threads
INCDIR = $(CONFDIR) $(ALLINC) $(TESTINC) ./lib/libscpi ./lib/ssd1306 ./lib/ads1115 ./threads
# Define C warning options here.
CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes
......
/*
* =====================================================================================
*
* Filename: ads1115.c
*
* Description: ADS1115 ChibiOS device driver
*
* Version: 1.0
* Created: 07/18/2019 11:42:23 AM
* Revision: none
* Compiler: gcc
*
* Author: Pedro Henrique Capp Kopper (pedro.kopper@ufrgs.br),
* Organization: Universidade Federal do Rio Grande do Sul
*
* =====================================================================================
*/
#include "hal.h"
#include "ch.h"
#include <stdio.h>
#include "ads1115.h"
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
uint16_t getSingleEndedConfigBitsForMUX(uint8_t channel) {
uint16_t c = 0;
switch (channel)
{
case (0):
c = ADS1115_REG_CONFIG_MUX_SINGLE_0;
break;
case (1):
c = ADS1115_REG_CONFIG_MUX_SINGLE_1;
break;
case (2):
c = ADS1115_REG_CONFIG_MUX_SINGLE_2;
break;
case (3):
c = ADS1115_REG_CONFIG_MUX_SINGLE_3;
break;
}
return c;
}
float voltsPerBit(void *ip) {
const ADS1115Driver *drvp = (const ADS1115Driver *)ip;
switch(drvp->scale) {
case ADS1115_REG_CONFIG_PGA_6_144V:
return ADS1115_VOLTS_PER_BIT_GAIN_TWOTHIRDS;
case ADS1115_REG_CONFIG_PGA_4_096V:
return ADS1115_VOLTS_PER_BIT_GAIN_ONE;
case ADS1115_REG_CONFIG_PGA_2_048V:
return ADS1115_VOLTS_PER_BIT_GAIN_TWO;
case ADS1115_REG_CONFIG_PGA_1_024V:
return ADS1115_VOLTS_PER_BIT_GAIN_FOUR;
case ADS1115_REG_CONFIG_PGA_0_512V:
return ADS1115_VOLTS_PER_BIT_GAIN_EIGHT;
case ADS1115_REG_CONFIG_PGA_0_256V:
return ADS1115_VOLTS_PER_BIT_GAIN_SIXTEEN;
}
return -1;
}
static msg_t write16bits(void *ip, uint8_t reg, uint16_t val) {
const ADS1115Driver *drvp = (const ADS1115Driver *)ip;
msg_t ret;
uint8_t txbuf[] = { reg, (val >> 8), (val & 0xFF) };
palClearLine(LINE_LED0);
i2cAcquireBus(drvp->config->i2cp);
i2cStart(drvp->config->i2cp, drvp->config->i2ccfg);
palClearLine(LINE_LED1);
ret = i2cMasterTransmitTimeout(drvp->config->i2cp, drvp->config->address,
txbuf, sizeof(txbuf), NULL, 0, TIME_INFINITE);
palClearLine(LINE_LED2);
i2cReleaseBus(drvp->config->i2cp);
return ret;
}
static uint16_t read16bits(void *ip, uint8_t reg) {
const ADS1115Driver *drvp = (const ADS1115Driver *)ip;
uint8_t regb[] = {reg};
i2cAcquireBus(drvp->config->i2cp);
i2cStart(drvp->config->i2cp, drvp->config->i2ccfg);
i2cMasterTransmitTimeout(drvp->config->i2cp, drvp->config->address, regb, sizeof(regb), NULL, 0, TIME_INFINITE);
uint8_t buf[2] = {0};
i2cMasterReceiveTimeout(drvp->config->i2cp, drvp->config->address, buf, sizeof(buf), TIME_INFINITE);
i2cReleaseBus(drvp->config->i2cp);
return ((((uint16_t) buf[0]) << 8) | (buf[1]));
}
void setScale(void *ip, uint16_t scale) {
ADS1115Driver *drvp = (ADS1115Driver *)ip;
drvp->scale = scale;
}
void setRate(void *ip, uint16_t rate) {
ADS1115Driver *drvp = (ADS1115Driver *)ip;
drvp->rate = rate;
}
float readMillivolts(void *ip, uint8_t channel) {
const ADS1115Driver *drvp = (const ADS1115Driver *)ip;
uint16_t config = ADS1115_REG_CONFIG_CQUE_NONE | // Disable the comparator (default val)
ADS1115_REG_CONFIG_CLAT_NONLAT | // Non-latching (default val)
ADS1115_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low (default val)
ADS1115_REG_CONFIG_CMODE_TRAD | // Traditional comparator (default val)
ADS1115_REG_CONFIG_MODE_SINGLE; // Single-shot mode (default)
config |= drvp->scale;
config |= drvp->rate;
config |= getSingleEndedConfigBitsForMUX(channel);
config |= ADS1115_REG_CONFIG_OS_SINGLE; // Trigger conversion
write16bits(ip, ADS1115_REG_POINTER_CONFIG, config);
chThdSleepMilliseconds(1); // Wait for conversion to start
while(ADS1115_REG_CONFIG_OS_BUSY == (read16bits(ip, ADS1115_REG_POINTER_CONFIG) & ADS1115_REG_CONFIG_OS_MASK)) {
chThdSleepMilliseconds(1);
}
(void)chMsgSend(chRegFindThreadByName("status"), (msg_t)"Reading\r\n");
uint16_t res = read16bits(ip, ADS1115_REG_POINTER_CONVERT);
return ((int16_t) res) * voltsPerBit(ip);
}
static const struct ADS1115VMT vmt_ads1115 = {
setScale, setRate, readMillivolts
};
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
void ads1115ObjectInit(ADS1115Driver *devp) {
devp->vmt = &vmt_ads1115;
devp->config = NULL;
devp->state = ADS1115_STOP;
}
void ads1115Start(ADS1115Driver *devp, const ADS1115Config *config) {
chDbgAssert((devp->state == ADS1115_STOP) || (devp->state == ADS1115_READY),
"ads1115Start(), invalid state");
chDbgCheck((devp != NULL) && (config != NULL));
devp->config = config;
setRate((void *) devp, ADS1115_REG_CONFIG_DR_128SPS);
setScale((void *) devp, ADS1115_REG_CONFIG_PGA_4_096V);
devp->state = ADS1115_READY;
}
void ads1115Stop(ADS1115Driver *devp) {
(void)devp;
}
#ifndef __ADS1115_H__
#define __ADS1115_H__
#include "hal.h"
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
#if !HAL_USE_I2C
#error "ADS1115 requires HAL_USE_I2C"
#endif
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
// From: https://github.com/soligen2010/Adafruit_ADS1115/blob/master/Adafruit_ADS1015.h
/*=========================================================================
I2C ADDRESS/BITS
-----------------------------------------------------------------------*/
#define ADS1115_ADDRESS (0x48) // 1001 000 (ADDR = GND)
/*=========================================================================*/
/*=========================================================================
Default thresholds for comparator
-----------------------------------------------------------------------*/
#define ADS1115_LOW_THRESHOLD_DEFAULT (0x8000)
#define ADS1115_HIGH_THRESHOLD_DEFAULT (0x7FFF)
/*=========================================================================*/
/*=========================================================================
POINTER REGISTER
-----------------------------------------------------------------------*/
#define ADS1115_REG_POINTER_MASK (0x03)
#define ADS1115_REG_POINTER_CONVERT (0x00)
#define ADS1115_REG_POINTER_CONFIG (0x01)
#define ADS1115_REG_POINTER_LOWTHRESH (0x02)
#define ADS1115_REG_POINTER_HITHRESH (0x03)
/*=========================================================================*/
/*=========================================================================
CONFIG REGISTER
-----------------------------------------------------------------------*/
#define ADS1115_REG_CONFIG_OS_MASK (0x8000)
#define ADS1115_REG_CONFIG_OS_SINGLE (0x8000) // Write: Set to start a single-conversion
#define ADS1115_REG_CONFIG_OS_BUSY (0x0000) // Read: Bit = 0 when conversion is in progress
#define ADS1115_REG_CONFIG_OS_NOTBUSY (0x8000) // Read: Bit = 1 when device is not performing a conversion
#define ADS1115_REG_CONFIG_MUX_MASK (0x7000)
#define ADS1115_REG_CONFIG_MUX_DIFF_0_1 (0x0000) // Differential P = AIN0, N = AIN1 (default)
#define ADS1115_REG_CONFIG_MUX_DIFF_0_3 (0x1000) // Differential P = AIN0, N = AIN3
#define ADS1115_REG_CONFIG_MUX_DIFF_1_3 (0x2000) // Differential P = AIN1, N = AIN3
#define ADS1115_REG_CONFIG_MUX_DIFF_2_3 (0x3000) // Differential P = AIN2, N = AIN3
#define ADS1115_REG_CONFIG_MUX_SINGLE_0 (0x4000) // Single-ended AIN0
#define ADS1115_REG_CONFIG_MUX_SINGLE_1 (0x5000) // Single-ended AIN1
#define ADS1115_REG_CONFIG_MUX_SINGLE_2 (0x6000) // Single-ended AIN2
#define ADS1115_REG_CONFIG_MUX_SINGLE_3 (0x7000) // Single-ended AIN3
#define ADS1115_REG_CONFIG_PGA_MASK (0x0E00)
#define ADS1115_REG_CONFIG_PGA_6_144V (0x0000) // +/-6.144V range = Gain 2/3
#define ADS1115_REG_CONFIG_PGA_4_096V (0x0200) // +/-4.096V range = Gain 1
#define ADS1115_REG_CONFIG_PGA_2_048V (0x0400) // +/-2.048V range = Gain 2 (default)
#define ADS1115_REG_CONFIG_PGA_1_024V (0x0600) // +/-1.024V range = Gain 4
#define ADS1115_REG_CONFIG_PGA_0_512V (0x0800) // +/-0.512V range = Gain 8
#define ADS1115_REG_CONFIG_PGA_0_256V (0x0A00) // +/-0.256V range = Gain 16
#define ADS1115_REG_CONFIG_MODE_MASK (0x0100)
#define ADS1115_REG_CONFIG_MODE_CONTIN (0x0000) // Continuous conversion mode
#define ADS1115_REG_CONFIG_MODE_SINGLE (0x0100) // Power-down single-shot mode (default)
#define ADS1115_REG_CONFIG_DR_MASK (0x00E0)
#define ADS1115_REG_CONFIG_DR_8SPS (0x0000) // 8 samples per second
#define ADS1115_REG_CONFIG_DR_16SPS (0x0020) // 16 samples per second
#define ADS1115_REG_CONFIG_DR_32SPS (0x0040) // 32 samples per second
#define ADS1115_REG_CONFIG_DR_64SPS (0x0060) // 64 samples per second
#define ADS1115_REG_CONFIG_DR_128SPS (0x0080) // 128 samples per second (default)
#define ADS1115_REG_CONFIG_DR_250SPS (0x00A0) // 250 samples per second
#define ADS1115_REG_CONFIG_DR_475SPS (0x00C0) // 475 samples per second
#define ADS1115_REG_CONFIG_DR_860SPS (0x00E0) // 860 samples per second
#define ADS1115_REG_CONFIG_CMODE_MASK (0x0010)
#define ADS1115_REG_CONFIG_CMODE_TRAD (0x0000) // Traditional comparator with hysteresis (default)
#define ADS1115_REG_CONFIG_CMODE_WINDOW (0x0010) // Window comparator
#define ADS1115_REG_CONFIG_CPOL_MASK (0x0008)
#define ADS1115_REG_CONFIG_CPOL_ACTVLOW (0x0000) // ALERT/RDY pin is low when active (default)
#define ADS1115_REG_CONFIG_CPOL_ACTVHI (0x0008) // ALERT/RDY pin is high when active
#define ADS1115_REG_CONFIG_CLAT_MASK (0x0004) // Determines if ALERT/RDY pin latches once asserted
#define ADS1115_REG_CONFIG_CLAT_NONLAT (0x0000) // Non-latching comparator (default)
#define ADS1115_REG_CONFIG_CLAT_LATCH (0x0004) // Latching comparator
#define ADS1115_REG_CONFIG_CQUE_MASK (0x0003)
#define ADS1115_REG_CONFIG_CQUE_1CONV (0x0000) // Assert ALERT/RDY after one conversions
#define ADS1115_REG_CONFIG_CQUE_2CONV (0x0001) // Assert ALERT/RDY after two conversions
#define ADS1115_REG_CONFIG_CQUE_4CONV (0x0002) // Assert ALERT/RDY after four conversions
#define ADS1115_REG_CONFIG_CQUE_NONE (0x0003) // Disable the comparator and put ALERT/RDY in high state (default)
/*=========================================================================*/
/*=========================================================================
GAIN VOLTAGES
-----------------------------------------------------------------------*/
#define ADS1115_VOLTS_PER_BIT_GAIN_TWOTHIRDS 0.0001875F
#define ADS1115_VOLTS_PER_BIT_GAIN_ONE 0.000125F
#define ADS1115_VOLTS_PER_BIT_GAIN_TWO 0.0000625F
#define ADS1115_VOLTS_PER_BIT_GAIN_FOUR 0.00003125F
#define ADS1115_VOLTS_PER_BIT_GAIN_EIGHT 0.000015625F
#define ADS1115_VOLTS_PER_BIT_GAIN_SIXTEEN 0.0000078125F
/*=========================================================================*/
#define _ads1115_methods \
void (*setScale)(void *ip, uint16_t scale); \
void (*setRate)(void *ip, uint16_t rate); \
float (*readMillivolts)(void *ip, uint8_t channel);
struct ADS1115VMT {
_ads1115_methods
};
typedef enum {
ADS1115_UNINIT = 0,
ADS1115_STOP = 1,
ADS1115_READY = 2,
} ads1115_state_t;
typedef struct {
I2CDriver *i2cp;
const I2CConfig *i2ccfg;
uint8_t address;
} ADS1115Config;
#define _ads1115_data \
ads1115_state_t state; \
const ADS1115Config *config; \
uint16_t scale; \
uint16_t rate;
typedef struct ADS1115Driver {
const struct ADS1115VMT *vmt;
_ads1115_data;
} ADS1115Driver;
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
#define ads1115SetScale(ip, scale) \
(ip)->vmt->setScale(ip, scale)
#define ads1115SetRate(ip, rate) \
(ip)->vmt->setRate(ip, rate)
#define ads1115ReadMillivolts(ip, channel) \
(ip)->vmt->readMillivolts(ip, channel)
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#ifdef __cplusplus
extern "C" {
#endif
void ads1115ObjectInit(ADS1115Driver *devp);
void ads1115Start(ADS1115Driver *devp, const ADS1115Config *config);
void ads1115Stop(ADS1115Driver *devp);
#ifdef __cplusplus
}
#endif
#endif
......@@ -34,16 +34,33 @@ static char scpi_input_buffer[SCPI_INPUT_BUFFER_LENGTH];
scpi_t scpi_context;
scpi_choice_def_t trigger_source[] = {
{"A0", 0},
{"A1", 1},
{"A2", 2},
{"A3", 3},
{"A4", 4},
SCPI_CHOICE_LIST_END /* termination of option list */
};
static size_t scpi_write(scpi_t * context, const char * data, size_t len) {
(void) context;
return chnWrite(COMM, (const uint8_t*) data, len);
}
static scpi_result_t scpi_measure_voltage(scpi_t * context) {
SCPI_ResultDouble(context, 3.14);
int32_t param;
if (!SCPI_ParamChoice(context, trigger_source, &param, TRUE)) {
return SCPI_RES_ERR;
}
float reading = (float) chMsgSend(chRegFindThreadByName("sensors"), (msg_t)param);
SCPI_ResultDouble(context, reading);
return SCPI_RES_OK;
}
static scpi_command_t scpi_commands[] = {
{ .pattern = "*IDN?", .callback = SCPI_CoreIdnQ,},
{ .pattern = "*RST", .callback = SCPI_CoreRst,},
......@@ -80,8 +97,7 @@ THD_FUNCTION(Comm, arg) {
/*char buf2[16] = {0};
sprintf(buf2, "Tecla: %c", buf);
(void)chMsgSend(chRegFindThreadByName("display"), (msg_t)buf2);*/
//adcsample_t *reading = (adcsample_t *) chMsgSend(chRegFindThreadByName("sensors"), (msg_t)0);
//chprintf((BaseSequentialStream *)COMM, "%d\r\n", reading[0]);
// //chprintf((BaseSequentialStream *)COMM, "%d\r\n", reading[0]);
SCPI_Input(&scpi_context, &buf, 1);
}
}
......@@ -2,6 +2,7 @@
#include "hal.h"
#include "board.h"
#include "ads1115.h"
#include "sensors.h"
#include <stdio.h>
......@@ -33,20 +34,41 @@ static const ADCConversionGroup adcgrpcfg1 = {
}
};
THD_WORKING_AREA(waSensors, 512);
static const I2CConfig i2ccfg = {
STM32_TIMINGR_PRESC(8U) | STM32_TIMINGR_SCLDEL(3U) | STM32_TIMINGR_SDADEL(3U) |
STM32_TIMINGR_SCLH(3U) | STM32_TIMINGR_SCLL(9U),
0,
0
};
static const ADS1115Config ads1115cfg = {
&I2CD1,
&i2ccfg,
ADS1115_ADDRESS,
};
static ADS1115Driver ADS1115;
THD_WORKING_AREA(waSensors, 1024);
THD_FUNCTION(Sensors, arg) {
(void)arg;
chRegSetThreadName("sensors");
ads1115ObjectInit(&ADS1115);
ads1115Start(&ADS1115, &ads1115cfg);
while (true) {
thread_t *tp = chMsgWait();
adcsample_t sample;
adcConvert(&ADCD1, &adcgrpcfg1, &sample, 1);
float volts = (sample / 4095.0f) * 18.0f;
//(void)chMsgSend(chRegFindThreadByName("display"), (msg_t)"VAI ADC");
//adcsample_t sample;
//adcConvert(&ADCD1, &adcgrpcfg1, &sample, 1);
//float volts = (sample / 4095.0f) * 18.0f;
char buf[16] = {0};
float volts = ads1115ReadMillivolts(&ADS1115, 0);
sprintf(buf, "Volts: %d.%d", (int)volts, (int) ((volts - ((int) volts)) * 100));
(void)chMsgSend(chRegFindThreadByName("display"), (msg_t)buf);
chMsgRelease(tp, (msg_t) &sample);
chMsgRelease(tp, (msg_t) volts);
}
}
#ifndef SENSORS_H
#define SENSORS_H
extern THD_WORKING_AREA(waSensors, 512);
extern THD_WORKING_AREA(waSensors, 1024);
THD_FUNCTION(Sensors, arg);
#endif /* SENSORS_H */
\ No newline at end of file
#endif /* SENSORS_H */
......@@ -2,6 +2,7 @@
#include "hal.h"
#include "board.h"
#include <string.h>
#include "status.h"
/* STATUS THREAD
......@@ -11,17 +12,19 @@
#define DEBUG &SD3 // UART3
THD_WORKING_AREA(waStatus, 128);
THD_WORKING_AREA(waStatus, 1024);
THD_FUNCTION(Status, arg) {
(void)arg;
chRegSetThreadName("status");
// Just blinks in sequence to test onboard LEDs
while (true) {
palClearLine(LINE_LED0);
chThdSleepMilliseconds(100);
palSetLine(LINE_LED0);
chThdSleepMilliseconds(100);
sdWrite(DEBUG, (uint8_t *)"Teste\r\n", sizeof("Teste\r\n"));
thread_t *tp = chMsgWait();
const uint8_t *msg = (const uint8_t *)chMsgGet(tp);
sdWrite(DEBUG, msg, strlen(msg));
chMsgRelease(tp, MSG_OK);
}
}
\ No newline at end of file
}
#ifndef STATUS_H
#define STATUS_H
extern THD_WORKING_AREA(waStatus, 128);
extern THD_WORKING_AREA(waStatus, 1024);
THD_FUNCTION(Status, arg);
#endif /* STATUS_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment