Зачем волноваться и переживать, если можно не волноваться и не переживать?

Меню навигации для мобильных

Цифровое радио (учебно - развлекательная)

Автор zenon, 05 Нояб., 2020, 17:19

« предыдущая - следующая »

zenon

Отвлекаемся от "серьёзных" тем, в целях обучения и развлечения, и схем "выходного дня".
Сыну 15. Кое-что уже сделано, по мере свободного времени что-то творим.
Сейчас в планах по быстрому rda5807m + pro mini, усилитель с темброблоком мы уже сделали, и даже разместили в корпус от коммутатора д-линк.
Схема в аттаче - делали летом, немнго не в тему раздела, но оно пересекается, УНЧ - ВЭФ260 - схема мелькала на коте.
Весь проект с платой если надо выложу, но он сделан на скорую руку, нам нужен был опыт пайки больше.
Библиотека для приемника есть тут (https://github.com/mathertel/Radio).

++
Ну и на очереди вот эти часы (https://radiokot.ru/circuit/digital/game/27/), всё уже есть, кроме своей платы под свои элементы, и датчик надо сделать.
https://github.com/minamonra/

zenon

Собрали тест. Видео (https://youtu.be/xWEQoZh0H1M).
https://github.com/minamonra/

Slabovik

Занятная вещица, но ведь хочется подробностей, так сказать, от первых лиц :)
Ардуиновскую библиотеку готовую, похоже, использовали, не изобретали?
Экранчик, управление?
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Эммм... тут просто скетч для дуины который пишет в регистры rda5807m частоту настройки радиоприёмника по i2c.
Ни экранчика, ни управления. :)
Как вариант находил управление по uart, вэб-морду...
Сама микросхема-то известная и вариантов препараций её много.
#include <Wire.h>              // I2C-Library

double f_ini=102.6;            // Стартовая частота настройки приемника. (Изменяем на нужную).

void setup()                    // Инициализация.
{
  Wire.begin();                // Инициализация двухпроводной шины I2C.
  setFrequency(f_ini);          // Вызываем фукнкцию загрузки частоты настройки в модуль EM5807M.
}

void loop()                    // Основной цикл программы
{
  // Здесь можем написать нашу самую лучшую программу, которая будет работать так как нам нужно.
}
  

void setFrequency(double fmhz)  // Функция загрузки частоты настройки в модуль EM5807M.
{
  int  frequencyB = 4 * (fmhz * 1000000 + 225000) / 32768;
  char frequencyH = frequencyB >> 8;    // Старший байт.
  char frequencyL = frequencyB & 0XFF;  // Накладываем маску 0xFF на младший байт.
  Wire.beginTransmission(0x60);          // Адрес чипа RDA5807M
  Wire.write(frequencyH);                // Старший байт.
  Wire.write(frequencyL);                // Младший байт.
  Wire.write(0xB8);                      // 1011 1000    =Стерео
  Wire.write(0x10);                      // 0001 0000
  Wire.write((byte)0x00);                // 
  Wire.endTransmission();                // формируем I2C-Stop.
} 
Отсюда (https://www.5v.ru/start/em5807m-arduino-nano-30.htm).
Вот ещё (https://tomeko.net/projects/RDA5807M_radio/index.php?lang=en).
И вот этот материал уже интереснее. (https://github.com/mathertel/Radio)
Ну и на коте есть проект.
https://github.com/minamonra/

zenon

Управление rda5807m от stm32f0, перебор забитых в массив станций двумя кнопками на PA4, PA5, i2c на PB6 и PB7.
#include <stm32f0xx.h>
static uint16_t R2H = 0, R3H = 0, R4H = 0, R5H = 0, R6H = 0, R7H = 0, R0AH = 0;
#define CHAN_SHIFT 6
#define FDEL 100
#define RDA5807_OWN_ADDRESS (0x11) // адрес произвольного доступа (порегистровый) I2C_INDX //I2C-Address RDA Chip for Index  Access
//  0x10 // I2C-Address RDA Chip for sequential  Access
//  0x11 // I2C-Address RDA Chip for Index  Access
#define nop()  __NOP()
#define pin_toggle(gpioport, gpios)  do{  \
    register uint32_t __port = gpioport->ODR;  \
    gpioport->BSRR = ((__port & gpios) << 16) | (~__port & gpios);}while(0)
#define pin_set(gpioport, gpios)  do{gpioport->BSRR = gpios;}while(0)
#define pin_clear(gpioport, gpios) do{gpioport->BSRR = ((gpios) << 16);}while(0)
#define pin_read(gpioport, gpios) (gpioport->IDR & (gpios) ? 1 : 0)
#define pin_write(gpioport, gpios)  do{gpioport->ODR = gpios;}while(0)

uint16_t fm_freq_vlg[] = {
  0,
 949, // 0 Радио 7 на семи холмах
 961, // 1 Love Radio
 965, // 2 Комсомольская правда
 972, // 3 Наше радио
 976, // 4 Радио дача
 983, // 5 Радио России
 988, // 6 Радио Energy
 992, // 7 Радио Maximum
1006, // 8 Europa +
1011, // 9 Эхо Москвы
1015, //10 Волгоград FM
1020, //11 Новая Волна
1026, //12 Ретро FM
1031, //13 Авторадио
1036, //14 Дорожное радио
1040, //15 Новое радио
1045, //16 Юмор FM
1051, //17 Радио Спутник
1056  //18 Русское радио
};

char station = 0;
static volatile uint64_t ticks = 0;
uint32_t lticks = 0;
void systick_init(void);
void gpio_init(void);
void i2c_init(void);
void rda5807_write16_reg(uint8_t reg, uint16_t data);
void rda5807_send_reg(uint8_t reg, uint8_t data_h,uint8_t data_l);
uint16_t rda5807_read16_reg(uint8_t reg);
void rda5807_init(uint16_t frequency, char volume, char delay);
void nop_delay(unsigned int i);


int main(void) {
  SystemInit();
  systick_init();
  gpio_init();
  i2c_init();
  rda5807_init(fm_freq_vlg[17], 15, 1);
  while (1) {
    // опрос кнопок раз в 20 ms
    if(lticks > ticks || ticks - lticks > 20) {
      // следующая станция
      if (!pin_read(GPIOA, 1<<4)) {
        if (station > 19) station = 0;
        station++;
        rda5807_init(fm_freq_vlg[station], 15, 1);
      }
      if (!pin_read(GPIOA, 1<<5)) {
        if (station < 1) station = 19; else station--;
        rda5807_init(fm_freq_vlg[station], 15, 1);
      }
    }
  }
} // main(void)

void SysTick_Handler(void) {
  ticks++;
}

void systick_init(void) {
  SystemCoreClockUpdate(); // Make sure SystemCoreClock is up-to-date
  SysTick->LOAD = (SystemCoreClock / 1000) - 1;
  SysTick->VAL = 0;
  SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk;
}

void gpio_init(void) {
  RCC->AHBENR |= RCC_AHBENR_GPIOAEN; // включили переферию A
  RCC->AHBENR  |= RCC_AHBENR_GPIOBEN; // B
  // Кнопки на PA4 PA5
  GPIOA->PUPDR = GPIO_PUPDR_PUPDR4_0; // PA4 как вход с подтяжкой вверх
  GPIOA->PUPDR = GPIO_PUPDR_PUPDR5_0; // PA5

}


void nop_delay(unsigned int i) {
  for (; i>0; i--) {
    for (int j = 0; j < FDEL; ++j) {
      __asm__ __volatile__("nop\n\t":::"memory");
    }
  }
}

void i2c_init(void) {
  //RCC->AHBENR  |= RCC_AHBENR_GPIOBEN; в gpio_init
        
  RCC->APB1ENR  |= RCC_APB1ENR_I2C1EN;
  RCC->CFGR3    |= RCC_CFGR3_I2C1SW; 

  GPIOB->AFR[0] |=  (1<<(4*6)) | (1<<(4*7));
  GPIOB->MODER  &= ~(GPIO_MODER_MODER6  | GPIO_MODER_MODER7  );    // PB6 и PB7
  GPIOB->MODER  |=  (GPIO_MODER_MODER6_1 | GPIO_MODER_MODER7_1);
  GPIOB->OTYPER |=  (GPIO_OTYPER_OT_6    | GPIO_OTYPER_OT_7  );
        
  I2C1->TIMINGR = (uint32_t)0x00B01A4B; 
  I2C1->CR1 = I2C_CR1_PE; 
}
// RDA5807                  STM32F0
// бело-серый  SCL/CLK      PB6      линия тактирования
// черный      SDA/DIO      PB7      линия данных

void rda5807_write16_reg(uint8_t reg, uint16_t data) {
  I2C1->CR2 =  I2C_CR2_AUTOEND | (3<<16) | (RDA5807_OWN_ADDRESS<<1); 

  while (!(I2C1->ISR & I2C_ISR_TXE)); // Check Tx empty

  I2C1->TXDR = reg; // Byte to send
  I2C1->CR2 |= I2C_CR2_START; // Go

  while (!(I2C1->ISR & I2C_ISR_TXIS));
  I2C1->TXDR = (uint8_t)(data>>8); // Byte to send
  
  while (!(I2C1->ISR & I2C_ISR_TXIS));
  I2C1->TXDR = (uint8_t)(data &0x00FF); // Byte to send
}

void rda5807_send_reg(uint8_t reg, uint8_t data_h,uint8_t data_l) {
  I2C1->CR2 =  I2C_CR2_AUTOEND | (3<<16) | (RDA5807_OWN_ADDRESS<<1); 

  while (!(I2C1->ISR & I2C_ISR_TXE)); // Check Tx empty
  I2C1->TXDR = reg; // Byte to send
  I2C1->CR2 |= I2C_CR2_START; // Go

  while (!(I2C1->ISR & I2C_ISR_TXIS));
  I2C1->TXDR = data_h; // Byte to send
  
  while (!(I2C1->ISR & I2C_ISR_TXIS));
  I2C1->TXDR = data_l; // Byte to send
}

uint16_t rda5807_read16_reg(uint8_t reg) {
  uint16_t temp=0;
  I2C1->CR2 =  (1<<16) | (RDA5807_OWN_ADDRESS<<1); 
  while (!(I2C1->ISR & I2C_ISR_TXE));
  I2C1->TXDR = reg; // Byte to send
  I2C1->CR2 |= I2C_CR2_START; // Go
  while (!(I2C1->ISR & I2C_ISR_TC)){};
  I2C1->CR2 =  I2C_CR2_AUTOEND | (2<<16) | 
                (RDA5807_OWN_ADDRESS<<1) |
                          I2C_CR2_RD_WRN |
                            I2C_CR2_NACK; 
  I2C1->CR2 |= I2C_CR2_START; // Go
  while (!(I2C1->ISR & I2C_ISR_RXNE)){};
  temp  = (uint16_t)(I2C1->RXDR <<8);
  while (!(I2C1->ISR & I2C_ISR_RXNE)){};
  temp |=  (uint16_t)I2C1->RXDR; 
  return temp;
}

void rda5807_init(uint16_t frequency, char volume, char delay) {
  if (delay==1) nop_delay(4000);
  
  // R2H = 0, R3H = 0, R4H = 0, R5H = 0, R7H = 0, R0AH = 0;
  // R2H = 0;    0b0000000000000000
  // R2H = R2H | 0b1000000000000000  DHIZ; 
  // R2H = R2H | 0b1000000000000001  ENABLE;

  // регистр 02H
  // DHIZ  (1<<15)
  // DMUTE (1<<14)
  // RENABLE (1<<0)
  // регистр 02H
  //
  R2H = R2H | (1<<15); // DHIZ  (1<<15)
  R2H = R2H | (1<<14); // DMUTE (1<<14)  0 mute, 1 normal operation
  R2H = R2H | (1<<0 ); // ENABLE
  R2H = R2H | (0<<13); // MONO  (0=stereo, 1=mono)
  R2H = R2H | (1<<12); // BASS  (0=disable, 1=enable bass boost)
  R2H = R2H | (0<<2 ); // NEW_METHOD (New  Demodulate Method Enable,  can  improve the receive sensitivity about 1dB)
  R2H = R2H | (0<<11); // RCLK NON-CALIBRATEMODE  0=RCLK clock is always supply 1=RCLK  clock  isnot  always  supply  when  FM work
  // Биты 6:4 CLK_MODE[2:0] кварц 000 = 32.768 он у нас и стоит, менять ничего не надо
 
  rda5807_write16_reg(0x02, R2H); // пишем регистр 02H

  // регистр 03H
  // BAND = 00 (87..108МГц), STEP = 00 (100кГц)
  // 3:2 BAND[1:0]Band Select.  00=87–108 MHz US/Europe 01=76–91 MHz Japan 10=76–108 MHz worldwide 11=65–76 MHz East Europe or 50-65MHz
  // выбор канала, зависит от BAND
  // BAND = 0: частота = интервал между каналами (кГц) * CHAN + 87.0 МГц.
  // BAND = 1 или 2: частота = интервал между каналами (кГц) * CHAN + 76.0 МГц.
  // BAND = 3: частота = интервал между каналами (кГц) * CHAN + 65.0 МГц.
  // частота 10 бит!
  R3H = (frequency - 870) << CHAN_SHIFT;  // chan = (frequency - band) << сдвиг на 6
  R3H = R3H | (1<<4);                    // TUNE после установки частоты установить TUNE бит!!

  rda5807_write16_reg(0x03, R3H);
  // регистр 05H
  R5H  = rda5807_read16_reg(0x05);
  R5H &= ~0x000F; // 0b0000000000001111
  R5H |= volume << 0; // Устанавливаем новую громкость
  rda5807_write16_reg(0x05, R5H);
}

https://github.com/minamonra/

zenon

Измочалил свой девбоард для stm, думал какую плату сделать... вышел гибрид для тестов с радиоприёмником, в основном для освоения...
На схеме всё приблизительно.
По хорошему другой контроллер надо бы сюда, но я ни разу не пробовал ни DS1307 ни память I2C...
5 кнопок, энкодер, выход на пищалку, один выход на реле...
https://github.com/minamonra/

Slabovik

#6
Однако наворочено :)
Но я побрюзжу на пользу дела: обратил внимание, что средняя точка для ОУ организована как-то не очень. Она без фильтрации, что значит, что все помехи с питания VCC1 будут на выходе ОУ с коэффициентом усиления 2.
И ФНЧ килогерц на 15 хорошо бы иметь в дополнение к emphasis коррекции (в вещании на УКВ используется подъём ВЧ с постоянной времени фильтра 50 мкс, следовательно, в приёмнике надо иметь ФНЧ с аналогичными параметрами).
Про LM358 ты и сам знаешь  ;)

А всё-таки вопросик у меня есть: антенну как считал?
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

#7
Плату сделал. Спешка наше всё... Наворотил вроде немного, хочется со всем этим разобраться...
ds1307 по напряжению то не подходит, ну ладно надо поискать ds1338, она вроде пин-то-пин.
Как на stm общаться с rtc по i2c пытаюсь разобраться, пока никак.
С кодом для тюнера вроде всё работает, кроме поиска станций (тут засада, вроде пытаюсь сделать как в даташите, подробнее позже напишу), подсматривал реализацию для avr тут (https://github.com/WiseLord/fm7segm/tree/master/src).
ФНЧ да, надо, может предложишь повторитель/усилитель с однополярным питание сюда? Хотя выход у приюмника сам по себе не слабый и необходимость в ОУ сомнительна, хотя...
Антенну - не рассчитывал.
Вообще надо ещё раз подумать и какой нибудь другой приёмник, качеством получше поискать.
Кстати, при включении через бит ENABLE раздражает щелчок довольно сильный, и похоже это "фича" этой микросхемы, также как и нарастающая громкость после смены частоты.
Такс... eeprom у меня 24cXX надо тоже посмотреть как они с 3.3 вольт дружат.
Часовой кварц откуда-то выдернул, маленких конденсаторов для него в 0805 формате не оказалось под рукой.
Ещё подтяжку i2c разместил в не удачном месте...
Сумбурно написал, весь в ремонте (в кватире ремонт).
//
ы. Появились у меня ещё:
STM32G070RBT6, STM32G030K6T6, STM32G030C8T6, STM32G030F6P6.
Хватит на долгие вечера... :))))


