Initial support for I2C soft pots on Module17

Initial support for I2C soft pots on Module17, ADC1 driver for input voltage
reading. Cherry-picked from Mathis DB9MAT repo.

TG-398
This commit is contained in:
Niccolò Izzo 2021-12-23 14:14:09 +01:00
parent d4ba8a5b9c
commit 2fdbf0f236
9 changed files with 505 additions and 57 deletions

View File

@ -310,12 +310,14 @@ dm1801_def = def + mk22fn512_def + {'PLATFORM_DM1801': ''}
##
mod17_src = src + stm32f405_src + ['platform/targets/Module17/platform.c',
'platform/drivers/display/SH110x_Mod17.c',
'platform/drivers/ADC/ADC1_Mod17.c',
'platform/drivers/keyboard/keyboard_Mod17.c',
'platform/drivers/NVM/nvmem_Mod17.c',
'platform/drivers/baseband/radio_Mod17.cpp',
'platform/drivers/audio/inputStream_Mod17.cpp',
'platform/drivers/audio/outputStream_Mod17.c',
'platform/drivers/audio/audio_Mod17.c']
'platform/drivers/audio/audio_Mod17.c',
'platform/drivers/baseband/MCP4551_Mod17.cpp']
mod17_inc = inc + stm32f405_inc + ['platform/targets/Module17']
mod17_def = def + stm32f405_def + {'PLATFORM_MOD17': ''}

View File

@ -35,6 +35,9 @@ static const uint16_t bat_v_max = 0x0819; // 8.10V
#elif defined BAT_LIPO_3S
static const uint16_t bat_v_min = 0x0AD4; // 10.83V
static const uint16_t bat_v_max = 0x0C73; // 12.45V
#elif defined BAT_MOD17
static const uint16_t bat_v_min = 0x0600; // 6.00V
static const uint16_t bat_v_max = 0x0DCD; // 13.8V
#elif defined BAT_NONE
static const uint16_t bat_v_min = 0;
static const uint16_t bat_v_max = 0;

View File

@ -0,0 +1,92 @@
/***************************************************************************
* Copyright (C) 2020 by Silvano Seva IU2KWO and Niccolò Izzo IU2KIN *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/
#include <interfaces/gpio.h>
#include <hwconfig.h>
#include <pthread.h>
#include <stdlib.h>
#include "ADC1_Mod17.h"
pthread_mutex_t adcMutex;
void adc1_init()
{
pthread_mutex_init(&adcMutex, NULL);
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
__DSB();
/*
* Configure GPIOs to analog input mode:
*/
gpio_setMode(AIN_VBAT, INPUT_ANALOG);
/*
* ADC clock is APB2 frequency divided by 8, giving 10.5MHz.
* We set the sample time of each channel to 84 ADC cycles and we have that
* a conversion takes 12 cycles: total conversion time is then of ~9us.
*/
ADC->CCR |= ADC_CCR_ADCPRE;
ADC1->SMPR2 = ADC_SMPR2_SMP3_2;
/*
* Convert one channel, no overrun interrupt, 12-bit resolution,
* no analog watchdog, discontinuous mode, no end of conversion interrupts,
* turn on ADC.
*/
ADC1->SQR1 = 0;
ADC1->CR2 = ADC_CR2_ADON;
}
void adc1_terminate()
{
pthread_mutex_destroy(&adcMutex);
ADC1->CR2 &= ~ADC_CR2_ADON;
RCC->APB2ENR &= ~RCC_APB2ENR_ADC1EN;
__DSB();
}
uint16_t adc1_getRawSample(uint8_t ch)
{
if(ch > 15) return 0;
pthread_mutex_lock(&adcMutex);
ADC1->SQR3 = ch;
ADC1->CR2 |= ADC_CR2_SWSTART;
while((ADC1->SR & ADC_SR_EOC) == 0) ;
uint16_t value = ADC1->DR;
pthread_mutex_unlock(&adcMutex);
return value;
}
uint16_t adc1_getMeasurement(uint8_t ch)
{
/*
* To avoid using floats, we convert the raw ADC sample to mV using 16.16
* fixed point math. The equation for conversion is (sample * 3300)/4096 but,
* since converting the raw ADC sample to 16.16 notation requires a left
* shift by 16 and dividing by 4096 is equivalent to shifting right by 12,
* we just shift left by four and then multiply by 3300.
* With respect to using floats, maximum error is -1mV.
*/
uint32_t sample = (adc1_getRawSample(ch) << 4) * 3300;
return ((uint16_t) (sample >> 16));
}

