Son_Gohan писал(а):
Насколько я понял, его можно использовать как кнопку, имеющую 5 положений - 4 в стороны и 1 центр (выбор, например)
можно, только код будет большой
это и есть велик с квадратными колесами
на нем можно ехать, но очень трудно и неудобно
вонъ тот тви модуль на диаграмме из моего прошлого поста внутри выглядит как еще одна диаграмма

и каждый модуль в этой диаграмме является огромной сложной схемой, правда вникать в них нам уже не надо
и вон те прямоугольники с жирными линиями это регистры с кучей бит, которые очень важны и нужны для понимания
но после того, как ты их понял - тебе не нужно больше о них знать, потому что за тебя умные дядьки уже написали готовыые функции для работы с шиной и тебе надо только почитать про эти функции
возьмем 2 рандомных урока из гугла
https://all-arduino.ru/arduino-dlya-nac ... rfejs-i2c/https://lesson.iarduino.ru/page/urok-4- ... k-arduino/что в них плохого? вроде бы даже нарисованы картинки для даунов и код есть
и если его вставить в редактор, то все соберется и будет работать
дальше возникает вопрос, а откуда берутся вот эти строчки
LiquidCrystal_I2C lcd(0x27,16,2); // Устанавливаем дисплей
lcd.init();
lcd.backlight();// Включаем подсветку дисплея
откуда автор их взял? что значат эти цифры? а какие цифры туда можно вбивать вообще и откуда мне это узнать?
и при чем тут и2ц?
вот поэтому в курсе и дается методика работы с документацией фреймворка, где брать инфу исходную
https://docs.arduino.cc/learn/communication/wire/теперь заглянем в файл, который реализует библиотеку
и минимальные функции для работы с шиной
https://github.com/arduino/ArduinoCore- ... c/Wire.cppbegin() - Initialise the I2C bus
beginTransmission() - Begins queueing up a transmission
endTransmission() - Transmit the bytes that have been queued and end the transmission
end() - Close the I2C bus
Код:
void TwoWire::begin(void)
{
rxBufferIndex = 0;
rxBufferLength = 0;
txBufferIndex = 0;
txBufferLength = 0;
twi_init();
twi_attachSlaveTxEvent(onRequestService); // default callback must exist
twi_attachSlaveRxEvent(onReceiveService); // default callback must exist
}
void TwoWire::begin(uint8_t address)
{
begin();
twi_setAddress(address);
}
void TwoWire::begin(int address)
{
begin((uint8_t)address);
}
внезапно видим, что тут аж 3 функции с одинаковым именем
мало того, эти функции используются еще какие-то функции twi_setAddress и twi_attachSlaveTxEvent twi_attachSlaveRxEvent twi_init
придется и их тоже писать
Код:
void twi_init(void)
{
// initialize state
twi_state = TWI_READY;
twi_sendStop = true; // default value
twi_inRepStart = false;
// activate internal pullups for twi.
digitalWrite(SDA, 1);
digitalWrite(SCL, 1);
// initialize twi prescaler and bit rate
cbi(TWSR, TWPS0);
cbi(TWSR, TWPS1);
TWBR = ((F_CPU / TWI_FREQ) - 16) / 2;
/* twi bit rate formula from atmega128 manual pg 204
SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
note: TWBR should be 10 or higher for master mode
It is 72 for a 16mhz Wiring board with 100kHz TWI */
// enable twi module, acks, and twi interrupt
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
}
Код:
void twi_setAddress(uint8_t address)
{
// set twi slave address (skip over TWGCE bit)
TWAR = address << 1;
}
это хоть не так страшно выглядит
Код:
void twi_attachSlaveTxEvent( void (*function)(void) )
{
twi_onSlaveTransmit = function;
}
тоже вроде мало кода, но что-то он какой-то странный, куча скобок всяких
Код:
void twi_attachSlaveRxEvent( void (*function)(uint8_t*, int) )
{
twi_onSlaveReceive = function;
}
тоже мало кода и тоже похож на предыдущий, но в скобках какая-то другая херня и еще со звездочкой
и это мы расковыряли только первую функцию begin()
оказывается в одной простой функции может быть 100500 функций, в которых могут быть еще 100500 других функций
что же там дальше будет?
Код:
void TwoWire::beginTransmission(uint8_t address)
{
// indicate that we are transmitting
transmitting = 1;
// set address of targeted slave
txAddress = address;
// reset tx buffer iterator vars
txBufferIndex = 0;
txBufferLength = 0;
}
вроде бы тут боле менее просто все
Код:
uint8_t TwoWire::endTransmission(uint8_t sendStop)
{
// transmit buffer (blocking)
uint8_t ret = twi_writeTo(txAddress, txBuffer, txBufferLength, 1, sendStop);
// reset tx buffer iterator vars
txBufferIndex = 0;
txBufferLength = 0;
// indicate that we are done transmitting
transmitting = 0;
return ret;
}
а тут опять какая-то функция twi_writeTo
Код:
uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait, uint8_t sendStop)
{
uint8_t i;
// ensure data will fit into buffer
if(TWI_BUFFER_LENGTH < length){
return 1;
}
// wait until twi is ready, become master transmitter
uint32_t startMicros = micros();
while(TWI_READY != twi_state){
if((twi_timeout_us > 0ul) && ((micros() - startMicros) > twi_timeout_us)) {
twi_handleTimeout(twi_do_reset_on_timeout);
return (5);
}
}
twi_state = TWI_MTX;
twi_sendStop = sendStop;
// reset error state (0xFF.. no error occurred)
twi_error = 0xFF;
// initialize buffer iteration vars
twi_masterBufferIndex = 0;
twi_masterBufferLength = length;
// copy data to twi buffer
for(i = 0; i < length; ++i){
twi_masterBuffer[i] = data[i];
}
// build sla+w, slave device address + w bit
twi_slarw = TW_WRITE;
twi_slarw |= address << 1;
// if we're in a repeated start, then we've already sent the START
// in the ISR. Don't do it again.
//
if (true == twi_inRepStart) {
// if we're in the repeated start state, then we've already sent the start,
// (@@@ we hope), and the TWI statemachine is just waiting for the address byte.
// We need to remove ourselves from the repeated start state before we enable interrupts,
// since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning
// up. Also, don't enable the START interrupt. There may be one pending from the
// repeated start that we sent ourselves, and that would really confuse things.
twi_inRepStart = false; // remember, we're dealing with an ASYNC ISR
startMicros = micros();
do {
TWDR = twi_slarw;
if((twi_timeout_us > 0ul) && ((micros() - startMicros) > twi_timeout_us)) {
twi_handleTimeout(twi_do_reset_on_timeout);
return (5);
}
} while(TWCR & _BV(TWWC));
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE); // enable INTs, but not START
} else {
// send start condition
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTA); // enable INTs
}
// wait for write operation to complete
startMicros = micros();
while(wait && (TWI_MTX == twi_state)){
if((twi_timeout_us > 0ul) && ((micros() - startMicros) > twi_timeout_us)) {
twi_handleTimeout(twi_do_reset_on_timeout);
return (5);
}
}
if (twi_error == 0xFF)
return 0; // success
else if (twi_error == TW_MT_SLA_NACK)
return 2; // error: address send, nack received
else if (twi_error == TW_MT_DATA_NACK)
return 3; // error: data send, nack received
else
return 4; // other twi error
}
Код:
void TwoWire::end(void)
{
twi_disable();
}
какая коротенькая функция, в которой еще какая-то функция
Код:
void twi_disable(void)
{
// disable twi module, acks, and twi interrupt
TWCR &= ~(_BV(TWEN) | _BV(TWIE) | _BV(TWEA));
// deactivate internal pullups for twi.
digitalWrite(SDA, 0);
digitalWrite(SCL, 0);
}
опять какой-то набор каракулей и букв непонятных
теперь представь, что если бы не было фреймворка, то все это гавно пришлось бы тебе писать самому или искать какие-то библиотеки
фреймворк же дает тебе просто готовый инструмент и если ты перейдеш на другой контроллер, то у тебя останутся все названия функций, реализация работы с шиной и2ц на другом контроллере будет скрыта от тебя и тебе не надо будет знать об этом
теперь, зная всю эту херню
https://docs.arduino.cc/learn/communication/wire/ ты можеш открыть библиотеку для работы с 2х строчным индикатором
https://github.com/johnrickman/LiquidCrystal_I2Cтам тоже есть функции
LiquidCrystal()
begin()
clear()
home()
setCursor()
write()
print()
cursor()
noCursor()
blink()
noBlink()
display()
noDisplay()
scrollDisplayLeft()
scrollDisplayRight()
autoscroll()
noAutoscroll()
leftToRight()
rightToLeft()
createChar()
посмотрим фунцию begin()
Код:
oid LiquidCrystal_I2C::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) {
if (lines > 1) {
_displayfunction |= LCD_2LINE;
}
_numlines = lines;
// for some 1 line displays you can select a 10 pixel high font
if ((dotsize != 0) && (lines == 1)) {
_displayfunction |= LCD_5x10DOTS;
}
// SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
// according to datasheet, we need at least 40ms after power rises above 2.7V
// before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50
delay(50);
// Now we pull both RS and R/W low to begin commands
expanderWrite(_backlightval); // reset expanderand turn backlight off (Bit 8 =1)
delay(1000);
//put the LCD into 4 bit mode
// this is according to the hitachi HD44780 datasheet
// figure 24, pg 46
// we start in 8bit mode, try to set 4 bit mode
write4bits(0x03 << 4);
delayMicroseconds(4500); // wait min 4.1ms
// second try
write4bits(0x03 << 4);
delayMicroseconds(4500); // wait min 4.1ms
// third go!
write4bits(0x03 << 4);
delayMicroseconds(150);
// finally, set to 4-bit interface
write4bits(0x02 << 4);
// set # lines, font size, etc.
command(LCD_FUNCTIONSET | _displayfunction);
// turn the display on with no cursor or blinking default
_displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
display();
// clear it off
clear();
// Initialize to default text direction (for roman languages)
_displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
// set the entry mode
command(LCD_ENTRYMODESET | _displaymode);
home();
}
как видиш, в этой библиотеке так же есть функции непонятные
возьмем более понятное нам название
Код:
inline void LiquidCrystal_I2C::command(uint8_t value) {
send(value, 0);
}
она вызывает другую простую функцию
Код:
void LiquidCrystal_I2C::send(uint8_t value, uint8_t mode) {
uint8_t highnib=value&0xf0;
uint8_t lownib=(value<<4)&0xf0;
write4bits((highnib)|mode);
write4bits((lownib)|mode);
}
этой простой функции есть еще функция write4bits
Код:
void LiquidCrystal_I2C::write4bits(uint8_t value) {
expanderWrite(value);
pulseEnable(value);
}
в этой функции есть еще 2 функции
Код:
void LiquidCrystal_I2C::expanderWrite(uint8_t _data){
Wire.beginTransmission(_Addr);
printIIC((int)(_data) | _backlightval);
Wire.endTransmission();
}
слава яйцам
наконец-то мы видим функцию из библиотеки и2ц Wire.beginTransmission и Wire.endTransmission
теперь ты понял, что скрывается под капотом фреймворка, чтобы ты просто смог вывести пару строчек на своем индикаторе при помощи такого кода
Код:
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x27 for a 16 chars and 2 line display
void setup()
{
lcd.init(); // initialize the lcd
lcd.setCursor(3,0);
lcd.print("Hello, world!");
}
void loop()
{
}
если вернуться к твоему аналоговому джойстику, то его можно заставить работать и ходить по меню
за тебя умные дядьки уже написали код библиотеки для работы с аналоговыми кнопками
https://alexgyver.ru/gyverbutton/а теперь посмотри часть библиотеки, которая работает с аналоговыми кнопками
https://github.com/GyverLibs/GyverButto ... nalogKey.hэта часть использует основной файл библиотеки
https://github.com/GyverLibs/GyverButto ... Button.cppпосле всего этого еще раз подумай, стоит ли тратить время на все это