:: добавлено 05 Март, 2023, 14:41
Мысли-коромысли... :)
В tqfp-32 корпусе нет выводов для часового кварца и vbat.
Зато, как оказалось, stm32f030 превращается в stm32f031 простым подключением библиотек от f031 камня, те f030=f031, подсказали на коте, после этого смог подключить IR на TIM2, высвободив TIM3 на энкодер.
фото:
https://github.com/minamonra/

Slabovik

Фильтрация секретов не представляет. Например, здесь (https://anklab.ru/forum/index.php?topic=84.0) я вставлял аж четвёртый порядок. Совершенно не зря, мне понравился звук.

Уровни для всего этого пучка интерфейсов можно согласовывать через TXS0108 (https://anklab.ru/forum/index.php?action=dlattach;topic=128.0;attach=4134) - занятная микросхемка. У нас в магазине такие не продавались, зато продавались готовые платки типа "преобразователь уровня для Ардуины" с этой микросхемкой. забрал все :)
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

#9
В попытка вникнуть в регистры stm32 терплю пока сплошные неудачи, каша в голове никак не систематизируется.
Даташит на 24C16N есть (https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&cad=rja&uact=8&ved=2ahUKEwjsnPKOl8r9AhURrosKHR1TCFoQFnoECAkQAQ&url=http%3A%2F%2Fpdf.datasheet.live%2Fdatasheets-1%2Fatmel%2FAT24C16N-10SA-2.7C.pdf&usg=AOvVaw0kekRKy6KGi2FNyVPgEuTP). референс мануал на F0 есть.
Но чтение/запись из eeprom получилась методом "тыка".
Наиболее подробно процесс нашёл на этом форуме (http://mcu.goodboard.ru/viewtopic.php?id=14#p266). Но там для F4, соответственно регистры в CMSIS тоже не так называются.
С инитом проблем нет, тайминги там только загвоздка, но они уже везде посчитаны.
У меня так:
void i2c_init (void)
{
  RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
      
  RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
  RCC->CFGR3 |= RCC_CFGR3_I2C1SW;
      
  GPIOB->AFR[0] |= (1<<(4*6)) |(1<<(4*7));
  GPIOB->MODER &= ~(GPIO_MODER_MODER6 |GPIO_MODER_MODER7);
  GPIOB->MODER |= (GPIO_MODER_MODER6_1 |GPIO_MODER_MODER7_1);
  GPIOB->OTYPER |=(GPIO_OTYPER_OT_6 |GPIO_OTYPER_OT_7);
  I2C1->TIMINGR = (uint32_t)0x00B01A4B;
  I2C1->CR1 = I2C_CR1_PE;
}


Чтение eeprom страницей сделал так:
void zeeprom_read_i2c (uint8_t addr, uint8_t *data, uint8_t nbytes) {
  uint8_t i;  // 

  I2C1->CR2 = (1 << 16) | I2C_CR2_START | addr;

  while((I2C1->ISR & I2C_ISR_TXIS) != I2C_ISR_TXIS);
  I2C1->TXDR = 0;
  while((I2C1->ISR & I2C_ISR_TC) != I2C_ISR_TC);
  I2C1->CR2  = I2C_CR2_AUTOEND | (nbytes << 16) | I2C_CR2_RD_WRN | addr;
  I2C1->CR2 |= I2C_CR2_START;

  for(i = 0; i < nbytes; i++) {
    while((I2C1->ISR & I2C_ISR_RXNE) != I2C_ISR_RXNE);
    *data++ = I2C1->RXDR;
  }
  delay_us(5);
}

nbytes тут 16 (по числу байт на странице, можно и константой сделать), а вот адресация страниц получилась странная, если читать передавая адрес 0xA0, то читается 1-ая страница, 0xA1 - вторая итд до 16-ой. Читаются правильно. Но как-то не правильно вроде так адресовать страницы.
Эмм от какого момента я до этого дошёл,... просто попробовал написать сканер для i2c ну и увидел адреса:
...OK!i: 32
...OK!i: 33
...OK!i: 34
...OK!i: 35

...OK!i: 160  0xA0
...OK!i: 161  0xA1
...OK!i: 162  0xA2
...OK!i: 163  0xA3
...OK!i: 164  0xA4
...OK!i: 165  0xA5
...OK!i: 166  0xA6
...OK!i: 167  0xA7
...OK!i: 168  0xA8
...OK!i: 169  0xA9
...OK!i: 170  0xAA
...OK!i: 171  0xAB
...OK!i: 172  0xAC
...OK!i: 173  0xAD
...OK!i: 174  0xAE
...OK!i: 175  0xAF



...OK!i: 192
...OK!i: 193


:: добавлено 08 Март, 2023, 00:04
Так... запись, а вот запись страницы выглядит сейчас так:
void zeeprom_write_i2c (uint8_t addr, uint8_t *data, uint8_t nbytes)  {
  uint8_t i = 0;
  I2C1->CR2 = I2C_CR2_RELOAD | (1 << 16) | I2C_CR2_START | addr;
  while((I2C1->ISR & I2C_ISR_TXIS) != I2C_ISR_TXIS);
    
  I2C1->TXDR = 0;
    
  while((I2C1->ISR & I2C_ISR_TCR) != I2C_ISR_TCR);
  //I2C1->CR2 = I2C_CR2_AUTOEND | (EEPROM_PAGE_SIZE << 16) | addr;
  I2C1->CR2 = I2C_CR2_AUTOEND | (nbytes<<16) | addr | 1 |  I2C_CR2_RD_WRN;
//I2C1->CR2 = addr | 1 | I2C_CR2_AUTOEND | I2C_CR2_RD_WRN;    
  for(i = 0; i < nbytes; i++)
  {
    while((I2C1->ISR & I2C_ISR_TXIS) != I2C_ISR_TXIS);
    I2C1->TXDR = data[i];
  }
  delay_us(5);
}
Но пишет неправильно, сразу две страницы заполняет, например выполнив zeeprom_write_i2c(0xA0, gDB, 16);, значения массива gDB будут в адресе 0xA0 и точно такие же в 0xA1.
https://github.com/minamonra/

zenon

#10
В общем с eeprom поступил так:
static uint8_t 
EEaddr [8] = { 160, // 0
               162, // 1
               164, // 2
               166, // 3
               168, // 4
               170, // 5
               172, // 6
               174  // 7
             }; 

Сделал чтение при загрузке параметров из eeprom, настройка ручная, регулировка громкости.
Сохраняются текущие настройки пока кнопкой.
Отображение громкости с включенным усилением баса vbXX, с выключенным voXX.
Меню сохранения станций додумаю и вроде на этой плате всё.
Реализацию часов уже на другой плате, будет или PCF8563T, которые едут, или поставлю камень из новых STM32G030K6T6, он почти такой же, но пины по другому сделали, ножки на питание сократили, есть выводы для часового кварца, так что для часов он лучше, не нужна лишняя микросхема.
За столом у меня приём ужасный, на самом деле принимает лучше.



аааа! Индикатор этот меня подвел! Нет в нём точек. Только двоеточие, точки есть у 14-ти пиновых, у 12 - нет, надо было сразу смотреть, так что при покупке надо лишний раз обращать внимание.
https://github.com/minamonra/

zenon

ы. а с антенной засада, те на плате её не знаю как на УКВ сделать. Только на свежем воздухе эта нормально работает... :)
https://github.com/minamonra/

zenon

Так конечно адреса чётными будут блин, на 1 заканчивается адрес если это запись... дошло таки.
https://github.com/minamonra/

zenon

#13
В общем победил лень.
Запустил анализатор (дешёвый китайский).
Про 24c16.
Объём всего 16 Кбит, те 2 Кбайт. Те 2048 ячейки по 8 бит.
Исходя из DS память организованна в 128 страниц по 16 ячеек.
Запись страницы = передать адрес кратный 16-ти, передать адрес ячейки, передать 16 байт, стоп.
Чтения страницы отдельной операцией нет, только последовательное чтение по адресу из 8 банков по 256 байт максимум.
Сделал чтение 16-ти байт для того чтобы было красиво.
Собственно чтение (адрес передаётся кратным 16, от 0 до 2032):
uint8_t ee24c16read_page (uint16_t addr, uint8_t* buff) {
  cntr = ttms;
  uint8_t addr_tmp = EE16ADDR + (addr/255<<1);

  I2C1->CR2 = 1<<16 | I2C_CR2_START | addr_tmp;
  while((I2C1->ISR & I2C_ISR_TXIS)==0) { if(ttms - cntr > I2CWAIT) return 0; }

  I2C1->TXDR = (uint8_t) addr;
  while((I2C1->ISR & I2C_ISR_TC)==0) { if(ttms - cntr > I2CWAIT) return 0; }

  I2C1->CR2 = 16<<16 | I2C_CR2_START | addr_tmp | I2C_CR2_AUTOEND | I2C_CR2_RD_WRN;
  for(uint8_t i=0; i<=16-1; i++) {
    while((I2C1->ISR & I2C_ISR_RXNE)==0) { if(ttms - cntr > I2CWAIT) return 0; }
    *(buff+i) = (uint8_t)I2C1->RXDR;
    buff[i]  = I2C1->RXDR ;
  }
  return 1;
}


Запись:
uint8_t ee24c16write_page (uint16_t addr, uint8_t *buff)
{
  uint8_t addr_tmp = EE16ADDR + (addr/255<<1);
  
  cntr = ttms;
  I2C1->CR2 = I2C_CR2_AUTOEND | (17<<16) | (addr_tmp) | I2C_CR2_START;
  while (!(I2C1->ISR & I2C_ISR_TXE))  { if(ttms - cntr > I2CWAIT) return 0; }; // TXE регистр данных передачи пуст (буфер передачи пуст)

  I2C1->TXDR =(uint8_t) addr;  
  while (!(I2C1->ISR & I2C_ISR_TXIS)) { if(ttms - cntr > I2CWAIT) return 0; };
  
  cntr = ttms;
  for (uint8_t i = 0; i <= 16 - 1; i++) {
    while (!(I2C1->ISR & I2C_ISR_TXIS))  // TXIS // ждем пока TXDR станет пустым и готовым к новым данным
    if(ttms - cntr > I2CWAIT) return 0;  //
    I2C1->TXDR = buff[i] ; //
  }
  while((I2C1->ISR & I2C_ISR_STOPF)==0) {}; // не знаю нужна проверка?
  return 1;
}
EE16ADDR      (0xA0)
ttms - из прерывания системного тикера (1/1000).
Вроде нормально работает?
В архиве сэйвы логгера.
Постранично:
uint8_t ee24c16rp(uint8_t page, uint8_t* buff)
{
  uint8_t addr = page * 16;
  return ee24c16read_page (addr, buff);
}

uint8_t ee24c16wp(uint8_t page, uint8_t *buff)
{
  uint8_t addr = page * 16;
  return ee24c16write_page(addr, buff);
}
В памяти 24c32/64 организация другая - по 32 байта странички, и для передачи нужен адрес из двух байт.
Чуть позже выложу тоже.
https://github.com/minamonra/

Slabovik

Уу, слона-то я и не заметил. Смотрел в код, а видел фигу, т.к. надо подпрограммы расколупывать, бррр...
Поздравляю с удачной отладкой :)

А по антенне маленько грустно. Для нормального приёма нужен штырь сантиметров 70-80, ну хотя бы 50, и противовес, в качестве которого можно использовать проводящий корпус-общий провод. Ибо суть антенны - собрать как можно больше распределённой по пространству энергии волн, а лучше всего это делают антенны, соизмеримые с размерами самих волн. Поэтому такие маленькие - это уже на гигагерцы. Честно говоря, я их считать не умею, потому и спросил про расчёт.
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

Ну да L/4, я помню...
Где-то проскакивало про фильтр простейший, прицепил, и! однако стало лучше.
Усилитель какой может попробовать? Хотя чувствительность у него и так не плохая.
Присматриваюсь к вот такому экземпляру TEF6686 (https://aliexpress.ru/item/1005004600166150.html?spm=a2g2w.cart.cart_split.26.4ea44aa67gI1cr&sku_id=12000029783044527).
Не знаю как на него с кодом...
Что-то меня на радиоприём потянуло...
Не замахнуться ли нам на что-нибудь с синтезатором? Но не слишком уж сложное, ёмкости маленькие и настройка контуров пугает...
https://github.com/minamonra/

zenon

#16
Почти все цели достигнуты.
С RTC всё оказалось просто, будильники осталось.
Не дождался модулей с али, взял ds3231.
Естественно некоторые вещи не выдумывал, подглядел... на то он и гитхаб. :)
i2c:
void i2c_init (void) {
  RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
      
  RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
  RCC->CFGR3 |= RCC_CFGR3_I2C1SW;
      
  GPIOB->AFR[0] |= (1<<(4*6)) |(1<<(4*7));
  GPIOB->MODER &= ~(GPIO_MODER_MODER6 |GPIO_MODER_MODER7);
  GPIOB->MODER |= (GPIO_MODER_MODER6_1 |GPIO_MODER_MODER7_1);
  GPIOB->OTYPER |=(GPIO_OTYPER_OT_6 |GPIO_OTYPER_OT_7);
  I2C1->TIMINGR = (uint32_t)0x00B01A4B;
  I2C1->CR1 = I2C_CR1_PE;
}

uint8_t i2c_write(uint8_t addr, uint8_t *data, uint8_t nbytes) {
  cntr = ttms;
  while(I2C1->ISR & I2C_ISR_BUSY) { if(ttms - cntr > I2C_TIMEOUT) return 0; }

  cntr = ttms;
  I2C1->CR2 = (nbytes << 16) | addr | I2C_CR2_AUTOEND | I2C_CR2_START; // autoend // now start transfer
  while(I2C1->CR2 & I2C_CR2_START) { if(ttms - cntr > I2CWAIT) return 0; }
    
  for(int i = 0; i < nbytes; ++i){
    cntr = ttms;
    while(!(I2C1->ISR & I2C_ISR_TXIS)){ if(ttms - cntr > I2CWAIT) return 0; }
    I2C1->TXDR = data[i]; // send data
  }
  while(I2C1->ISR & I2C_ISR_BUSY){ if(ttms - cntr > I2CWAIT) break; } // wait for data gone
  return 1;
}

uint8_t i2c_read(uint8_t addr, uint8_t *data, uint8_t nbytes) {
  cntr = ttms;
  while (I2C1->ISR & I2C_ISR_BUSY) { if(ttms - cntr > I2CWAIT) return 0; }
  cntr = ttms;
  while (I2C1->CR2 & I2C_CR2_START) { if(ttms - cntr > I2CWAIT) return 0; }
  // read N bytes
  I2C1->CR2 = (nbytes<<16) | addr | 1 | I2C_CR2_AUTOEND | I2C_CR2_RD_WRN;
  I2C1->CR2 |= I2C_CR2_START;

  for (uint8_t i = 0; i < nbytes; ++i) {
    cntr = ttms;
    while (!(I2C1->ISR & I2C_ISR_RXNE)) { if(ttms - cntr > I2CWAIT) return 0; }
    *data++ = I2C1->RXDR;
  }
  return 1;
}

Запись/чтение в/из rda5807:
static uint8_t rda5807writeI2C(uint8_t nbytes) {
  cntr = ttms;
  I2C1->CR2 = (nbytes << 16) | RDA5807M_I2C_ADDR | I2C_CR2_AUTOEND | I2C_CR2_START;
  while(I2C1->CR2 & I2C_CR2_START) { if(ttms - cntr > I2C_TIMEOUT) return 0; }

  for (int i = 0; i < nbytes; ++i){
    cntr = ttms;
    while(!(I2C1->ISR & I2C_ISR_TXIS)) { if(ttms - cntr > I2CWAIT) return 0; };
    I2C1->TXDR = wrBuf[i]; // wrBuff[14]
  }
  
  while(I2C1->ISR & I2C_ISR_BUSY) { if(ttms - cntr > I2CWAIT) break; } // wait for data gone
  return 1;
}


uint8_t *rda580xReadStatus (void) {
  uint8_t i;
  i2c_read(RDA5807M_I2C_ADDR, rdBuf, sizeof(rdBuf));  //rdBuf[12];
  return rdBuf;
}

ds3231.h:
struct ds3231_calendar {
    uint8_t second;
    uint8_t minute;
    uint8_t hour;
    uint8_t dayWeek;
    uint8_t dayMonth;
    uint8_t month;
    uint8_t year;
};

#define DS3231_I2C_ADDR_WRITE                     (uint8_t)0xD0
#define DS3231_I2C_ADDR_READ                      (uint8_t)0xD1

#define DS3231_SEC_ADDR                           (uint8_t)0x0
#define DS3231_MIN_ADDR                           (uint8_t)0x1
#define DS3231_HOUR_ADDR                          (uint8_t)0x2
#define DS3231_DAY_WEEK_ADDR                      (uint8_t)0x3
#define DS3231_DAY_MONTH_ADDR                     (uint8_t)0x4
#define DS3231_MONTH_ADDR                         (uint8_t)0x5
#define DS3231_YEAR_ADDR                          (uint8_t)0x6
#define DS3231_A1M1_ADDR                          (uint8_t)0x7
#define DS3231_A1M2_ADDR                          (uint8_t)0x8
#define DS3231_A1M3_ADDR                          (uint8_t)0x9
#define DS3231_A1M4_ADDR                          (uint8_t)0xA
#define DS3231_CONTROL_ADDR                       (uint8_t)0xE
#define DS3231_CON_STAT_ADDR                      (uint8_t)0xF

void ds3231init();
void ds3231get(struct ds3231_calendar*);
void ds3231set(struct ds3231_calendar*);
ds3231.c:
void ds3231init() {
  uint8_t init[3] = {DS3231_CONTROL_ADDR, 0x0, 0x0};
  i2c_write(DS3231_I2C_ADDR_WRITE, init, 3);
}

// получение даты и времени из RTC
void ds3231get(struct ds3231_calendar* cal) {
  uint8_t date[7] = {0};
  uint8_t addr = 0;
  i2c_write(DS3231_I2C_ADDR_WRITE, &addr, 1);
  i2c_read(DS3231_I2C_ADDR_READ, date, 7);        
  cal->second=date[0];
  cal->minute=date[1];
  cal->hour=date[2];
  cal->dayWeek=date[3];
  cal->dayMonth=date[4];
  cal->month=date[5];
  cal->year=date[6];
}

// записать дату и время в RTC
void ds3231set(struct ds3231_calendar* cal) {
  uint8_t new_data[8] = {
    DS3231_SEC_ADDR, 
    cal->second,
    cal->minute,
    cal->hour,
    cal->dayWeek,
    cal->dayMonth,
    cal->month,
    cal->year
  };
  i2c_write(DS3231_I2C_ADDR_WRITE, new_data, 8);
}
ы. Напомню всё для F0 серии.

:: добавлено 05 Апр., 2023, 14:13
И 24c32/64:
Тут уже есть операция чтения страницы (не как в 24c16 только последовательное чтение).
addr тут кратен 32-ум.
uint8_t eeprom24c64pgread(uint16_t addr, uint8_t* buff, uint8_t nbytes) {
  I2C1->CR2 = (1 << 16) | I2C_CR2_START | (EEADDR << 1); // пишем 1 байт, старт, адрес ведомого
  while((I2C1->ISR & I2C_ISR_TXE) != I2C_ISR_TXE) {}; // ждем передачи

  I2C1->CR2 = (2 << 16) | (EEADDR << 1);
  while (!(I2C1->ISR & I2C_ISR_TXE)) {};
  cntr = ttms;
  I2C1->TXDR = (uint8_t) (addr >> 8); // Byte to send
  I2C1->CR2 |= I2C_CR2_START; // Go
  while (!(I2C1->ISR & I2C_ISR_TXIS)) { if(ttms - cntr > I2CWAIT) return 0; } // check start

  I2C1->TXDR = (uint8_t)(addr & 0x00FF); // Byte to send 
  while (!(I2C1->ISR & I2C_ISR_TC)) {};

  //          атостоп           
  I2C1->CR2 = I2C_CR2_AUTOEND | (nbytes<<16) | (EEADDR<<1) | I2C_CR2_RD_WRN | I2C_CR2_NACK;
  I2C1->CR2 |= I2C_CR2_START; // Go

  cntr = ttms;
  for (uint8_t i = 0; i <= nbytes - 1; i++) {
    while (!(I2C1->ISR & I2C_ISR_RXNE))  // wait for data
    if(ttms - cntr > I2CWAIT) { return 0; break; }
    buff[i]  = I2C1->RXDR ;
  }
  return 1;
}

uint8_t eeprom24c64pgwrite(uint16_t addr, uint8_t *buff, uint8_t nbytes) {
  I2C1->CR2 =  I2C_CR2_AUTOEND | ((2+nbytes)<<16) | (EEADDR<<1);
  while (!(I2C1->ISR & I2C_ISR_TXE)) {}; // TXE регистр данных передачи пуст (буфер передачи пуст) // Check Tx empty
   
  I2C1->TXDR =(uint8_t) (addr>>8); // младшая часть адреса страницы (при присвоении к 8-ми битной переменной, старший байт сам улетит)
  I2C1->CR2 |= I2C_CR2_START; // Go
      
  while (!(I2C1->ISR & I2C_ISR_TXIS)) {}; //TXIS
  I2C1->TXDR = (uint8_t)(addr & 0x00FF); // старшая часть адреса страницы
  
  cntr = ttms;
  for (uint8_t i = 0; i <= nbytes - 1; i++) {
    while (!(I2C1->ISR & I2C_ISR_TXIS)) //TXIS // ждем пока TXDR станет пустым и готовым к новым данным
    if(ttms - cntr > I2CWAIT) return 4;  // return 0;  // check busy
    I2C1->TXDR = buff[i] ; // Byte to send
  }
  return 1;
}
ыы. Да, пободаться i2c в stm32 заставил... зато сейчас вроде как всё встало на свои места. :)
https://github.com/minamonra/