View File

@ -0,0 +1,74 @@
/***************************************************************************
* Copyright (C) 2020 by Silvano Seva IU2KWO and Niccolò Izzo IU2KIN *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/
#ifndef ADC1_H
#define ADC1_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Driver for ADC1, used on Module 17 to sample input voltage
*
* NOTE: values inside the enum are the channel numbers of STM32 ADC1 peripheral.
*/
enum adcCh
{
ADC_VBAT_CH = 3,
};
/**
* Initialise ADC1.
*/
void adc1_init();
/**
* Turn off ADC1.
*/
void adc1_terminate();
/**
* Get current measurement of a given channel returning the raw ADC value.
*
* NOTE: the mapping provided in enum adcCh DOES NOT correspond to the physical
* ADC channel mapping!
*
* @param ch: channel number.
* @return current value of the specified channel, in ADC counts.
*/
uint16_t adc1_getRawSample(uint8_t ch);
/**
* Get current measurement of a given channel.
*
* NOTE: the mapping provided in enum adcCh DOES NOT correspond to the physical
* ADC channel mapping!
*
* @param ch: channel number.
* @return current value of the specified channel in mV.
*/
uint16_t adc1_getMeasurement(uint8_t ch);
#ifdef __cplusplus
}
#endif
#endif /* ADC1_H */

View File

@ -0,0 +1,56 @@
/***************************************************************************
* Copyright (C) 2021 by Federico Amedeo Izzo IU2NUO, *
* Niccolò Izzo IU2KIN *
* Frederik Saraci IU2NRO *
* Silvano Seva IU2KWO *
* Mathis Schmieder DB9MAT *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/
#ifndef MCP4551_H
#define MCP4551_H
#include <stdint.h>
#include <stdbool.h>
#include <datatypes.h>
#ifdef __cplusplus
extern "C" {
#endif
// Common WIPER values
#define MCP4551_WIPER_MID 0x080
#define MCP4551_WIPER_A 0x100
#define MCP4551_WIPER_B 0x000
// Command definitions (sent to WIPER register)
#define MCP4551_CMD_WRITE 0x00
#define MCP4551_CMD_INC 0x04
#define MCP4551_CMD_DEC 0x08
#define MCP4551_CMD_READ 0x0C
/**
* Initialise I2C.
*/
void i2c_init();
void mcp4551_init(uint8_t addr);
void mcp4551_setWiper(uint8_t devAddr, uint16_t value);
#ifdef __cplusplus
}
#endif
#endif /* MCP4551_H */

View File

