[PIC®/AVR®/dsPIC®产品] ATtiny13A与RDA5807的收音机,很有参靠价值的代码

[复制链接]
 楼主| 598330983 发表于 2024-9-28 13:09 | 显示全部楼层 |阅读模式
TinyPocketRadio 是一款基于 ATtiny13A 和 RDA5807MP 的简单 FM 立体声收音机。它由 CR2032 纽扣电池供电,可通过 32 毫米音频插头驱动 3.5 欧姆耳机。电路板尺寸为 38 x 23 mm。它有一个电源开关和三个按钮:“频道+”、音量-“和”音量+”。
游客,如果您要查看本帖隐藏内容请回复



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


本帖子中包含更多资源

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

×
 楼主| 598330983 发表于 2024-9-28 13:10 | 显示全部楼层
  1. // ===================================================================================
  2. // Project:   TinyPocketRadio - FM Tuner based on ATtiny13A
  3. // Version:   v1.0
  4. // Year:      2020
  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 code implements a simple Pocket Radio with three control buttons
  14. // (Vol+/- and Channel Seek). The FM tuner IC RDA5807MP is controled via
  15. // I²C by the ATtiny.
  16. //
  17. // The I²C protocol implementation is based on a crude bitbanging method.
  18. // It was specifically designed for the limited resources of ATtiny10 and
  19. // ATtiny13, but should work with some other AVRs as well. Due to the low
  20. // clock frequency of the CPU, it does not require any delays for correct
  21. // timing. In order to save resources, only the basic functionalities which
  22. // are needed for this application are implemented. For a detailed
  23. // information on the working principle of the I²C implementation visit
  24. // https://github.com/wagiminator/attiny13-tinyoleddemo
  25. //
  26. // The code utilizes the sleep mode power down function to save power.
  27. // The CPU wakes up on every button press by pin change interrupt, transmits
  28. // the appropriate command via I²C to the RDA5807 and falls asleep again.
  29. //
  30. // Wiring:
  31. // -------
  32. //                            +-\/-+
  33. //         --- RST ADC0 PB5  1|°   |8  Vcc
  34. // I2C SDA ------- ADC3 PB3  2|    |7  PB2 ADC1 -------- VOL+ BUTTON
  35. // I2C SCL ------- ADC2 PB4  3|    |6  PB1 AIN1 OC0B --- VOL- BUTTON
  36. //                      GND  4|    |5  PB0 AIN0 OC0A --- SEEK BUTTON
  37. //                            +----+
  38. //
  39. // Compilation Settings:
  40. // ---------------------
  41. // Controller: ATtiny13A
  42. // Core:       MicroCore (https://github.com/MCUdude/MicroCore)
  43. // Clockspeed: 1.2 MHz internal
  44. // BOD:        BOD disabled
  45. // Timing:     Micros disabled
  46. //
  47. // Leave the rest on default settings. Don't forget to "Burn bootloader"!
  48. // No Arduino core functions or libraries are used. Use the makefile if
  49. // you want to compile without Arduino IDE.
  50. //
  51. // Fuse settings: -U lfuse:w:0x2a:m -U hfuse:w:0xff:m


  52. // ===================================================================================
  53. // Libraries and Definitions
  54. // ===================================================================================

  55. // Libraries
  56. #include <avr/io.h>           // for GPIO
  57. #include <avr/sleep.h>        // for sleep functions
  58. #include <avr/interrupt.h>    // for interrupts
  59. #include <util/delay.h>       // for delays

  60. // Pin definitions
  61. #define BT_SEEK   PB0         // CH+  button
  62. #define BT_VOLM   PB1         // VOL- button
  63. #define BT_VOLP   PB2         // VOL+ button
  64. #define I2C_SDA   PB3         // I2C serial data pin
  65. #define I2C_SCL   PB4         // I2C serial clock pin
  66. #define BT_MASK   (1<<BT_SEEK)|(1<<BT_VOLM)|(1<<BT_VOLP)

  67. // ===================================================================================
  68. // I2C Implementation
  69. // ===================================================================================

  70. // I2C macros
  71. #define I2C_SDA_HIGH()  DDRB &= ~(1<<I2C_SDA) // release SDA   -> pulled HIGH by resistor
  72. #define I2C_SDA_LOW()   DDRB |=  (1<<I2C_SDA) // SDA as output -> pulled LOW  by MCU
  73. #define I2C_SCL_HIGH()  DDRB &= ~(1<<I2C_SCL) // release SCL   -> pulled HIGH by resistor
  74. #define I2C_SCL_LOW()   DDRB |=  (1<<I2C_SCL) // SCL as output -> pulled LOW  by MCU

  75. // I2C init function
  76. void I2C_init(void) {
  77.   DDRB  &= ~((1<<I2C_SDA)|(1<<I2C_SCL));  // pins as input (HIGH-Z) -> lines released
  78.   PORTB &= ~((1<<I2C_SDA)|(1<<I2C_SCL));  // should be LOW when as ouput
  79. }

  80. // I2C transmit one data byte to the slave, ignore ACK bit, no clock stretching allowed
  81. void I2C_write(uint8_t data) {
  82.   for(uint8_t i = 8; i; i--, data<<=1) {  // transmit 8 bits, MSB first
  83.     I2C_SDA_LOW();                        // SDA LOW for now (saves some flash this way)
  84.     if(data & 0x80) I2C_SDA_HIGH();       // SDA HIGH if bit is 1
  85.     I2C_SCL_HIGH();                       // clock HIGH -> slave reads the bit
  86.     I2C_SCL_LOW();                        // clock LOW again
  87.   }
  88.   I2C_SDA_HIGH();                         // release SDA for ACK bit of slave
  89.   I2C_SCL_HIGH();                         // 9th clock pulse is for the ACK bit
  90.   I2C_SCL_LOW();                          // but ACK bit is ignored
  91. }

  92. // I2C start transmission
  93. void I2C_start(uint8_t addr) {
  94.   I2C_SDA_LOW();                          // start condition: SDA goes LOW first
  95.   I2C_SCL_LOW();                          // start condition: SCL goes LOW second
  96.   I2C_write(addr);                        // send slave address
  97. }

  98. // I2C stop transmission
  99. void I2C_stop(void) {
  100.   I2C_SDA_LOW();                          // prepare SDA for LOW to HIGH transition
  101.   I2C_SCL_HIGH();                         // stop condition: SCL goes HIGH first
  102.   I2C_SDA_HIGH();                         // stop condition: SDA goes HIGH second
  103. }

  104. // ===================================================================================
  105. // RDA5807 Implementation
  106. // ===================================================================================

  107. // RDA definitions
  108. #define RDA_ADDR_SEQ    0x20              // RDA I2C write address for sequential access
  109. #define RDA_ADDR_INDEX  0x22              // RDA I2C write address for indexed access
  110. #define R2_SEEK_ENABLE  0x0100            // RDA seek enable bit
  111. #define R2_SOFT_RESET   0x0002            // RDA soft reset bit
  112. #define R5_VOLUME       0x000F            // RDA volume mask
  113. #define RDA_VOL         5                 // start volume

  114. // RDA write registers
  115. uint16_t RDA_regs[6] = {
  116.   0b1101001000000101,                     // RDA register 0x02
  117.   0b0001010111000000,                     // RDA register 0x03
  118.   0b0000101000000000,                     // RDA register 0x04
  119.   0b1000100010000000,                     // RDA register 0x05
  120.   0b0000000000000000,                     // RDA register 0x06
  121.   0b0000000000000000                      // RDA register 0x07
  122. };

  123. // RDA write specified register
  124. void RDA_writeReg(uint8_t reg) {
  125.   I2C_start(RDA_ADDR_INDEX);              // start I2C for index write to RDA
  126.   I2C_write(0x02 + reg);                  // set the register to write
  127.   I2C_write(RDA_regs[reg] >> 8);          // send high byte
  128.   I2C_write(RDA_regs[reg] & 0xFF);        // send low byte
  129.   I2C_stop();                             // stop I2C
  130. }

  131. // RDA write all registers
  132. void RDA_writeAllRegs(void) {
  133.   I2C_start(RDA_ADDR_SEQ);                // start I2C for sequential write to RDA
  134.   for(uint8_t i=0; i<6; i++) {            // write to 6 registers
  135.     I2C_write(RDA_regs[i] >> 8);          // send high byte
  136.     I2C_write(RDA_regs[i] & 0xFF);        // send low byte
  137.   }
  138.   I2C_stop();                             // stop I2C
  139. }

  140. // RDA initialize tuner
  141. void RDA_init(void) {
  142.   I2C_init();                             // init I2C
  143.   RDA_regs[0] |= R2_SOFT_RESET;           // set soft reset
  144.   RDA_regs[3] |= RDA_VOL;                 // set start volume
  145.   RDA_writeAllRegs();                     // write all registers
  146.   RDA_regs[0] &= 0xFFFD;                  // clear soft reset
  147.   RDA_writeReg(0);                        // write to register 0x02
  148. }

  149. // RDA set volume
  150. void RDA_setVolume(uint8_t vol) {
  151.   RDA_regs[3] &= 0xFFF0;                  // clear volume bits
  152.   RDA_regs[3] |= vol;                     // set volume
  153.   RDA_writeReg(3);                        // write to register 0x05
  154. }

  155. // RDA seek next channel
  156. void RDA_seekUp(void) {
  157.   RDA_regs[0] |= R2_SEEK_ENABLE;          // set seek enable bit
  158.   RDA_writeReg(0);                        // write to register 0x02
  159. }

  160. // ===================================================================================
  161. // Main Function
  162. // ===================================================================================

  163. int main(void) {
  164.   // Setup pins
  165.   PORTB |= (BT_MASK);                     // pull-ups for button pins
  166.   
  167.   // Setup pin change interrupt
  168.   GIMSK = (1<<PCIE);                      // turn on pin change interrupts
  169.   PCMSK = (BT_MASK);                      // turn on interrupt on button pins
  170.   sei();                                  // enable global interrupts

  171.   // Disable unused peripherals and set sleep mode to save power
  172.   ADCSRA = 0;                             // disable ADC
  173.   ACSR   = (1<<ACD);                      // disable analog comperator
  174.   PRR    = (1<<PRTIM0) | (1<<PRADC);      // shut down ADC and timer0
  175.   set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // set sleep mode to power down

  176.   // Setup radio
  177.   uint8_t volume = RDA_VOL;               // set start volume
  178.   RDA_init();                             // initialize RDA
  179.   RDA_seekUp();                           // seek a channel

  180.   // Loop
  181.   while(1) {
  182.     sleep_mode();                         // sleep until button is pressed
  183.     _delay_ms(1);                         // debounce
  184.     uint8_t buttons = ~PINB & (BT_MASK);  // read button pins
  185.     switch (buttons) {                    // send corresponding command to RDA
  186.       case (1<<BT_SEEK): RDA_seekUp(); break;
  187.       case (1<<BT_VOLM): if(volume)      RDA_setVolume(--volume); break;
  188.       case (1<<BT_VOLP): if(volume < 15) RDA_setVolume(++volume); break;
  189.       default: break;
  190.     }
  191.   }
  192. }

  193. // Pin change interrupt service routine
  194. EMPTY_INTERRUPT(PCINT0_vect);             // nothing to be done here, just wake up from sleep
734774645 发表于 2024-9-28 17:00 | 显示全部楼层
开关做成按钮就好看了。
天灵灵地灵灵 发表于 2024-9-28 17:14 | 显示全部楼层
可以在一个地址里读取2个字节
您需要登录后才可以回帖 登录 | 注册

本版积分规则

266

主题

5573

帖子

22

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

266

主题

5573

帖子

22

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