[PIC®/AVR®/dsPIC®产品] ATtiny85驱动RDA5807的收音机应用

[复制链接]
 楼主| 598330983 发表于 2024-9-28 13:33 | 显示全部楼层 |阅读模式
游客,如果您要查看本帖隐藏内容请回复



  1. // ===================================================================================
  2. // Project:   tinyFMradio - FM Tuner with RDS based on ATtiny45/85 and RDA5807
  3. // Version:   v1.0
  4. // Year:      2019 - 2021
  5. // Author:    Stefan Wagner
  6. // Github:    https://github.com/wagiminator
  7. // EasyEDA:   https://easyeda.com/wagiminator
  8. // License:   http://creativecommons.org/licenses/by-sa/3.0/
  9. // ===================================================================================
  10. //
  11. // Description:
  12. // ------------
  13. // This is just a demo sketch that implements basic functionality. By pressing
  14. // the rotary encoder button the RDA5807 seeks the next radio station. Turning
  15. // the rotary encoder increases/decreases the volume. Selected frequency and
  16. // volume are stored in the EEPROM. Station name, frequency, signal strength,
  17. // volume and battery state of charge are shown on an OLED display.
  18. //
  19. // References:
  20. // -----------
  21. // RDA5807 datasheet:
  22. // https://datasheet.lcsc.com/szlcsc/1806121226_RDA-Microelectronics-RDA5807MP_C167245.pdf
  23. //
  24. // SSD1306 OLED datasheet:
  25. // https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
  26. //
  27. // The OLED font was adapted from Neven Boyanov and Stephen Denne:
  28. // https://github.com/datacute/Tiny4kOLED
  29. //
  30. // The RDA8507 implementation was adapted from Maarten Janssen:
  31. // https://hackaday.io/project/9009-arduino-radio-with-rds
  32. //
  33. // Wiring:
  34. // -------
  35. //                              +-\/-+
  36. //           --- RST ADC0 PB5  1|°   |8  Vcc
  37. // Encoder A ------- ADC3 PB3  2|    |7  PB2 ADC1 -------- OLED/RDA SCK
  38. // Encoder B ------- ADC2 PB4  3|    |6  PB1 AIN1 OC0B --- Encoder SW
  39. //                        GND  4|    |5  PB0 AIN0 OC0A --- OLED/RDA SDA
  40. //                              +----+
  41. //
  42. // Compilation Settings:
  43. // ---------------------
  44. // Core:    ATtinyCore (https://github.com/SpenceKonde/ATTinyCore)
  45. // Board:   ATtiny25/45/85 (No bootloader)
  46. // Chip:    ATtiny 45 or ATtiny85 (depending on your chip)
  47. // Clock:   1 MHz (internal)
  48. // B.O.D:   disabled
  49. //
  50. // Leave the rest on default settings. Don't forget to "Burn bootloader"!
  51. // No Arduino core functions or libraries are used. Use the makefile if
  52. // you want to compile without Arduino IDE.
  53. //
  54. // Fuse settings: -U lfuse:w:0x62:m -U hfuse:w:0xd7:m -U efuse:w:0xff:m


  55. // ===================================================================================
  56. // Libraries, Definitions and Macros
  57. // ===================================================================================

  58. // Libraries
  59. #include <avr/io.h>                 // for GPIO
  60. #include <avr/eeprom.h>             // for storing user settings into EEPROM
  61. #include <avr/pgmspace.h>           // for reading data from program memory
  62. #include <avr/interrupt.h>          // for interrupt functions
  63. #include <util/delay.h>             // for delays

  64. // Pin assignments
  65. #define PIN_SDA       PB0           // I2C Serial Data,  connect to OLED/RDA
  66. #define PIN_SCL       PB2           // I2C Serial Clock, connect to OLED/RDA
  67. #define PIN_ENC_SW    PB1           // pin connected to rotary encoder switch
  68. #define PIN_ENC_A     PB3           // pin connected to rotary encoder A
  69. #define PIN_ENC_B     PB4           // pin connected to rotary encoder B

  70. // EEPROM identifier
  71. #define EEPROM_IDENT  0x6CE7        // to identify if EEPROM was written by this program

  72. // Text strings
  73. const char HEADER[] PROGMEM = "Tiny FM Radio v1.0";

  74. // Variables
  75. uint16_t  channel;                  // 0 .. 1023
  76. uint8_t   volume = 1;               // 0 .. 15

  77. // Pin manipulation macros
  78. #define pinInput(x)   DDRB  &= ~(1<<(x))        // set pin to INPUT
  79. #define pinOutput(x)  DDRB  |=  (1<<(x))        // set pin to OUTPUT
  80. #define pinLow(x)     PORTB &= ~(1<<(x))        // set pin to LOW
  81. #define pinHigh(x)    PORTB |=  (1<<(x))        // set pin to HIGH
  82. #define pinPullup(x)  PORTB |=  (1<<(x))        // enable PULLUP resistor
  83. #define pinIntEn(x)   PCMSK |=  (1<<(x))        // enable pin change interrupt
  84. #define pinIntDis(x)  PCMSK &= ~(1<<(x))        // disable pin change interrupt
  85. #define pinRead(x)    (PINB &   (1<<(x)))       // READ pin

  86. // ===================================================================================
  87. // I2C Master Implementation
  88. // ===================================================================================

  89. // I2C macros
  90. #define I2C_SDA_HIGH()  pinInput(PIN_SDA)       // release SDA   -> pulled HIGH by resistor
  91. #define I2C_SDA_LOW()   pinOutput(PIN_SDA)      // SDA as output -> pulled LOW  by MCU
  92. #define I2C_SCL_HIGH()  pinInput(PIN_SCL)       // release SCL   -> pulled HIGH by resistor
  93. #define I2C_SCL_LOW()   pinOutput(PIN_SCL)      // SCL as output -> pulled LOW  by MCU
  94. #define I2C_SDA_READ()  pinRead(PIN_SDA)        // read SDA line
  95. #define I2C_CLOCKOUT()  I2C_SCL_HIGH();I2C_SCL_LOW()  // clock out

  96. // I2C transmit one data byte to the slave, ignore ACK bit, no clock stretching allowed
  97. void I2C_write(uint8_t data) {
  98.   for(uint8_t i=8; i; i--, data<<=1) {          // transmit 8 bits, MSB first
  99.     (data&0x80)?I2C_SDA_HIGH():I2C_SDA_LOW();   // SDA depending on bit
  100.     I2C_CLOCKOUT();                             // clock out -> slave reads the bit
  101.   }
  102.   I2C_SDA_HIGH();                               // release SDA for ACK bit of slave
  103.   I2C_CLOCKOUT();                               // 9th clock pulse is for the ignored ACK bit
  104. }

  105. // I2C start transmission
  106. void I2C_start(uint8_t addr) {
  107.   I2C_SDA_LOW();                                // start condition: SDA goes LOW first
  108.   I2C_SCL_LOW();                                // start condition: SCL goes LOW second
  109.   I2C_write(addr);                              // send slave address
  110. }

  111. // I2C restart transmission
  112. void I2C_restart(uint8_t addr) {
  113.   I2C_SDA_HIGH();                               // prepare SDA for HIGH to LOW transition
  114.   I2C_SCL_HIGH();                               // restart condition: clock HIGH
  115.   I2C_start(addr);                              // start again
  116. }

  117. // I2C stop transmission
  118. void I2C_stop(void) {
  119.   I2C_SDA_LOW();                                // prepare SDA for LOW to HIGH transition
  120.   I2C_SCL_HIGH();                               // stop condition: SCL goes HIGH first
  121.   I2C_SDA_HIGH();                               // stop condition: SDA goes HIGH second
  122. }

  123. // I2C receive one data byte from the slave (ack=0 for last byte, ack>0 if more bytes to follow)
  124. uint8_t I2C_read(uint8_t ack) {
  125.   uint8_t data = 0;                             // variable for the received byte
  126.   I2C_SDA_HIGH();                               // release SDA -> will be toggled by slave
  127.   for(uint8_t i=8; i; i--) {                    // receive 8 bits
  128.     data <<= 1;                                 // bits shifted in right (MSB first)
  129.     I2C_SCL_HIGH();                             // clock HIGH
  130.     if(I2C_SDA_READ()) data |= 1;               // read bit
  131.     I2C_SCL_LOW();                              // clock LOW -> slave prepares next bit
  132.   }
  133.   if(ack) I2C_SDA_LOW();                        // pull SDA LOW to acknowledge (ACK)
  134.   I2C_CLOCKOUT();                               // clock out -> slave reads ACK bit
  135.   return data;                                  // return the received byte
  136. }

  137. // ===================================================================================
  138. // OLED Implementation
  139. // ===================================================================================

  140. // OLED definitions
  141. #define OLED_ADDR       0x78                    // OLED write address
  142. #define OLED_CMD_MODE   0x00                    // set command mode
  143. #define OLED_DAT_MODE   0x40                    // set data mode
  144. #define OLED_INIT_LEN   9                       // length of init command array

  145. // OLED init settings
  146. const uint8_t OLED_INIT_CMD[] PROGMEM = {
  147.   0xC8, 0xA1,                                   // flip screen
  148.   0xA8, 0x1F,                                   // set multiplex ratio
  149.   0xDA, 0x02,                                   // set com pins hardware configuration
  150.   0x8D, 0x14,                                   // set DC-DC enable
  151.   0xAF                                          // display on
  152. };

  153. // Standard ASCII 5x8 font (adapted from Neven Boyanov and Stephen Denne)
  154. const uint8_t OLED_FONT[] PROGMEM = {
  155.   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00,
  156.   0x14, 0x7F, 0x14, 0x7F, 0x14, 0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x23, 0x13, 0x08, 0x64, 0x62,
  157.   0x36, 0x49, 0x55, 0x22, 0x50, 0x00, 0x05, 0x03, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x41, 0x00,
  158.   0x00, 0x41, 0x22, 0x1C, 0x00, 0x14, 0x08, 0x3E, 0x08, 0x14, 0x08, 0x08, 0x3E, 0x08, 0x08,
  159.   0x00, 0x00, 0xA0, 0x60, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x60, 0x60, 0x00, 0x00,
  160.   0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, 0x42, 0x7F, 0x40, 0x00,
  161.   0x42, 0x61, 0x51, 0x49, 0x46, 0x21, 0x41, 0x45, 0x4B, 0x31, 0x18, 0x14, 0x12, 0x7F, 0x10,
  162.   0x27, 0x45, 0x45, 0x45, 0x39, 0x3C, 0x4A, 0x49, 0x49, 0x30, 0x01, 0x71, 0x09, 0x05, 0x03,
  163.   0x36, 0x49, 0x49, 0x49, 0x36, 0x06, 0x49, 0x49, 0x29, 0x1E, 0x00, 0x36, 0x36, 0x00, 0x00,
  164.   0x00, 0x56, 0x36, 0x00, 0x00, 0x08, 0x14, 0x22, 0x41, 0x00, 0x14, 0x14, 0x14, 0x14, 0x14,
  165.   0x00, 0x41, 0x22, 0x14, 0x08, 0x02, 0x01, 0x51, 0x09, 0x06, 0x32, 0x49, 0x59, 0x51, 0x3E,
  166.   0x7C, 0x12, 0x11, 0x12, 0x7C, 0x7F, 0x49, 0x49, 0x49, 0x36, 0x3E, 0x41, 0x41, 0x41, 0x22,
  167.   0x7F, 0x41, 0x41, 0x22, 0x1C, 0x7F, 0x49, 0x49, 0x49, 0x41, 0x7F, 0x09, 0x09, 0x09, 0x01,
  168.   0x3E, 0x41, 0x49, 0x49, 0x7A, 0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00, 0x41, 0x7F, 0x41, 0x00,
  169.   0x20, 0x40, 0x41, 0x3F, 0x01, 0x7F, 0x08, 0x14, 0x22, 0x41, 0x7F, 0x40, 0x40, 0x40, 0x40,
  170.   0x7F, 0x02, 0x0C, 0x02, 0x7F, 0x7F, 0x04, 0x08, 0x10, 0x7F, 0x3E, 0x41, 0x41, 0x41, 0x3E,
  171.   0x7F, 0x09, 0x09, 0x09, 0x06, 0x3E, 0x41, 0x51, 0x21, 0x5E, 0x7F, 0x09, 0x19, 0x29, 0x46,
  172.   0x46, 0x49, 0x49, 0x49, 0x31, 0x01, 0x01, 0x7F, 0x01, 0x01, 0x3F, 0x40, 0x40, 0x40, 0x3F,
  173.   0x1F, 0x20, 0x40, 0x20, 0x1F, 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x63, 0x14, 0x08, 0x14, 0x63,
  174.   0x07, 0x08, 0x70, 0x08, 0x07, 0x61, 0x51, 0x49, 0x45, 0x43, 0x00, 0x7F, 0x41, 0x41, 0x00,
  175.   0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x41, 0x41, 0x7F, 0x00, 0x04, 0x02, 0x01, 0x02, 0x04,
  176.   0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x01, 0x02, 0x04, 0x00, 0x20, 0x54, 0x54, 0x54, 0x78,
  177.   0x7F, 0x48, 0x44, 0x44, 0x38, 0x38, 0x44, 0x44, 0x44, 0x20, 0x38, 0x44, 0x44, 0x48, 0x7F,
  178.   0x38, 0x54, 0x54, 0x54, 0x18, 0x08, 0x7E, 0x09, 0x01, 0x02, 0x18, 0xA4, 0xA4, 0xA4, 0x7C,
  179.   0x7F, 0x08, 0x04, 0x04, 0x78, 0x00, 0x44, 0x7D, 0x40, 0x00, 0x40, 0x80, 0x84, 0x7D, 0x00,
  180.   0x7F, 0x10, 0x28, 0x44, 0x00, 0x00, 0x41, 0x7F, 0x40, 0x00, 0x7C, 0x04, 0x18, 0x04, 0x78,
  181.   0x7C, 0x08, 0x04, 0x04, 0x78, 0x38, 0x44, 0x44, 0x44, 0x38, 0xFC, 0x24, 0x24, 0x24, 0x18,
  182.   0x18, 0x24, 0x24, 0x18, 0xFC, 0x7C, 0x08, 0x04, 0x04, 0x08, 0x48, 0x54, 0x54, 0x54, 0x20,
  183.   0x04, 0x3F, 0x44, 0x40, 0x20, 0x3C, 0x40, 0x40, 0x20, 0x7C, 0x1C, 0x20, 0x40, 0x20, 0x1C,
  184.   0x3C, 0x40, 0x30, 0x40, 0x3C, 0x44, 0x28, 0x10, 0x28, 0x44, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C,
  185.   0x44, 0x64, 0x54, 0x4C, 0x44, 0x08, 0x36, 0x41, 0x41, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00,
  186.   0x00, 0x41, 0x41, 0x36, 0x08, 0x08, 0x04, 0x08, 0x10, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
  187. };

  188. // OLED variables
  189. uint8_t OLED_x, OLED_y;                         // current cursor position
  190. const uint16_t DIVIDER[] PROGMEM = {10000, 1000, 100, 10, 1}; // BCD conversion array

  191. // OLED init function
  192. void OLED_init(void) {
  193.   I2C_start(OLED_ADDR);                         // start transmission to OLED
  194.   I2C_write(OLED_CMD_MODE);                     // set command mode
  195.   for (uint8_t i = 0; i < OLED_INIT_LEN; i++)
  196.     I2C_write(pgm_read_byte(&OLED_INIT_CMD[i]));// send the command bytes
  197.   I2C_stop();                                   // stop transmission
  198. }

  199. // OLED set the cursor
  200. void OLED_setCursor(uint8_t xpos, uint8_t ypos) {
  201.   I2C_start(OLED_ADDR);                         // start transmission to OLED
  202.   I2C_write(OLED_CMD_MODE);                     // set command mode
  203.   I2C_write(xpos & 0x0F);                       // set low nibble of start column
  204.   I2C_write(0x10 | (xpos >> 4));                // set high nibble of start column
  205.   I2C_write(0xB0 | (ypos & 0x07));              // set start page
  206.   I2C_stop();                                   // stop transmission
  207.   OLED_x = xpos; OLED_y = ypos;                 // set the cursor variables
  208. }

  209. // OLED clear rest of the current line
  210. void OLED_clearLine(void) {
  211.   I2C_start(OLED_ADDR);                         // start transmission to OLED
  212.   I2C_write(OLED_DAT_MODE);                     // set data mode
  213.   while(OLED_x++ < 128) I2C_write(0);           // clear rest of the line
  214.   I2C_stop();                                   // stop transmission
  215.   if(++OLED_y > 3) OLED_y = 0;                  // calculate next line
  216.   OLED_setCursor(0, OLED_y);                    // set cursor to start of next line
  217. }

  218. // OLED clear screen
  219. void OLED_clearScreen(void) {
  220.   OLED_setCursor(0, 0);                         // set cursor to home position
  221.   for(uint8_t i=4; i; i--) OLED_clearLine();    // clear all 4 lines
  222. }

  223. // OLED plot a single character
  224. void OLED_plotChar(char c) {
  225.   uint16_t ptr = c - 32;                        // character pointer
  226.   ptr += ptr << 2;                              // -> ptr = (ch - 32) * 5;
  227.   I2C_write(0x00);                              // write space between characters
  228.   for (uint8_t i=5 ; i; i--) I2C_write(pgm_read_byte(&OLED_FONT[ptr++]));
  229.   OLED_x += 6;                                  // update cursor
  230.   if (OLED_x > 122) {                           // line end ?
  231.     I2C_stop();                                 // stop data transmission
  232.     OLED_setCursor(0,++OLED_y);                 // set next line start
  233.     I2C_start(OLED_ADDR);                       // start transmission to OLED
  234.     I2C_write(OLED_DAT_MODE);                   // set data mode
  235.   }
  236. }

  237. // OLED print a string
  238. void OLED_printStr(uint8_t* str) {
  239.   I2C_start(OLED_ADDR);                         // start transmission to OLED
  240.   I2C_write(OLED_DAT_MODE);                     // set data mode
  241.   while(*str) OLED_plotChar(*str++);            // plot each character
  242.   I2C_stop();                                   // stop transmission
  243. }

  244. // OLED print a string from program memory
  245. void OLED_print(const char* p) {
  246.   I2C_start(OLED_ADDR);                         // start transmission to OLED
  247.   I2C_write(OLED_DAT_MODE);                     // set data mode
  248.   char ch = pgm_read_byte(p);                   // read first character from program memory
  249.   while (ch) {                                  // repeat until string terminator
  250.     OLED_plotChar(ch);                          // print character on OLED
  251.     ch = pgm_read_byte(++p);                    // read next character
  252.   }
  253.   I2C_stop();                                   // stop transmission
  254. }

  255. // OLED print a string from program memory with new line
  256. void OLED_println(const char* p) {
  257.   OLED_print(p);
  258.   OLED_clearLine();
  259. }

  260. // OLED print 8-bit value as 2-digit decimal (BCD conversion by substraction method)
  261. void OLED_printVal(uint8_t value) {
  262.   if(value > 99) value = 99;                    // limit 2-digit value
  263.   I2C_start(OLED_ADDR);                         // start transmission to OLED
  264.   I2C_write(OLED_DAT_MODE);                     // set data mode
  265.   uint8_t digitval = 0;                         // start with digit value 0
  266.   while(value >= 10) {                          // if current divider fits into the value
  267.     digitval++;                                 // increase digit value
  268.     value -= 10;                                // decrease value by divider
  269.   }
  270.   if(digitval) OLED_plotChar(digitval + '0');   // print first digit
  271.   else OLED_plotChar(' ');                      // leading space if zero
  272.   OLED_plotChar(value + '0');                   // print second digit
  273.   I2C_stop();                                   // stop transmission
  274. }

  275. // OLED print frequency (BCD conversion by substraction method)
  276. void OLED_printFrequency(uint16_t value) {
  277.   uint8_t leadflag = 0;                         // flag for leading spaces
  278.   I2C_start(OLED_ADDR);                         // start transmission to OLED
  279.   I2C_write(OLED_DAT_MODE);                     // set data mode
  280.   for(uint8_t digit = 0; digit < 5; digit++) {  // 5 digits
  281.     uint8_t digitval = 0;                       // start with digit value 0
  282.     uint16_t divider = pgm_read_word(&DIVIDER[digit]); // current divider
  283.     while(value >= divider) {                   // if current divider fits into the value
  284.       leadflag = 1;                             // end of leading spaces
  285.       digitval++;                               // increase digit value
  286.       value -= divider;                         // decrease value by divider
  287.     }
  288.     if(leadflag || (digit > 1)) OLED_plotChar(digitval + '0'); // print the digit
  289.     else OLED_plotChar(' ');                    // or print leading space
  290.     if(digit == 2) OLED_plotChar('.');          // print decimal after 3rd digit
  291.   }
  292.   I2C_stop();                                   // stop transmission
  293. }

  294. // ===================================================================================
  295. // RDA5807 Implementation
  296. // ===================================================================================

  297. // RDA definitions
  298. #define RDA_ADDR_SEQ    0x20                    // RDA I2C write address for sequential access
  299. #define RDA_ADDR_INDEX  0x22                    // RDA I2C write address for indexed access
  300. #define RDA_VOL         1                       // start volume

  301. // RDA register definitions
  302. enum{ RDA_REG_2, RDA_REG_3, RDA_REG_4, RDA_REG_5, RDA_REG_6, RDA_REG_7 };
  303. enum{ RDA_REG_A, RDA_REG_B, RDA_REG_C, RDA_REG_D, RDA_REG_E, RDA_REG_F };
  304. uint16_t RDA_read_regs[6];                      // RDA registers for reading
  305. uint16_t RDA_write_regs[6] = {                  // RDA registers for writing:
  306.   0b1101001000001101,                           // RDA register 0x02 preset
  307.   0b0001010111000000,                           // RDA register 0x03 preset
  308.   0b0000101000000000,                           // RDA register 0x04 preset
  309.   0b1000100010000000,                           // RDA register 0x05 preset
  310.   0b0000000000000000,                           // RDA register 0x06 preset
  311.   0b0000000000000000                            // RDA register 0x07 preset
  312. };

  313. // RDA state macros
  314. #define RDA_hasRdsData        ( RDA_read_regs[RDA_REG_A] & 0x8000 )
  315. #define RDA_isTuning          (~RDA_read_regs[RDA_REG_A] & 0x4000 )
  316. #define RDA_tuningError       ( RDA_read_regs[RDA_REG_A] & 0x2000 )
  317. #define RDA_hasRdsBlockE      ( RDA_read_regs[RDA_REG_A] & 0x0800 )
  318. #define RDA_isStereo          ( RDA_read_regs[RDA_REG_A] & 0x0400 )
  319. #define RDA_channel           ( RDA_read_regs[RDA_REG_A] & 0x03FF )
  320. #define RDA_isTunedToChannel  ( RDA_read_regs[RDA_REG_B] & 0x0100 )
  321. #define RDA_rdsBlockE         ( RDA_read_regs[RDA_REG_B] & 0x0010 )
  322. #define RDA_rdsBlockErrors    ( RDA_read_regs[RDA_REG_B] & 0x000F )
  323. #define RDA_signalStrength    ((RDA_read_regs[RDA_REG_B] & 0xFE00 ) >> 9 )

  324. // RDA variables
  325. uint8_t RDA_stationName[9];                     // string for the station name
  326. uint8_t RDA_rdsStationName[8];                  // just for internal use

  327. // RDA write specified register
  328. void RDA_writeReg(uint8_t reg) {
  329.   I2C_start(RDA_ADDR_INDEX);                    // start I2C for index write to RDA
  330.   I2C_write(0x02 + reg);                        // set the register to write
  331.   I2C_write(RDA_write_regs[reg] >> 8);          // send high byte
  332.   I2C_write(RDA_write_regs[reg]);               // send low byte
  333.   I2C_stop();                                   // stop I2C
  334. }

  335. // RDA write all registers
  336. void RDA_writeAllRegs(void) {
  337.   I2C_start(RDA_ADDR_SEQ);                      // start I2C for sequential write to RDA
  338.   for(uint8_t i=0; i<6; i++) {                  // write to 6 registers
  339.     I2C_write(RDA_write_regs[i] >> 8);          // send high byte
  340.     I2C_write(RDA_write_regs[i]);               // send low byte
  341.   }
  342.   I2C_stop();                                   // stop I2C
  343. }

  344. // RDA read all registers
  345. void RDA_readAllRegs(void) {
  346.   I2C_start(RDA_ADDR_SEQ | 1);                  // start I2C for sequential read from RDA
  347.   for(uint8_t i=0; i<6; i++)                    // read 6 registers
  348.     RDA_read_regs[i] = (uint16_t)(I2C_read(1) << 8) | I2C_read(5-i);
  349.   I2C_stop();                                   // stop I2C
  350. }

  351. // RDA clear station
  352. void RDA_resetStation(void) {
  353.   for(uint8_t i=0; i<8; i++) RDA_stationName[i] = ' ';
  354. }

  355. // RDA initialize tuner
  356. void RDA_init(void) {
  357.   RDA_resetStation();                           // reset station available
  358.   RDA_stationName[8] = 0;                       // set string terminator
  359.   RDA_write_regs[RDA_REG_2] |=  0x0002;         // set soft reset
  360.   RDA_write_regs[RDA_REG_5] |=  RDA_VOL;        // set start volume
  361.   RDA_writeAllRegs();                           // write all registers
  362.   RDA_write_regs[RDA_REG_2] &= ~0x0002;         // clear soft reset
  363.   RDA_writeReg(RDA_REG_2);                      // write to register 0x02
  364. }

  365. // RDA set volume
  366. void RDA_setVolume(uint8_t vol) {
  367.   RDA_write_regs[RDA_REG_5] &= ~0x000F;         // clear volume bits
  368.   RDA_write_regs[RDA_REG_5] |=  vol;            // set volume
  369.   RDA_writeReg(RDA_REG_5);                      // write to register 0x05
  370. }

  371. // RDA tune to a specified channel
  372. void RDA_setChannel(uint16_t chan) {
  373.   RDA_resetStation();
  374.   RDA_write_regs[RDA_REG_3] &= ~0xFFC0;         // clear channel
  375.   RDA_write_regs[RDA_REG_3] |= (chan << 6) | 0x0010;  // set channel and tune enable
  376.   RDA_writeReg(RDA_REG_3);                      // write register
  377. }

  378. // RDA seek next channel
  379. void RDA_seekUp(void) {
  380.   RDA_resetStation();
  381.   RDA_write_regs[RDA_REG_2] |=  0x0100;         // set seek enable bit
  382.   RDA_writeReg(RDA_REG_2);                      // write to register 0x02
  383. }

  384. // RDA update status and handle RDS
  385. void RDA_updateStatus(void) {
  386.   RDA_readAllRegs();

  387.   // When tuned disable tuning and stop seeking
  388.   if (!RDA_isTuning) {
  389.     RDA_write_regs[RDA_REG_3] &= ~0x0010;       // clear tune enable flag
  390.     RDA_writeReg(RDA_REG_3);
  391.     RDA_write_regs[RDA_REG_2] &= ~0x0100;       // clear seek enable flag
  392.     RDA_writeReg(RDA_REG_2);
  393.   }

  394.   // Check for RDS data
  395.   if(RDA_hasRdsData) {                          // RDS ready?
  396.     // Toggle RDS flag to request new data
  397.     RDA_write_regs[RDA_REG_2] &= ~0x0008;       // clear RDS flag
  398.     RDA_writeReg(RDA_REG_2);                    // write to register 0x02
  399.     RDA_write_regs[RDA_REG_2] |=  0x0008;       // set RDS flag
  400.     RDA_writeReg(RDA_REG_2);                    // write to register 0x02

  401.     // Decode RDS message (station name)
  402.     if(!RDA_rdsBlockE) {                                         // REG_B..F carrying blocks A-D?
  403.       if( (RDA_read_regs[RDA_REG_D] & 0xF800) == 0x0000) {       // is it station name?
  404.         uint8_t offset = (RDA_read_regs[RDA_REG_D] & 0x03) << 1; // get character position
  405.         uint8_t c1 = RDA_read_regs[RDA_REG_F] >> 8;              // get character 1
  406.         uint8_t c2 = RDA_read_regs[RDA_REG_F];                   // get character 2

  407.         // Copy station name characters only if received twice in a row...
  408.         if(RDA_rdsStationName[offset] == c1)                     // 1st char received twice?
  409.              RDA_stationName[offset] = c1;                       // copy to station name
  410.         else RDA_rdsStationName[offset] = c1;                    // save for next test
  411.         if(RDA_rdsStationName[offset + 1] == c2)                 // 2nd char received twice?
  412.              RDA_stationName[offset + 1] = c2;                   // copy to station name
  413.         else RDA_rdsStationName[offset + 1] = c2;                // save for next test
  414.       }
  415.     }
  416.   }
  417. }

  418. // Calculate frequency in 10kHz
  419. uint16_t RDA_getFrequency(void) {
  420.   return(8700 + (RDA_channel << 3) + (RDA_channel << 1));
  421. }

  422. // Waits until tuning completed
  423. void RDA_waitTuning(void) {
  424.   do {
  425.     _delay_ms(100);
  426.     RDA_updateStatus();
  427.   } while(RDA_isTuning);
  428. }

  429. // ===================================================================================
  430. // ADC Implementation for Supply Voltage Measurement
  431. // ===================================================================================

  432. // Init ADC
  433. void ADC_init(void) {
  434.   ADCSRA = (1<<ADPS1) | (1<<ADPS0);             // set ADC clock prescaler to 8
  435.   ADMUX  = (1<<MUX3)  | (1<<MUX2);              // set 1.1V Vref against Vcc
  436. }

  437. // Read Vcc voltage in dV by measuring 1.1V reference against Vcc
  438. uint8_t ADC_readVcc(void) {
  439.   PRR    &= ~(1<<PRADC);                        // power on ADC
  440.   ADCSRA |=  (1<<ADEN);                         // enable ADC
  441.   _delay_ms(2);                                 // wait for vref to settle
  442.   ADCSRA |= (1<<ADSC);                          // start sampling
  443.   while(ADCSRA & (1<<ADSC));                    // wait for sampling to complete
  444.   uint16_t vcc = ADC;                           // read sampling result
  445.   ADCSRA &= ~(1<<ADEN);                         // disable ADC
  446.   PRR    |=  (1<<PRADC);                        // power off ADC
  447.   vcc = 11253 / vcc;                            // calculate Vcc in dV; 11253 = 1.1*1023*10
  448.   return vcc;                                   // divide by 8 and return result
  449. }

  450. // ===================================================================================
  451. // Rotary Encoder Implementation using Pin Change Interrupt
  452. // ===================================================================================

  453. // Global variables
  454. volatile uint8_t  ENC_a0, ENC_b0, ENC_ab0;
  455. volatile int16_t  ENC_count, ENC_countMin, ENC_countMax, ENC_countStep;

  456. // Init rotary encoder
  457. void ENC_init(void) {
  458.   pinPullup(PIN_ENC_A);                         // enable pullup on encoder pins ...
  459.   pinPullup(PIN_ENC_B);
  460.   pinPullup(PIN_ENC_SW);
  461.   pinIntEn(PIN_ENC_A);                          // enable pin change interrupt on ENC A
  462.   ENC_a0  = !pinRead(PIN_ENC_A);                // set initial values ...
  463.   ENC_b0  = !pinRead(PIN_ENC_B);
  464.   ENC_ab0 = (ENC_a0 == ENC_b0);
  465.   GIMSK  |= (1<<PCIE);                          // enable pin change interrupts
  466. }

  467. // Set parameters for rotary encoder
  468. void ENC_set(int16_t rmin, int16_t rmax, int16_t rstep, int16_t rvalue) {
  469.   ENC_countMin  = rmin << 1;                    // min value
  470.   ENC_countMax  = rmax << 1;                    // max value
  471.   ENC_countStep = rstep;                        // count steps (negative if CCW)
  472.   ENC_count     = rvalue << 1;                  // actual count value
  473. }

  474. // reads current rotary encoder value
  475. int ENC_get(void) {
  476.   return(ENC_count >> 1);
  477. }

  478. // Pin change interrupt service routine for rotary encoder
  479. ISR(PCINT0_vect) {
  480.   uint8_t a = !pinRead(PIN_ENC_A);
  481.   uint8_t b = !pinRead(PIN_ENC_B);
  482.   if(a != ENC_a0) {                             // A changed?
  483.     ENC_a0 = a;
  484.     if(b != ENC_b0) {                           // B changed?
  485.       ENC_b0 = b;
  486.       ENC_count += (a == b) ? -ENC_countStep : ENC_countStep;
  487.       if((a == b) != ENC_ab0) ENC_count += (a == b) ? -ENC_countStep : ENC_countStep;
  488.       if(ENC_count < ENC_countMin) ENC_count = ENC_countMin;
  489.       if(ENC_count > ENC_countMax) ENC_count = ENC_countMax;
  490.       ENC_ab0 = (a == b);
  491.     }
  492.   }
  493. }

  494. // ===================================================================================
  495. // EEPROM Functions
  496. // ===================================================================================

  497. // updates frequency and volume stored in EEPROM
  498. void EEPROM_update() {
  499.   eeprom_update_word((uint16_t*)0, EEPROM_IDENT);
  500.   eeprom_update_word((uint16_t*)2, RDA_channel);
  501.   eeprom_update_byte((uint8_t*)4, volume);
  502. }

  503. // reads frequency and volume stored in EEPROM
  504. uint8_t EEPROM_get() {
  505.   uint16_t identifier = eeprom_read_word((const uint16_t*)0);
  506.   if (identifier == EEPROM_IDENT) {
  507.     channel = eeprom_read_word((const uint16_t*)2);
  508.     volume  = eeprom_read_byte((const uint8_t*)4);
  509.     return 1;
  510.   }
  511.   return 0;
  512. }

  513. // ===================================================================================
  514. // Main Function
  515. // ===================================================================================

  516. int main(void) {
  517.   // Setup  
  518.   ADC_init();                                   // setup ADC
  519.   ENC_init();                                   // setup rotary encoder
  520.   sei();                                        // enable global interrupts

  521.   // Disable unused peripherals to save power
  522.   ACSR =  (1<<ACD);                             // disable analog comperator
  523.   PRR  =  (1<<PRADC)                            // shut down ADC
  524.        |  (1<<PRUSI)                            // shut down USI
  525.        |  (1<<PRTIM0)                           // shut down timer0
  526.        |  (1<<PRTIM1);                          // shut down timer1

  527.   // Prepare and start OLED
  528.   OLED_init();
  529.   OLED_clearScreen();
  530.   OLED_println(HEADER);
  531.   OLED_print(PSTR("Starting ..."));

  532.   // Start the tuner
  533.   RDA_init();
  534.   if(EEPROM_get()) RDA_setChannel(channel);
  535.   else RDA_seekUp();
  536.   ENC_set(0, 15, 1, volume);
  537.   RDA_setVolume(volume);
  538.   RDA_waitTuning();

  539.   // Loop
  540.   while(1) {
  541.     // Update information on OLED
  542.     RDA_updateStatus();
  543.     uint8_t vcc = ADC_readVcc();
  544.     OLED_setCursor(0, 1);
  545.     OLED_print(PSTR("Station:  "));
  546.     OLED_printStr(RDA_stationName);
  547.     OLED_clearLine();
  548.     OLED_print(PSTR("Vol: "));
  549.     OLED_printVal(volume);
  550.     OLED_print(PSTR("   Frq: "));
  551.     OLED_printFrequency(RDA_getFrequency());
  552.     OLED_print(PSTR("Sig: "));
  553.     OLED_printVal(RDA_signalStrength);
  554.     OLED_print(PSTR("   Bat: "));
  555.     if(vcc < 32) OLED_println(PSTR("weak"));
  556.     else OLED_println(PSTR("OK"));

  557.     // Check rotary encoder switch for channel seek
  558.     if (!pinRead(PIN_ENC_SW)) {                 // seek up if encoder button is pressed
  559.       OLED_setCursor(0, 1);
  560.       OLED_println(PSTR("Tuning ..."));
  561.       OLED_clearLine(); OLED_clearLine();
  562.       RDA_seekUp();
  563.       RDA_waitTuning();
  564.       while (!pinRead(PIN_ENC_SW));
  565.       EEPROM_update();
  566.     }

  567.     // Check rotary encoder for volume change
  568.     if (volume != ENC_get()) {                  // change volume if encoder was turned
  569.       volume = ENC_get();
  570.       RDA_setVolume(volume);
  571.       EEPROM_update();
  572.     }
  573.   }
  574. }



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| 598330983 发表于 2024-9-28 13:40 | 显示全部楼层
低成本RDA5807MP是一款单芯片广播 FM 立体声收音机调谐器,具有完全集成的合成器、IF 选择性、RDS/RBDS 和 MPX 解码器。调谐器采用 CMOS 工艺,支持多接口,需要最少的外部元件。所有这些都使其非常适合便携式设备。