@ -0,0 +1,203 @@
/***************************************************************************
* Copyright (C) 2021 by Federico Amedeo Izzo IU2NUO, *
* Niccolò Izzo IU2KIN *
* Frederik Saraci IU2NRO *
* Silvano Seva IU2KWO *
* Mathis Schmieder DB9MAT *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/
#include <interfaces/gpio.h>
#include <interfaces/delays.h>
#include <hwconfig.h>
#include "MCP4551.h"
/*
* Implementation of MCP4551 I2C interface.
*
* Hardware I2C is not yet implemented. Bit-bang, baby!
*/
void _i2c_start();
void _i2c_stop();
void _i2c_write(uint8_t val);
uint8_t _i2c_read(bool ack);
void i2c_init()
{
gpio_setMode(I2C_SDA, INPUT);
gpio_setMode(I2C_SCL, OUTPUT);
gpio_clearPin(I2C_SCL);
}
void mcp4551_init(uint8_t addr)
{
mcp4551_setWiper(addr, MCP4551_WIPER_MID);
}
void mcp4551_setWiper(uint8_t devAddr, uint16_t value)
{
_i2c_start();
_i2c_write(devAddr << 1);
uint8_t temp = ((value >> 8 & 0x01) | MCP4551_CMD_WRITE);
_i2c_write(temp);
temp = (value & 0xFF);
_i2c_write(temp);
_i2c_stop();
}
/*uint16_t i2c_readReg16(uint8_t devAddr, uint8_t reg)
{
_i2c_start();
_i2c_write(devAddr << 1);
_i2c_write(reg);
_i2c_start();
_i2c_write(devAddr | 0x01);
uint8_t valHi = _i2c_read(true);
uint8_t valLo = _i2c_read(false);
_i2c_stop();
return (valHi << 8) | valLo;
} */
/*
* Software I2C routine
*/
void _i2c_start()
{
gpio_setMode(I2C_SDA, OUTPUT);
/*
* Lines commented to keep SCL high when idle
*
gpio_clearPin(I2C_SCL);
delayUs(2);
*/
gpio_setPin(I2C_SDA);
delayUs(5);
gpio_setPin(I2C_SCL);
delayUs(5);
gpio_clearPin(I2C_SDA);
delayUs(5);
gpio_clearPin(I2C_SCL);
delayUs(6);
}
void _i2c_stop()
{
gpio_setMode(I2C_SDA, OUTPUT);
gpio_clearPin(I2C_SCL);
delayUs(5);
gpio_clearPin(I2C_SDA);
delayUs(5);
gpio_setPin(I2C_SCL);
delayUs(5);
gpio_setPin(I2C_SDA);
delayUs(5);
/*
* Lines commented to keep SCL high when idle
*
gpio_clearPin(I2C_SCL);
delayUs(5);
*/
}
void _i2c_write(uint8_t val)
{
gpio_setMode(I2C_SDA, OUTPUT);
for(uint8_t i = 0; i < 8; i++)
{
gpio_clearPin(I2C_SCL);
delayUs(1);
if(val & 0x80)
{
gpio_setPin(I2C_SDA);
}
else
{
gpio_clearPin(I2C_SDA);
}
val <<= 1;
delayUs(1);
gpio_setPin(I2C_SCL);
delayUs(5);
}
/* Ensure SCL is low before releasing SDA */
gpio_clearPin(I2C_SCL);
/* Clock cycle for slave ACK/NACK */
gpio_setMode(I2C_SDA, INPUT_PULL_UP);
delayUs(5);
gpio_setPin(I2C_SCL);
delayUs(5);
gpio_clearPin(I2C_SCL);
delayUs(1);
/* Asserting SDA pin allows to fastly bring the line to idle state */
gpio_setPin(I2C_SDA);
gpio_setMode(I2C_SDA, OUTPUT);
delayUs(6);
}
uint8_t _i2c_read(bool ack)
{
gpio_setMode(I2C_SDA, INPUT_PULL_UP);
gpio_clearPin(I2C_SCL);
uint8_t value = 0;
for(uint8_t i = 0; i < 8; i++)
{
delayUs(5);
gpio_setPin(I2C_SCL);
delayUs(5);
value <<= 1;
value |= gpio_readPin(I2C_SDA);
gpio_clearPin(I2C_SCL);
}
/*
* Set ACK/NACK state BEFORE putting SDA gpio to output mode.
* This avoids spurious spikes which can be interpreted as NACKs
*/
gpio_clearPin(I2C_SDA);
gpio_setMode(I2C_SDA, OUTPUT);
delayUs(5);
if(!ack) gpio_setPin(I2C_SDA);
/* Clock cycle for ACK/NACK */
delayUs(5);
gpio_setPin(I2C_SCL);
delayUs(5);
gpio_clearPin(I2C_SCL);
delayUs(5);
return value;
}

View File