Slabovik

Синтезаторы можно попробовать TEA7001, TSA6057. Правда, они довольно старые и и надо пятивольтовое питание. А вот по логическим уровням SDA SCL трёхвольтовое управление должно вроде вписаться, при условии, что управляющие сигналы КМОП-уровней (с этим у STM проблем нет - они КМОП от рождения). Надо только подумать (а лучше почитать), как их к радиочасти прикрутить. Но там не сложно, можно использовать и микросхемы "всё а одном". Отличие только в том, что настройкой гетеродинного контура будет синтезатор управлять, подавая напряжение на его варикапы, а не пользователь.
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

#18
Самые доступные сейчас модули на Si5351A. AD9834 как-то не для экспериментов.
А аналоговая часть?
Сейчас поглядываю на SI4735... Ещё FMdx хочу понять что из себя представляет, пощупать надо этот TEF.
ы. Зашёл на гитхаб поиск по TEF6686 выдал кучу результатов, что уже радует.
https://github.com/minamonra/

Slabovik

Тут надо бы определиться, делать цифровой приёмник на микросхеме (Si4735), либо использовать синтезатор для управления аналоговым трактом. Я цифровых приёмников до сих пор не собирал совсем. Но глянул - у этой Si4735 внутри всё есть, вопрос только в управлении ею. TEF примерно то же самое.
Но вообще да, аналоговый тракт будет собрать сложнее, как ни странно, и от контуров тоже никуда не уйти. Фишка будет только в точной установке частоты гетеродина, она не будет плавать (ну, почти, т.к. генератор кварцован) и цифровом управлении ею.