FM 调谐器 IC RDA5807MP 由 ATtiny 通过 I²C 控制。它有 6 个可写的 16 位寄存器(地址 0x02 - 0x07)和 6 个可读的 16 位寄存器(地址 0x0A - 0x0F)。RDA5807 有两种写入访问方法,一种是顺序方法,其中寄存器总是从地址 0x02 开始写入,另一种是索引方法,其中首先传输寄存器地址,然后传输内容。两种方法都由不同的 I²C 地址决定。要传输 16 位寄存器内容,首先发送高字节。RDA5807 是通过设置或清除相应 registers 中的某些 bits 来控制的。各个寄存器的含义的详细信息可以在数据表中找到。当前 register 内容保存在 RDA_regs 数组中。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
734774645 发表于 2024-9-28 17:11 | 显示全部楼层
IO模拟的,比较通用,学习一下。
wanduzi 发表于 2024-9-29 20:04 | 显示全部楼层
85现在是多少钱一片、
wanduzi 发表于 2024-9-29 20:05 | 显示全部楼层
查了一下这个芯片竟然好几块,用不起。。。
gejigeji521 发表于 2024-9-30 11:56 | 显示全部楼层
下载参靠学习。
单片小菜 发表于 2024-9-30 13:51 | 显示全部楼层
这个电路图有源文件吗?
KCCHEN 发表于 2024-10-1 08:24 | 显示全部楼层
学习一下
antusheng 发表于 2024-11-24 19:34 | 显示全部楼层
这个是真不错。好玩。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

266

主题

5573

帖子

22

粉丝
快速回复 在线客服 返回列表 返回顶部