@ -15,6 +15,16 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* As a special exception, if other files instantiate templates or use *
* macros or inline functions from this file, or you compile this file *
* and link it with other works to produce a work based on this file, *
* this file does not by itself cause the resulting work to be covered *
* by the GNU General Public License. However the source code for this *
* file must still be made available in accordance with the GNU General *
* Public License. This exception does not invalidate any other reasons *
* why a work based on this file might be covered by the GNU General *
* Public License. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/
@ -80,41 +90,36 @@ void display_init()
gpio_clearPin(LCD_RS);
gpio_clearPin(LCD_RST); /* Reset controller */
delayMs(1);
delayMs(50);
gpio_setPin(LCD_RST);
delayMs(5);
delayMs(50);
gpio_clearPin(LCD_CS);
gpio_clearPin(LCD_RS); /* RS low -> command mode */
spi2_sendRecv(0xAE); /* Disable Display */
spi2_sendRecv(0x20); /* Set Memory Addressing Mode to horizontal addressing Mode */
spi2_sendRecv(0xAE); // SH110X_DISPLAYOFF,
spi2_sendRecv(0xd5); // SH110X_SETDISPLAYCLOCKDIV, 0x51,
spi2_sendRecv(0x51);
//spi2_sendRecv(0x20); // SH110X_MEMORYMODE,
spi2_sendRecv(0x81); // SH110X_SETCONTRAST, 0x4F,
spi2_sendRecv(0x4F);
spi2_sendRecv(0xAD); // SH110X_DCDC, 0x8A,
spi2_sendRecv(0x8A);
spi2_sendRecv(0xA0); // SH110X_SEGREMAP,
spi2_sendRecv(0xC0); // SH110X_COMSCANINC,
spi2_sendRecv(0xDC); // SH110X_SETDISPSTARTLINE, 0x0,
spi2_sendRecv(0x00);
spi2_sendRecv(0xB0); /* Set Page Start Address for Page Addressing Mode */
spi2_sendRecv(0xC8); /* Set COM Output Scan Direction */
spi2_sendRecv(0x00); /* Set low column address */
spi2_sendRecv(0x10); /* Set high column address */
spi2_sendRecv(0x40); /* Set start line address */
spi2_sendRecv(0x81); /* Set contrast */
spi2_sendRecv(0xCF);
spi2_sendRecv(0xA1); /* Set segment re-map 0 to 127 */
spi2_sendRecv(0xA6); /* Set normal color */
spi2_sendRecv(0xA8); /* Set multiplex ratio (1 to 64) */
spi2_sendRecv(0x3F);
spi2_sendRecv(0xA4); /* Output follows RAM content */
spi2_sendRecv(0xD3); /* Set display offset */
spi2_sendRecv(0x00); /* Not offset */
spi2_sendRecv(0xD5); /* Set display clock divide ratio/oscillator frequency */
spi2_sendRecv(0xF0); /* Set divide ratio */
spi2_sendRecv(0xD9); /* Set pre-charge period */
spi2_sendRecv(0xd3); // SH110X_SETDISPLAYOFFSET, 0x60,
spi2_sendRecv(0x60);
spi2_sendRecv(0xd9); // SH110X_SETPRECHARGE, 0x22,
spi2_sendRecv(0x22);
spi2_sendRecv(0xDA); /* Set com pins hardware configuration */
spi2_sendRecv(0x12);
spi2_sendRecv(0xDB); /* Set vcomh */
spi2_sendRecv(0x40);
spi2_sendRecv(0x8D); /* Set DC-DC enable */
spi2_sendRecv(0x14);
spi2_sendRecv(0xAF); /* Enable Display */
spi2_sendRecv(0xdb); // SH110X_SETVCOMDETECT, 0x35,
spi2_sendRecv(0x35);
spi2_sendRecv(0xa8); // SH110X_SETMULTIPLEX, 0x3F,
spi2_sendRecv(0x3f);
spi2_sendRecv(0xa4); // SH110X_DISPLAYALLON_RESUME,
spi2_sendRecv(0xa6); // SH110X_NORMALDISPLAY,
spi2_sendRecv(0xAF); // SH110x_DISPLAYON
gpio_setPin(LCD_CS);
}
@ -130,36 +135,26 @@ void display_terminate()
void display_renderRow(uint8_t row)
{
/* magic stuff */
uint8_t *buf = (frameBuffer + 128 * row);
for (uint8_t i = 0; i<16; i++)
{
uint8_t tmp[8] = {0};
for (uint8_t j = 0; j < 8; j++)
{
uint8_t tmp_buf = buf[j*16 + i];
int count = __builtin_popcount(tmp_buf);
while (count > 0)
{
int pos = __builtin_ctz(tmp_buf);
tmp[pos] |= 1UL << j;
tmp_buf &= ~(1 << pos);
count--;
}
}
uint8_t *buf = (frameBuffer);
for (uint8_t s = 0; s < 8; s++)
{
(void) spi2_sendRecv(tmp[s]);
}
}
for(uint16_t i=0; i<64; i++)
{
uint8_t out=0, tmp=buf[i*16 + 15-row];
for(uint8_t j=0; j<8; j++)
{
out|=((tmp>>(7-j))&1)<<j;
}
spi2_sendRecv(out);
}
}
void display_renderRows(uint8_t startRow, uint8_t endRow)
{
gpio_clearPin(LCD_CS);
for(uint8_t row = startRow; row < endRow; row++)
for(uint8_t row = startRow; row <= endRow; row++)
{
gpio_clearPin(LCD_RS); /* RS low -> command mode */
(void) spi2_sendRecv(0xB0 | row); /* Set Y position */
@ -168,13 +163,13 @@ void display_renderRows(uint8_t startRow, uint8_t endRow)
gpio_setPin(LCD_RS); /* RS high -> data mode */
display_renderRow(row);
}
gpio_setPin(LCD_CS);
}
void display_render()
{
display_renderRows(0, SCREEN_HEIGHT / 8);
display_renderRows(0, SCREEN_WIDTH / 8 - 1);
}
bool display_renderingInProgress()

