Jivarofad/m2560 update (#4593)

* New drivers : buzzer/speaker, voice wtv20 / jq6500 , i2c

First commit, testings to be done

* Voice correction

* Better way to drive voice modules

Same thing as LCD drivers

* Issue #2337 (error mismatch) solved

* Font correction

* Fonts correction again

* oups...

* Voice modules (Companion) & flash saving (ST7920)

* Documentation update

* I2C driver correction

* Mega2560 directory renamed to mega2560

* M2560 driver correction

* Voice directory correction

* Voice=ON does not works with MEGA2560 (OFF/WTV20/JQ6500)

* Compliant to cmake documentation

* On M2560, audioAVR use playnumber et playduration declared in voice.h
Don't know why, must be discuted...

* ‘{’ token forgotten

* Simplier way for AVR voice
This commit is contained in:
Franck Aguerre
2017-03-18 09:44:47 +01:00
committed by Bertrand Songis
parent 225ca3160e
commit 49d1aafc1b
25 changed files with 1138 additions and 247 deletions
+1 -1
View File
@@ -283,7 +283,7 @@ void audioTimerCountdown(uint8_t timer, int value)
// pass
}
#if defined(CPUM2560)
#if defined(CPUM2560) && (defined(VOICE_WTV20) || defined(VOICE_JQ6500))
else if (g_model.timers[timer].countdownBeep == COUNTDOWN_VOICE) {
if (value >= 0 && value <= TIMER_COUNTDOWN_START(timer)) {
playNumber(value, 0, 0);
+1 -1
View File
@@ -36,7 +36,7 @@ void loadFontCache();
extern const pm_uchar font_5x7[];
extern const pm_uchar font_10x14[];
#if defined(BOLD_FONT) && (!defined(CPUM64) || defined(TELEMETRY_NONE)) && !defined(BOOT)
#if defined(BOLD_FONT) && ((!defined(CPUM64) && !defined(PCBMEGA2560)) || defined(TELEMETRY_NONE)) && !defined(BOOT)
#define BOLD_SPECIFIC_FONT
extern const pm_uchar font_5x7_B[];
#endif
+1
View File
@@ -531,6 +531,7 @@ void lcdDrawNumber(coord_t x, coord_t y, lcdint_t val, LcdFlags flags, uint8_t l
x += 2;
}
}
#if defined(BOLD_FONT) && !defined(CPUM64) || defined(TELEMETRY_NONE)
if (flags & BOLD) fw += 1;
#endif
+14 -31
View File
@@ -62,48 +62,31 @@ uint8_t lcdRefresh_ST7920(uint8_t full)
{
LCD_LOCK();
static uint8_t state;
uint8_t yst,yend;
uint8_t yst;
uint8_t yend;
uint8_t y_table[6]={0,13,26,39,52,64};
uint8_t x_addr = 0;
uint8_t y_addr = 0;
uint16_t line_offset = 0;
uint8_t col_offset = 0;
uint8_t bit_count = 0;
uint8_t result;
uint8_t * p;
if (full != 0) {
uint8_t *p;
if(full!=0){
yst=0;
yend=64;
state=0;
}
else {
switch (state) { // Since writing to ST7920 is too slow we need to split it to five bands.
default:
case 0:
yst=0;
yend=13;
state=1;
break;
case 1:
yst=13;
yend=26;
state=2;
break;
case 2:
yst=26;
yend=39;
state=3;
break;
case 3:
yst=39;
yend=52;
state=4;
break;
case 4:
yst=52;
yend=64;
state=0;
break;
else{ //Since writing to ST7920 is too slow we need to split it to five bands
yst=y_table[state];
yend=y_table[state+1];
if (state==4){
state=0;
}
else{
state++;
}
}
for (uint8_t y=yst; y<yend; y++) {
+9 -6
View File
@@ -1,5 +1,5 @@
option(AUDIO "Audio support" OFF)
option(VOICE "Voice support" OFF)
set(VOICE "OFF" CACHE STRING "Voice support (OFF/ON/WTV20/JQ6500)")
option(GRAPHICS "Additional graphics" ON)
option(BOLD "Bold font" ON)
option(BATTGRAPH "Battery graph" OFF)
@@ -45,14 +45,17 @@ if(BUZZER)
add_definitions(-DBUZZER)
set(SRC ${SRC} buzzer.cpp)
endif()
if(VOICE)
if((VOICE STREQUAL ON AND NOT PCB STREQUAL MEGA2560) OR ((VOICE STREQUAL WTV20 OR VOICE STREQUAL JQ6500) AND (PCB STREQUAL MEGA2560)))
add_definitions(-DVOICE)
if(PCB STREQUAL MEGA2560)
add_definitions(-DVOICE_${VOICE})
endif()
set(TARGET_SRC ${TARGET_SRC} voice.cpp)
foreach(LANGUAGE ${TTS_LANGUAGES})
if(TTS STREQUAL ${LANGUAGE})
add_definitions(-DTTS_${LANGUAGE})
string(TOLOWER ${LANGUAGE} lang_lower)
set(SRC ${SRC} translations/tts_${lang_lower}.cpp)
string(TOUPPER ${LANGUAGE} LANGUAGE_CAPITALIZED)
if(TTS STREQUAL ${LANGUAGE_CAPITALIZED})
add_definitions(-DTTS_${LANGUAGE_CAPITALIZED})
set(SRC ${SRC} translations/tts_${LANGUAGE}.cpp)
endif()
endforeach()
endif()
+2 -1
View File
@@ -1,5 +1,6 @@
option(PWRMANAGE "Pwr Manage" OFF)
option(SDCARD "SD card support" ON)
option(SDCARD "SD card support" OFF)
option(ROTENC_DIV2 "Rotary encoder resolution/2" OFF)
set(ARCH AVR)
set(MCU atmega2560)
string(TOLOWER ${PCB} FLAVOUR)
+30 -16
View File
@@ -18,23 +18,24 @@
* GNU General Public License for more details.
*/
#include "opentx.h"
#include "opentx.h"
#include "i2c_driver.h"
void boardInit()
{
#if !defined(SIMU)
// Set up I/O port data directions and initial states (unused pin setting : input, pull-up on)
DDRA = 0b11111111; PORTA = 0b00000000; // LCD data
DDRB = 0b01110111; PORTB = 0b00101111; // 7:WTV20SDBusy, 6:PPM_OUT, 5:SimCTRL, 4:Buzzer, SDCARD[3:MISO 2:MOSI 1:SCK 0:CS]
DDRB = 0b01100111; PORTB = 0b10111111; // 7:VoiceBusy, 6:PPM_OUT, 5:SimCTRL, 4:Unused, SDCARD[3:MISO 2:MOSI 1:SCK 0:CS]
DDRC = 0b11111100; PORTC = 0b00000011; // 7-3:LCD, 2:BackLight, 1:ID2_SW, 0:ID1_SW
DDRD = 0b00000000; PORTD = 0b11111100; // 7:AilDR_SW, 6:N/A, 5:N/A, 4:N/A, 3:RENC2_B, 2:RENC2_A, 1:I2C_SDA, 0:I2C_SCL
DDRE = 0b00001010; PORTE = 0b01110100; // 7:PPM_IN, 6:N/A, 5:RENC1_B, 4:RENC1_A, 3:WTV20SDData, 2:N/A, 1:TELEM_TX, 0:TELEM_RX
DDRD = 0b00000000; PORTD = 0b11111111; // 7:AilDR_SW, 6:N/A, 5:N/A, 4:N/A, 3:RENC2_B, 2:RENC2_A, 1:I2C_SDA, 0:I2C_SCL
DDRE = 0b00001010; PORTE = 0b01110100; // 7:PPM_IN, 6:N/A, 5:RENC1_B, 4:RENC1_A, 3:VoiceData, 2:N/A, 1:TELEM_TX, 0:TELEM_RX
DDRF = 0b00000000; PORTF = 0b11111111; // 7-0:Trim switch inputs
DDRG = 0b00100000; PORTG = 0b11011111; // 7:N/A, 6:N/A, 5:WTV20SDClock, 4:N/A, 3:N/A, 2:TCut_SW, 1:Gear_SW, 0: RudDr_SW
DDRG = 0b00100000; PORTG = 0b11011111; // 7:N/A, 6:N/A, 5:VoiceClock, 4:N/A, 3:N/A, 2:TCut_SW, 1:Gear_SW, 0: RudDr_SW
#if defined(DEBUG)
DDRH = 0b01011010; PORTH = 0b11110100; // 7:N/A, 6:RF_Activated, 5:DSC_Activated, 4:Hold_Power, 3:Speaker, 2:N/A, 1:WTV20SDReset, 0:Haptic
DDRH = 0b01011011; PORTH = 0b11110100; // 7:N/A, 6:RF_Activated, 5:DSC_Activated, 4:Hold_Power, 3:Speaker, 2:N/A, 1:VoiceReset, 0:Haptic
#else
DDRH = 0b00011010; PORTH = 0b11110100; // 7:N/A, 6:RF_Activated, 5:DSC_Activated, 4:Hold_Power, 3:Speaker, 2:N/A, 1:WTV20SDReset, 0:Haptic
DDRH = 0b00011011; PORTH = 0b11110100; // 7:N/A, 6:RF_Activated, 5:DSC_Activated, 4:Hold_Power, 3:Speaker, 2:N/A, 1:VoiceReset, 0:Haptic
#endif
DDRJ = 0b00000000; PORTJ = 0b11111111; // 7:N/A, 6:N/A, 5:N/A, 4:N/A, 3:N/A, 2:N/A, 1:RENC2_push, 0:RENC1_push
DDRK = 0b00000000; PORTK = 0b00000000; // Analogic input (no pull-ups)
@@ -52,17 +53,24 @@ void boardInit()
#if defined(AUDIO)
// TIMER4 set into CTC mode, prescaler 16MHz/64=250 kHz
// Used for audio tone generation
TCCR4B = (1<<WGM42) | (0b011 << CS40);
TCCR4B = (0b10<<WGM42) | (0b011 << CS40);
TCCR4A = 0x00;
#endif
#if defined(VOICE)
// WTV20SD set-up, with TIMER5
#if defined(VOICE_WTV20)
// Voice set-up with TIMER5
OCR5A = 0x1F4; //2ms
TCCR5B = (1 << WGM52) | (0b011 << CS50); // CTC OCR5A
TIMSK5 |= (1<<OCIE5A); // Start the interrupt so the unit reset can occur
#endif
#if defined(VOICE_JQ6500)
// Voice set-up with TIMER5
JQ6500_Serial_on; // Idle state (1)
OCR5A = 0x19; // 0x1A=104µs needed for the 9600Bps serial command
TCCR5B = (1 << WGM52) | (0b011 << CS50); // CTC OCR5A
#endif
/* Rotary encoder interrupt set-up */
EIMSK = 0; // disable ALL external interrupts.
// encoder 1
@@ -72,8 +80,14 @@ void boardInit()
EICRA = (1<<ISC30) | (1<<ISC20);
EIFR = (3<<INTF2);
EIMSK = (3<<INT4) | (3<<INT2); // enable the two rot. enc. ext. int. pairs.
#endif // !defined(SIMU)
}
#if defined(RTCLOCK)
i2c_init(); // hardware I2C init
#endif
#endif // !SIMU
}
uint8_t pwrCheck()
{
@@ -162,7 +176,7 @@ static const pm_uchar crossTrim[] PROGMEM = {
TRIMS_GPIO_PIN_RHL,
TRIMS_GPIO_PIN_RHR
};
uint8_t trimDown(uint8_t idx)
{
uint8_t in = ~PINF; //was PIND
@@ -205,7 +219,7 @@ ISR(INT4_vect) // Arduino2560 IO02 (portE pin4)
if (input == 0 || input == 0x30) incRotaryEncoder(0, -1);
#endif
}
ISR(INT5_vect) // Arduino2560 IO03 (portE pin5)
{
uint8_t input = (PINE & 0x30);
@@ -235,7 +249,7 @@ ISR(INT3_vect) // Arduino2560 IO18 (portD pin3)
if (input == 0 || input == 0x0C) incRotaryEncoder(1, +1);
#endif
}
// RTC driver
void rtcSetTime(const struct gtm * tm)
{
+32 -27
View File
@@ -23,9 +23,6 @@
#include "../common/avr/board_avr.h"
//Mods for futur use ? Beta tester love it
//#define ROTENC_DIV2 // rotenc resolution/2
// Board driver
void boardInit(void);
#define boardOff() pwrOff()
@@ -104,22 +101,22 @@ void sdPoll10ms(void);
#define INP_L_Trainer 7
// Servitudes driver
#define INP_E_PPM_IN 7 //not used (reserved)
#define INP_B_WTV_BUSY 7 //WTV20SD, not used (reserved)
#define INP_E_PPM_IN 7 //directly used
#define INP_B_VoiceBuzy 7 //directly used
#define OUT_B_PPM 6
#define OUT_B_SIM_CTL 5
#define OUT_B_BUZZER 4
#define I_O_B_UNUSED 4 //unused, was Buzzer
#define INP_D_I2C_SCL 1
#define INP_D_I2C_SDA 0
#define OUT_E_WTV_DATA 3 //WTV20SD
#define OUT_E_VoiceData 3
#define INP_E_TELEM_RX 1
#define OUT_E_TELEM_TX 0
#define OUT_G_WTV_CLK 5 //WTV20SD
#define OUT_G_VoiceClock 5
#define INP_H_RF_Activated 6
#define INP_H_DSC_Activated 5 //not used, reserved for pwrCheck()
#define INP_H_Hold_Power 4 //not used, reserved for pwrCheck()
#define OUT_H_Speaker 3
#define OUT_H_WTV_RESET 1 //WTV20SD
#define INP_H_DSC_Activated 5 //pwrCheck(), directly used
#define INP_H_Hold_Power 4 //pwrCheck(), directly used
#define OUT_H_SpeakerBuzzer 3
#define OUT_H_VoiceReset 1
#define OUT_H_HAPTIC 0
// Rotary encoders driver
@@ -132,13 +129,14 @@ void sdPoll10ms(void);
#define REA_DOWN() (~PINJ & (1<<INP_J_ROT_ENC_1_PUSH))
#define REB_DOWN() (~PINJ & (1<<INP_J_ROT_ENC_2_PUSH))
#define ROTENC_DOWN() (REA_DOWN() || REB_DOWN())
#define ROTENC_DIV2 // rotary encoders resolution/2
// LCD driver
#define PORTA_LCD_DAT PORTA
#define PORTC_LCD_CTRL PORTC
#if defined(LCD_KS108) // (For KS108 LCd only) MEGA R/W pin always at 0 state in Opentx then
#define OUT_C_LCD_CS2 6 // Use this pin to control second KS108
#else // And connect LCD R/W pin to ground via a 1k resistor
#if defined(LCD_KS108) // (For KS108 LCD only) MEGA R/W pin always at 0 state in Opentx then
#define OUT_C_LCD_CS2 6 // use this pin to control second KS108 (CS2)
#else // and connect KS108 R/W pin to ground via a 1k resistor
#define OUT_C_LCD_RnW 6
#endif
#define OUT_C_LCD_E 7
@@ -167,8 +165,8 @@ void pwrOff();
#define hapticOff() PORTH &= ~(1 << OUT_H_HAPTIC)
// Buzzer driver
#define buzzerOn() PORTB |= (1 << OUT_B_BUZZER)
#define buzzerOff() PORTB &= ~(1 << OUT_B_BUZZER)
#define buzzerOn() PORTH |= (1 << OUT_H_SpeakerBuzzer)
#define buzzerOff() PORTH &= ~(1 << OUT_H_SpeakerBuzzer)
// Speaker driver
#if defined(AUDIO)
@@ -176,16 +174,23 @@ void pwrOff();
#define speakerOff() TCCR4A &= ~(1 << COM4A0)
#endif
// Voice driver
#if defined(VOICE)
#define WTV20SD_Clock_on PORTG |= (1<<OUT_G_WTV_CLK)
#define WTV20SD_Clock_off PORTG &= ~(1<<OUT_G_WTV_CLK)
#define WTV20SD_Data_on PORTE |= (1<<OUT_E_WTV_DATA)
#define WTV20SD_Data_off PORTE &= ~(1<<OUT_E_WTV_DATA)
#define WTV20SD_Reset_on PORTH |= (1<<OUT_H_WTV_RESET)
#define WTV20SD_Reset_off PORTH &= ~(1<<OUT_H_WTV_RESET)
#define WTV20SD_BUSY (PINB & 0x80)
#define WTV20SD_CLK (PING & 0x20)
// Voice driver (WTV20)
#if defined(VOICE_WTV20)
#define WTV20_Data_on PORTE |= (1<<OUT_E_VoiceData)
#define WTV20_Data_off PORTE &= ~(1<<OUT_E_VoiceData)
#define WTV20_BUSY (PINB & (1<<INP_B_VoiceBuzy))
#define WTV20_Clock_on PORTG |= (1<<OUT_G_VoiceClock)
#define WTV20_Clock_off PORTG &= ~(1<<OUT_G_VoiceClock)
#define WTV20_CLK (PING & (1<<OUT_G_VoiceClock))
#define WTV20_Reset_on PORTH |= (1<<OUT_H_VoiceReset)
#define WTV20_Reset_off PORTH &= ~(1<<OUT_H_VoiceReset)
#endif
// Voice driver (JQ6500)
#if defined(VOICE_JQ6500)
#define JQ6500_Serial_on PORTE |= (1<<OUT_E_VoiceData)
#define JQ6500_Serial_off PORTE &= ~(1<<OUT_E_VoiceData)
#define JQ6500_BUSY (PINB & (1<<INP_B_VoiceBuzy))
#endif
#endif // _BOARD_MEGA2560_H_
+196
View File
@@ -0,0 +1,196 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
// From https://github.com/g4lvanix/I2C-master-lib
#include <avr/io.h>
#include <util/twi.h>
#include "i2c_driver.h"
//#define F_SCL 400000UL // SCL frequency
#define F_SCL 800000UL // SCL frequency
#define Prescaler 1
#define TWBR_val ((((F_CPU / F_SCL) / Prescaler) - 16 ) / 2)
void wait()
{
while( !(TWCR & (1<<TWINT)) );
}
void i2c_init(void)
{
TWBR = (uint8_t)TWBR_val;
}
uint8_t i2c_start(uint8_t address)
{
// reset TWI control register
TWCR = 0;
// transmit START condition
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
// wait for end of transmission
wait();
// check if the start condition was successfully transmitted
if((TWSR & 0xF8) != TW_START){ return 1; }
// load slave address into data register
TWDR = address;
// start transmission of address
TWCR = (1<<TWINT) | (1<<TWEN);
// wait for end of transmission
wait();
// check if the device has acknowledged the READ / WRITE mode
uint8_t twst = TW_STATUS & 0xF8;
if ( (twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK) ) return 1;
return 0;
}
uint8_t i2c_write(uint8_t data)
{
// load data into data register
TWDR = data;
// start transmission of data
TWCR = (1<<TWINT) | (1<<TWEN);
// wait for end of transmission
wait();
if( (TWSR & 0xF8) != TW_MT_DATA_ACK ){ return 1; }
return 0;
}
uint8_t i2c_read_ack(void)
{
// start TWI module and acknowledge data after reception
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
// wait for end of transmission
wait();
// return received data from TWDR
return TWDR;
}
uint8_t i2c_read_nack(void)
{
// start receiving without acknowledging reception
TWCR = (1<<TWINT) | (1<<TWEN);
// wait for end of transmission
wait();
// return received data from TWDR
return TWDR;
}
uint8_t i2c_transmit(uint8_t address, uint8_t* data, uint16_t length)
{
if (i2c_start(address | I2C_WRITE)) return 1;
for (uint16_t i = 0; i < length; i++)
{
if (i2c_write(data[i])) return 1;
}
i2c_stop();
return 0;
}
uint8_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length)
{
if (i2c_start(address | I2C_READ)) return 1;
for (uint16_t i = 0; i < (length-1); i++)
{
data[i] = i2c_read_ack();
}
data[(length-1)] = i2c_read_nack();
i2c_stop();
return 0;
}
uint8_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length)
{
if (i2c_start(devaddr | 0x00)) return 1;
i2c_write(regaddr);
for (uint16_t i = 0; i < length; i++)
{
if (i2c_write(data[i])) return 1;
}
i2c_stop();
return 0;
}
uint8_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length)
{
if (i2c_start(devaddr)) return 1;
i2c_write(regaddr);
if (i2c_start(devaddr | 0x01)) return 1;
for (uint16_t i = 0; i < (length-1); i++)
{
data[i] = i2c_read_ack();
}
data[(length-1)] = i2c_read_nack();
i2c_stop();
return 0;
}
void i2c_stop(void)
{
// transmit STOP condition
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
}
uint8_t iic_read (
uint8_t dev, /* Device address */
uint8_t adr, /* Read start address */
uint16_t cnt, /* Read uint8_t count */
uint8_t *buff /* Read data buffer */
)
{
uint8_t ret;
ret = i2c_readReg(dev, adr, buff, cnt);
return (!ret);
}
uint8_t iic_write (
uint8_t dev, /* Device address */
uint8_t adr, /* Write start address */
uint16_t cnt, /* Write uint8_t count */
const uint8_t *buff /* Data to be written */
)
{
uint8_t ret;
ret = i2c_writeReg(dev, adr, (uint8_t *)buff, cnt);
return (!ret);
}
+108
View File
@@ -0,0 +1,108 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
// From https://github.com/g4lvanix/I2C-master-lib
#ifndef I2C_DRIVER_H
#define I2C_DRIVER_H
#define I2C_READ 0x01
#define I2C_WRITE 0x00
void i2c_init(void);
uint8_t i2c_start(uint8_t address);
uint8_t i2c_write(uint8_t data);
uint8_t i2c_read_ack(void);
uint8_t i2c_read_nack(void);
uint8_t i2c_transmit(uint8_t address, uint8_t* data, uint16_t length);
uint8_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length);
uint8_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length);
uint8_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length);
void i2c_stop(void);
// M2560 functions
uint8_t iic_read (
uint8_t dev, /* Device address */
uint8_t adr, /* Read start address */
uint16_t cnt, /* Read uint8_t count */
uint8_t *buff /* Read data buffer */
);
uint8_t iic_write (
uint8_t dev, /* Device address */
uint8_t adr, /* Write start address */
uint16_t cnt, /* Write uint8_t count */
const uint8_t *buff /* Data to be written */
);
#endif // I2C_DRIVER_H
/*
##### void I2C_init(void)
This function needs to be called only once to set up the correct SCL frequency
for the bus
##### uint8_t I2C_start(uint8_t address)
This function needs to be called any time a connection to a new slave device should
be established. The function returns 1 if an error has occurred, otherwise it returns
0.
The syntax to start a operation write to a device is either:
`I2C_start(SLAVE_ADDRESS+I2C_WRITE);`
or
`I2C_start(SLAVE_WRITE_ADDRESS);`
The syntax to start a read operation from a device is either:
`I2C_start(SLAVE_ADDRESS+I2C_READ);`
or
`I2C_start(SLAVE_READ_ADDRESS);`
##### uint8_t I2C_write(uint8_t data)
This function is used to write data to the currently active device.
The only parameter this function takes is the 8 bit unsigned integer to be sent.
The function returns 1 if an error has occurred, otherwise it returns
0.
##### uint8_t I2C_read_ack(void)
This function is used to read one byte from a device and request another byte of data
after the transmission is complete by sending the acknowledge bit.
This function returns the received byte.
##### uint8_t I2C_read_nack(void)
This function is used to read one byte from a device an then not requesting another
byte and therefore stopping the current transmission.
This function returns the received byte.
##### uint8_t i2c_transmit(uint8_t address, uint8_t* data, uint16_t length);
This function is used to transmit [length] number of bytes to an I2C device with the given
I2C address from [data].
The [address] passed to this function is the 7-bit slave address, left
shifted by one bit (i.e. 7-bit slave address is `0x2F` -> `(0x2F)<<1` = `0x5E`)
##### uint8_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length);
This function is used to read [length] number of bytes from the I2C device with the given
I2C address into the [data].
The [address] passed to this function is the 7-bit slave address, left
shifted by one bit (i.e. 7-bit slave address is `0x2F` -> `(0x2F)<<1` = `0x5E`)
##### void I2C_stop(void)
This function disables the TWI peripheral completely
and therefore disconnects the device from the bus.
*/
+5 -142
View File
@@ -2,7 +2,7 @@
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
@@ -18,147 +18,10 @@
* GNU General Public License for more details.
*/
// driver adapted from somo14d driver for gruvin9x board for driving WTV20SDMini module
#include "opentx.h"
// Start and stop bits need to be 2ms in duration. Start bit is low, stop bit is high
#define WTV20SD_STOP_TIME 68 // This is the needed 2ms (4) + 30ms (60) to allow for the
// point at which the busy flag is checkable + 2ms for saftey (4)
#define WTV20SD_START_TIME 5 // The 2ms of a stop/start bit
enum WTV20SD_State
{
PAUSE,
SENDSTART,
SENDDATA,
SENDSTOP
};
#define QUEUE_LENGTH 10
uint16_t WTV20SD_playlist[QUEUE_LENGTH] = {0};
volatile uint8_t WTV20SD_InputIndex = 0;
uint8_t WTV20SD_PlayIndex = 0;
uint8_t Startstop = WTV20SD_START_TIME;
uint16_t WTV20SD_current = 0;
uint8_t state = PAUSE;
void WTV20SD_sendstart()
{
WTV20SD_Clock_off; // Start Bit, CLK low for 2ms
--Startstop;
if (!Startstop) state = SENDDATA;
}
void WTV20SD_senddata()
{
static uint8_t i = 0;
if (!WTV20SD_CLK) {
// Only change data when the CLK is low
if (WTV20SD_current & 0x8000) {
WTV20SD_Data_on; // Data high
}
WTV20SD_current = (WTV20SD_current<<1);
++i;
_delay_us(1); // Data setup delay
WTV20SD_Clock_on; // CLK high
}
else {
// Don't alter after sending last bit in preparation for sending stop bit
WTV20SD_Clock_off; // CLK low
WTV20SD_Data_off; // Data low
}
if (i == 16) { i = 0; Startstop = WTV20SD_STOP_TIME; state = SENDSTOP; }
}
void WTV20SD_sendstop()
{
WTV20SD_Data_off; // Data low
WTV20SD_Clock_on; // Stop Bit, CLK high for 2ms
--Startstop;
if (!Startstop && !WTV20SD_BUSY) state = PAUSE;
}
void pushPrompt(uint16_t prompt)
{
// if mute active => no voice
if (g_eeGeneral.beepMode == e_mode_quiet) return;
/* Load playlist and activate interrupt */
WTV20SD_playlist[WTV20SD_InputIndex] = prompt;
++WTV20SD_InputIndex;
WTV20SD_InputIndex %= QUEUE_LENGTH;
if (!isPlaying()) {
TIMSK5 |= (1<<OCIE5A); // enable interrupts on Output Compare A Match
}
}
uint8_t isPlaying()
{
/* interrupts active on Output Compare A Match ? */
#if defined(SIMU)
return false;
#else
return (TIMSK5 & (1<<OCIE5A));
#endif
}
#if !defined(SIMU)
ISR(TIMER5_COMPA_vect) // every 0.5ms normally, every 2ms during startup reset
{
static uint8_t reset_dly=4;
static uint8_t reset_pause=150;
if (reset_dly) {
OCR5A=0x1f4;
reset_dly--;
WTV20SD_Reset_off;
WTV20SD_Data_off;
WTV20SD_Clock_on;
} // RESET low
else if (reset_pause) {
OCR5A=0x1f4;
reset_pause--;
WTV20SD_Reset_on;
} // RESET high
else {
OCR5A = 0x7d; // another 0.5ms
if (state == PAUSE) {
if (WTV20SD_PlayIndex == WTV20SD_InputIndex) {
TIMSK5 &= ~(1<<OCIE5A); // stop reentrance
TCNT5=0; // reset timer
return; // nothing else to play
}
else {
WTV20SD_current = WTV20SD_playlist[WTV20SD_PlayIndex];
++WTV20SD_PlayIndex;
WTV20SD_PlayIndex %= QUEUE_LENGTH;
Startstop = WTV20SD_START_TIME;
state = SENDSTART;
}
} // end PAUSE
if (state == SENDSTART) {
WTV20SD_sendstart();
return;
}
if (state == SENDDATA) {
WTV20SD_senddata();
return;
}
if (state == SENDSTOP) {
WTV20SD_sendstop();
return;
}
}
}
#if defined(VOICE_WTV20)
#include "targets/mega2560/voice_wtv20.cpp"
#elif defined(VOICE_JQ6500)
#include "targets/mega2560/voice_jq6500.cpp"
#endif
+1 -1
View File
@@ -23,7 +23,7 @@
#define PROMPT_SYSTEM_BASE 480
extern void pushPrompt(uint16_t prompt);
extern uint8_t isPlaying();
extern bool isPlaying();
#define I18N_PLAY_FUNCTION(lng, x, ...) void x(__VA_ARGS__)
#define PLAY_FUNCTION(x, ...) void x(__VA_ARGS__)
+109
View File
@@ -0,0 +1,109 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
// JQ6500 mp3 module driver
// 6 X 10 (bits) X 104µs = 6.24 mS to send a playfile command
#include "opentx.h"
#define QUEUE_LENGTH 16*2 //bytes
enum JQ6500_State
{
START = 0x7E, //Start
NUMBY = 0x04, //Num bytes follow
SELEC = 0X03, //Select file
FILEH = 0x00, //Dummy file
FILEL = 0x01, //Dummy file
TERMI = 0xEF //Termination
};
JQ6500_State state = START;
uint8_t JQ6500_playlist[QUEUE_LENGTH] = {0};
volatile uint8_t JQ6500_InputIndex = 0;
uint8_t JQ6500_PlayIndex = 0;
bool isPlaying()
{
/* interrupts active on Output Compare A Match ? */
#if defined(SIMU)
return false;
#else
return (TIMSK5 & (1<<OCIE5A));
#endif
}
void pushPrompt(uint16_t prompt)
{
// if mute active => no voice
if (g_eeGeneral.beepMode == e_mode_quiet) return;
++prompt; // With SDformatter, first FAT address = 1 ? Not all th time ??
/* Load playlist and activate interrupt */
JQ6500_playlist[JQ6500_InputIndex] = (uint8_t)(prompt >> 8); // MSB first
++JQ6500_InputIndex;
JQ6500_playlist[JQ6500_InputIndex] = (uint8_t)(prompt & 0xFF); // LSB after
++JQ6500_InputIndex;
if (JQ6500_InputIndex == QUEUE_LENGTH) JQ6500_InputIndex = 0;
if (!isPlaying()) {
TCNT5=0;
OCR5A = 0xFA;
TIMSK5 |= (1<<OCIE5A); // enable interrupts on Output Compare A Match
}
}
uint8_t JQ6500_sendbyte(uint8_t Data_byte)
{
static uint8_t i = 0;
if (!i) { JQ6500_Serial_off; ++i; return 0; } // serial start bit
if (i==9) { JQ6500_Serial_on; i = 0; return 1; } // serial stop bit
if ((Data_byte >> (i-1)) & 0x01) { JQ6500_Serial_on;} else { JQ6500_Serial_off;} // send data bits
++i;
return 0;
}
#if !defined(SIMU)
ISR(TIMER5_COMPA_vect) // every 104µS
{
sei();
if (state == START) { OCR5A = 0x19; if (JQ6500_BUSY) return; if (JQ6500_sendbyte(state)) { state = NUMBY; return; } }
if (state == NUMBY) { if (JQ6500_sendbyte(state)) { state = SELEC; return; } }
if (state == SELEC) { if (JQ6500_sendbyte(state)) { state = FILEH; return; } }
if (state == FILEH) { if (JQ6500_sendbyte(JQ6500_playlist[JQ6500_PlayIndex])) { ++JQ6500_PlayIndex; state = FILEL; return; } }
if (state == FILEL) { if (JQ6500_sendbyte(JQ6500_playlist[JQ6500_PlayIndex])) { ++JQ6500_PlayIndex; state = TERMI; return; } }
if (state == TERMI) {
if (JQ6500_sendbyte(state)) {
state = START;
if (JQ6500_PlayIndex == QUEUE_LENGTH) JQ6500_PlayIndex = 0;
if (JQ6500_PlayIndex == JQ6500_InputIndex) { OCR5A = 0x19; TIMSK5 &= ~(1<<OCIE5A); } // stop reentrance
return; }
}
cli();
}
#endif
+172
View File
@@ -0,0 +1,172 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
// WTV20SDMini module driver, adapted from somo14d driver (gruvin9x board)
#include "opentx.h"
// Start and stop bits need to be 2ms in duration. Start bit is low, stop bit is high
#define WTV20_STOP_TIME 84 // This is the needed 2ms (4) + 40ms (80) to allow for the
// point at which the busy flag is checkable + 2ms for saftey (4)
#define WTV20_START_TIME 5 // The 2ms of a stop/start bit
enum WTV20_State
{
RESET,
RESETPAUSE,
PAUSE,
SENDSTART,
SENDDATA,
SENDSTOP
};
#define QUEUE_LENGTH 10
uint16_t WTV20_playlist[QUEUE_LENGTH] = {0};
volatile uint8_t WTV20_InputIndex = 0;
uint8_t WTV20_PlayIndex = 4; // Also used for reset,(save 2 static uint8_t)
uint8_t Startstop = WTV20_START_TIME;
uint16_t WTV20_current = 0;
uint8_t state = RESET;
void WTV20_sendstart()
{
WTV20_Clock_off; // Start Bit, CLK low for 2ms
--Startstop;
if (!Startstop) state = SENDDATA;
}
void WTV20_senddata()
{
static uint8_t i = 0;
if (!WTV20_CLK) {
// Only change data when the CLK is low
if (WTV20_current & 0x8000) {
WTV20_Data_on; // Data high
}
WTV20_current = (WTV20_current << 1);
++i;
_delay_us(1); // Data setup delay
WTV20_Clock_on; // CLK high
}
else {
// Don't alter after sending last bit in preparation for sending stop bit
WTV20_Clock_off; // CLK low
WTV20_Data_off; // Data low
}
if (i == 16) { i = 0; Startstop = WTV20_STOP_TIME; state = SENDSTOP; }
}
void WTV20_sendstop()
{
WTV20_Data_off; // Data low
WTV20_Clock_on; // Stop Bit, CLK high for 2ms
--Startstop;
if (!Startstop && !WTV20_BUSY) state = PAUSE;
}
void pushPrompt(uint16_t prompt)
{
// if mute active => no voice
if (g_eeGeneral.beepMode == e_mode_quiet) return;
/* Load playlist and activate interrupt */
WTV20_playlist[WTV20_InputIndex] = prompt;
++WTV20_InputIndex;
if (WTV20_InputIndex == QUEUE_LENGTH) WTV20_InputIndex = 0;
if (!isPlaying()) {
TIMSK5 |= (1<<OCIE5A); // enable interrupts on Output Compare A Match
}
}
bool isPlaying()
{
/* interrupts active on Output Compare A Match ? */
#if defined(SIMU)
return false;
#else
return (TIMSK5 & (1<<OCIE5A));
#endif
}
#if !defined(SIMU)
ISR(TIMER5_COMPA_vect) // every 0.5ms normally, every 2ms during startup reset
{
sei();
if (state == PAUSE) {
if (WTV20_PlayIndex == WTV20_InputIndex) {
TIMSK5 &= ~(1<<OCIE5A); // stop reentrance
TCNT5=0; // reset timer
return; // nothing else to play
}
else {
WTV20_current = WTV20_playlist[WTV20_PlayIndex];
++WTV20_PlayIndex;
if (WTV20_PlayIndex == QUEUE_LENGTH) WTV20_PlayIndex = 0;
Startstop = WTV20_START_TIME;
state = SENDSTART;
}
} // end PAUSE
if (state == SENDSTART) {
WTV20_sendstart();
return;
}
if (state == SENDDATA) {
WTV20_senddata();
return;
}
if (state == SENDSTOP) {
WTV20_sendstop();
return;
}
if (state == RESET) {
if (WTV20_PlayIndex) { // WTV20_PlayIndex used as reset counter
// OCR5A=0x1f4; setted in board_mega2560.cpp
WTV20_Reset_off;
WTV20_Data_off;
WTV20_Clock_on;
--WTV20_PlayIndex;
return;
} // RESET low
else {
state = RESETPAUSE;
return;
}
}
if (state == RESETPAUSE) {
WTV20_Reset_on; // RESET high
if (!WTV20_BUSY) {
state = PAUSE;
OCR5A = 0x7d; // 0.5 ms after init
}
}
cli();
}
#endif