Цифровая же микросхема - это всё внутри, обработка тоже цифровая. Управлять просто, а как оно внутри.... тут мои знания дают пробел. По крайней мере я точно знаю, что повлиять на то, что внутри, уже не получится...
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

#20
Цитата: Slabovik от 06 Апр., 2023, 17:43Я цифровых приёмников до сих пор не собирал совсем.
Будем пробовать 4735?
Сегодня заказал на пробу тут (https://aliexpress.ru/item/1005003147639640.html?spm=a2g2w.orderdetail.0.0.4ce34aa6BG8jFW&sku_id=12000024356825873).
Из МК думаю расчехлить из новых какой-нибудь G030/G070.
Дисплеи есть 1.77TFT (https://aliexpress.ru/item/1005001621932172.html?spm=a2g2w.orderdetail.0.0.6b564aa6rQa79R&sku_id=12000018917906810).
ы. Набрёл на статью в хакере (https://xakep.ru/2021/07/22/diy-si473x/).

:: добавлено 07 Апр., 2023, 17:22
Добавлю схему и проект в segger на текущий проект.
Многое не оптимально, есть функции которые только для эксперимента.
Управление только с пульта (с энкодером ещё предстоит разобраться), установка времени по длинному нажатию utf, добавление в память станций по длинному mode.
Программу управления дисплей+пульт писал впервые, поэтому вышло не айс, но работает, надо бы переделать получше обдумав алгоритм, но пока так.
Работает норм, станции с кнопок 0...9 (можно расширить до двухзнаков, но не знаю), вкл/выкл баса, mute, настройка на частоту ручная.
rds естественно нет, куда ж его выведешь в данном исполнении? :)
f031_radioclock - проект в KiCad.
z137rcc - проект в Segger.
https://github.com/minamonra/

Slabovik

#21
Чувствую, что я за тобой просто не успею...
зы: а Хакер не даёт прочитать статью полностью :(
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

zenon

#22
:)  ;)
Прицепил статью.

:: добавлено 12 Апр., 2023, 21:58
https://github.com/minamonra/

zenon

Доехал до меня модуль PCF8563, убираю не глядя DS2331, подключаю новый...
Пытаюсь запустить... ну блин лажа какая-то... ну нет чтобы на адреса посмотреть... у 24C16 они уже заняты (0xA3 и 0xA2), так ещё она и припаяна под индикатором и просто так её не снять.. Пришлось пока обратно всё верстать.
Да, разводка у этого китайского модуля не очень, часовой кварц не припаян к массе и дороги как антенны.
https://github.com/minamonra/

zenon

Фильтр который был спаян навесом показал себя хорошо, на столе заметно уменьшились помехи от всего импульсного.
Реализация взята отсюда (http://dedclub.blogspot.com/2018/02/fm-875-108.html).
Схема радио-часов сейчас приобретает такой вид:
https://github.com/minamonra/