View File

@ -39,7 +39,7 @@
#define PIX_FMT_BW
/* Device has no battery */
#define BAT_NONE
#define BAT_MOD17
/* Signalling LEDs */
#define PTT_LED GPIOC,8
@ -75,4 +75,12 @@
#define MIC_MUTE GPIOC,4
#define MIC_GAIN GPIOC,5
#define AIN_VBAT GPIOA,3
/* I2C for MCP4551 */
#define I2C_SDA GPIOB,7
#define I2C_SCL GPIOB,6
#define SOFTPOT_RX 0x2E
#define SOFTPOT_TX 0x2F
#endif

View File

@ -27,6 +27,9 @@
#include <backlight.h>
#include <interfaces/rtc.h>
#include <interfaces/audio.h>
#include <ADC1_Mod17.h>
#include <interfaces/nvmem.h>
#include <MCP4551.h>
void platform_init()
{
@ -39,6 +42,14 @@ void platform_init()
gpio_setMode(PTT_OUT, OUTPUT);
gpio_clearPin(PTT_OUT);
nvm_init();
adc1_init();
i2c_init();
mcp4551_init(SOFTPOT_RX);
mcp4551_init(SOFTPOT_TX);
//mcp4551_setWiper(SOFTPOT_RX, MCP4551_WIPER_A);
//mcp4551_setWiper(SOFTPOT_TX, MCP4551_WIPER_A);
audio_init();
}
@ -48,11 +59,15 @@ void platform_terminate()
gpio_clearPin(PTT_LED);
gpio_clearPin(SYNC_LED);
gpio_clearPin(ERR_LED);
adc1_terminate();
nvm_terminate();
audio_terminate();
}
uint16_t platform_getVbat()
{
return 0;
return adc1_getMeasurement(ADC_VBAT_CH)*5;
}
uint8_t platform_getMicLevel()