Advertisement
Guest User

pi-fm-transmitter

a guest
Jan 23rd, 2013
4,063
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 8.98 KB | None | 0 0
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #include <dirent.h>
  5. #include <math.h>
  6. #include <fcntl.h>
  7. #include <assert.h>
  8. #include <malloc.h>
  9. #include <sys/mman.h>
  10. #include <sys/types.h>
  11. #include <sys/stat.h>
  12. #include <signal.h>
  13. #include <unistd.h>
  14.  
  15. #define PAGE_SIZE (4*1024)
  16. #define BLOCK_SIZE (4*1024)
  17.  
  18. int  mem_fd;
  19. char *gpio_mem, *gpio_map;
  20. char *spi0_mem, *spi0_map;
  21.  
  22.  
  23. // I/O access
  24. volatile unsigned *gpio;
  25. volatile unsigned *allof7e;
  26.  
  27. // GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y)
  28. #define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
  29. #define OUT_GPIO(g) *(gpio+((g)/10)) |=  (1<<(((g)%10)*3))
  30. #define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))
  31.  
  32. #define GPIO_SET *(gpio+7)  // sets   bits which are 1 ignores bits which are 0
  33. #define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0
  34. #define GPIO_GET *(gpio+13)  // sets   bits which are 1 ignores bits which are 0
  35.  
  36. #define ACCESS(base) *(volatile int*)((int)allof7e+base-0x7e000000)
  37. #define SETBIT(base, bit) ACCESS(base) |= 1<<bit
  38. #define CLRBIT(base, bit) ACCESS(base) &= ~(1<<bit)
  39.  
  40. #define CM_GP0CTL (0x7e101070)
  41. #define GPFSEL0 (0x7E200000)
  42. #define CM_GP0DIV (0x7e101074)
  43. #define CLKBASE (0x7E101000)
  44. #define DMABASE (0x7E007000)
  45. #define PWMBASE  (0x7e20C000) /* PWM controller */
  46.  
  47.  
  48. struct GPCTL {
  49.     char SRC         : 4;
  50.     char ENAB        : 1;
  51.     char KILL        : 1;
  52.     char             : 1;
  53.     char BUSY        : 1;
  54.     char FLIP        : 1;
  55.     char MASH        : 2;
  56.     unsigned int     : 13;
  57.     char PASSWD      : 8;
  58. };
  59.  
  60.  
  61.  
  62. void getRealMemPage(void** vAddr, void** pAddr) {
  63.     void* a = valloc(4096);
  64.  
  65.     ((int*)a)[0] = 1;  // use page to force allocation.
  66.  
  67.     mlock(a, 4096);  // lock into ram.
  68.  
  69.     *vAddr = a;  // yay - we know the virtual address
  70.  
  71.     unsigned long long frameinfo;
  72.  
  73.     int fp = open("/proc/self/pagemap", 'r');
  74.     lseek(fp, ((int)a)/4096*8, SEEK_SET);
  75.     read(fp, &frameinfo, sizeof(frameinfo));
  76.  
  77.     *pAddr = (void*)((int)(frameinfo*4096));
  78. }
  79.  
  80. void freeRealMemPage(void* vAddr) {
  81.  
  82.     munlock(vAddr, 4096);  // unlock ram.
  83.  
  84.     free(vAddr);
  85. }
  86.  
  87. void setup_fm()
  88. {
  89.  
  90.     /* open /dev/mem */
  91.     if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
  92.         printf("can't open /dev/mem \n");
  93.         exit (-1);
  94.     }
  95.  
  96.     allof7e = (unsigned *)mmap(
  97.                   NULL,
  98.                   0x01000000,  //len
  99.                   PROT_READ|PROT_WRITE,
  100.                   MAP_SHARED,
  101.                   mem_fd,
  102.                   0x20000000  //base
  103.               );
  104.  
  105.     if ((int)allof7e==-1) exit(-1);
  106.  
  107.     SETBIT(GPFSEL0 , 14);
  108.     CLRBIT(GPFSEL0 , 13);
  109.     CLRBIT(GPFSEL0 , 12);
  110.  
  111.  
  112.     struct GPCTL setupword = {6/*SRC*/, 1, 0, 0, 0, 1,0x5a};
  113.  
  114.     ACCESS(CM_GP0CTL) = *((int*)&setupword);
  115. }
  116.  
  117.  
  118. void modulate(int m)
  119. {
  120.     ACCESS(CM_GP0DIV) = (0x5a << 24) + 0x4d72 + m;
  121. }
  122.  
  123. struct CB {
  124.     volatile unsigned int TI;
  125.     volatile unsigned int SOURCE_AD;
  126.     volatile unsigned int DEST_AD;
  127.     volatile unsigned int TXFR_LEN;
  128.     volatile unsigned int STRIDE;
  129.     volatile unsigned int NEXTCONBK;
  130.     volatile unsigned int RES1;
  131.     volatile unsigned int RES2;
  132.  
  133. };
  134.  
  135. struct DMAregs {
  136.     volatile unsigned int CS;
  137.     volatile unsigned int CONBLK_AD;
  138.     volatile unsigned int TI;
  139.     volatile unsigned int SOURCE_AD;
  140.     volatile unsigned int DEST_AD;
  141.     volatile unsigned int TXFR_LEN;
  142.     volatile unsigned int STRIDE;
  143.     volatile unsigned int NEXTCONBK;
  144.     volatile unsigned int DEBUG;
  145. };
  146.  
  147. struct PageInfo {
  148.     void* p;  // physical address
  149.     void* v;   // virtual address
  150. };
  151.  
  152. struct PageInfo constPage;  
  153. struct PageInfo instrPage;
  154. struct PageInfo instrs[1024];
  155.  
  156.  
  157. void playWav(char* filename, float samplerate)
  158. {
  159.     int fp= STDIN_FILENO;
  160.     if(filename[0]!='-') fp = open(filename, 'r');
  161.     //int sz = lseek(fp, 0L, SEEK_END);
  162.     //lseek(fp, 0L, SEEK_SET);
  163.     //short* data = (short*)malloc(sz);
  164.     //read(fp, data, sz);
  165.  
  166.     int bufPtr=0;
  167.     float datanew, dataold = 0;
  168.     short data;
  169.  
  170.     for (int i=0; i<22; i++)
  171.        read(fp, &data, 2);  // read past header
  172.  
  173.     while (read(fp, &data, 2)) {
  174.         float fmconstant = samplerate * 50.0e-6;  // for pre-emphisis filter.  50us time constant
  175.         int clocksPerSample = 22500.0/samplerate*1400.0;  // for timing
  176.  
  177.         datanew = (float)(data)/32767;
  178.  
  179.         float sample = datanew + (dataold-datanew) / (1-fmconstant);  // fir of 1 + s tau
  180.         float dval = sample*15.0;  // actual transmitted sample.  15 is bandwidth (about 75 kHz)
  181.  
  182.         int intval = (int)(round(dval));  // integer component
  183.         float frac = (dval - (float)intval)/2 + 0.5;
  184.         unsigned int fracval = frac*clocksPerSample;
  185.  
  186.         bufPtr++;
  187.         while( ACCESS(DMABASE + 0x04 /* CurBlock*/) ==  (int)(instrs[bufPtr].p)) usleep(1000);
  188.         ((struct CB*)(instrs[bufPtr].v))->SOURCE_AD = (int)constPage.p + 2048 + intval*4 - 4 ;
  189.  
  190.         bufPtr++;
  191.         while( ACCESS(DMABASE + 0x04 /* CurBlock*/) ==  (int)(instrs[bufPtr].p)) usleep(1000);
  192.         ((struct CB*)(instrs[bufPtr].v))->TXFR_LEN = clocksPerSample-fracval;
  193.  
  194.         bufPtr++;
  195.         while( ACCESS(DMABASE + 0x04 /* CurBlock*/) ==  (int)(instrs[bufPtr].p)) usleep(1000);
  196.         ((struct CB*)(instrs[bufPtr].v))->SOURCE_AD = (int)constPage.p + 2048 + intval*4+4;
  197.  
  198.         bufPtr=(bufPtr+1) % (1024);
  199.         while( ACCESS(DMABASE + 0x04 /* CurBlock*/) ==  (int)(instrs[bufPtr].p)) usleep(1000);
  200.         ((struct CB*)(instrs[bufPtr].v))->TXFR_LEN = fracval;
  201.  
  202.         dataold = datanew;
  203.     }
  204.     close(fp);
  205. }
  206.  
  207. void unSetupDMA(){
  208.     printf("exiting\n");
  209.     struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS(DMABASE));
  210.     DMA0->CS =1<<31;  // reset dma controller
  211.  
  212. }
  213.  
  214. void handSig() {
  215.     exit(0);
  216. }
  217. void setupDMA( float centerFreq ){
  218.  
  219.   atexit(unSetupDMA);
  220.   signal (SIGINT, handSig);
  221.   signal (SIGTERM, handSig);
  222.   signal (SIGHUP, handSig);
  223.   signal (SIGQUIT, handSig);
  224.  
  225.    // allocate a few pages of ram
  226.    getRealMemPage(&constPage.v, &constPage.p);
  227.  
  228.    int centerFreqDivider = (int)((500.0 / centerFreq) * (float)(1<<12) + 0.5);
  229.  
  230.    // make data page contents - it's essientially 1024 different commands for the
  231.    // DMA controller to send to the clock module at the correct time.
  232.    for (int i=0; i<1024; i++)
  233.      ((int*)(constPage.v))[i] = (0x5a << 24) + centerFreqDivider - 512 + i;
  234.  
  235.  
  236.    int instrCnt = 0;
  237.  
  238.    while (instrCnt<1024) {
  239.      getRealMemPage(&instrPage.v, &instrPage.p);
  240.  
  241.      // make copy instructions
  242.      struct CB* instr0= (struct CB*)instrPage.v;
  243.  
  244.      for (int i=0; i<4096/sizeof(struct CB); i++) {
  245.        instrs[instrCnt].v = (void*)((int)instrPage.v + sizeof(struct CB)*i);
  246.        instrs[instrCnt].p = (void*)((int)instrPage.p + sizeof(struct CB)*i);
  247.        instr0->SOURCE_AD = (unsigned int)constPage.p+2048;
  248.        instr0->DEST_AD = PWMBASE+0x18 /* FIF1 */;
  249.        instr0->TXFR_LEN = 4;
  250.        instr0->STRIDE = 0;
  251.        //instr0->NEXTCONBK = (int)instrPage.p + sizeof(struct CB)*(i+1);
  252.        instr0->TI = (1/* DREQ  */<<6) | (5 /* PWM */<<16) |  (1<<26/* no wide*/) ;
  253.        instr0->RES1 = 0;
  254.        instr0->RES2 = 0;
  255.  
  256.        if (i%2) {
  257.          instr0->DEST_AD = CM_GP0DIV;
  258.          instr0->STRIDE = 4;
  259.          instr0->TI = (1<<26/* no wide*/) ;
  260.        }
  261.  
  262.        if (instrCnt!=0) ((struct CB*)(instrs[instrCnt-1].v))->NEXTCONBK = (int)instrs[instrCnt].p;
  263.        instr0++;
  264.        instrCnt++;
  265.      }
  266.    }
  267.    ((struct CB*)(instrs[1023].v))->NEXTCONBK = (int)instrs[0].p;
  268.  
  269.    // set up a clock for the PWM
  270.    ACCESS(CLKBASE + 40*4 /*PWMCLK_CNTL*/) = 0x5A000026;
  271.    usleep(1000);
  272.    ACCESS(CLKBASE + 41*4 /*PWMCLK_DIV*/)  = 0x5A002800;
  273.    ACCESS(CLKBASE + 40*4 /*PWMCLK_CNTL*/) = 0x5A000016;
  274.    usleep(1000);
  275.  
  276.    // set up pwm
  277.    ACCESS(PWMBASE + 0x0 /* CTRL*/) = 0;
  278.    usleep(1000);
  279.    ACCESS(PWMBASE + 0x4 /* status*/) = -1;  // clear errors
  280.    usleep(1000);
  281.    ACCESS(PWMBASE + 0x0 /* CTRL*/) = -1; //(1<<13 /* Use fifo */) | (1<<10 /* repeat */) | (1<<9 /* serializer */) | (1<<8 /* enable ch */) ;
  282.    usleep(1000);
  283.    ACCESS(PWMBASE + 0x8 /* DMAC*/) = (1<<31 /* DMA enable */) | 0x0707;
  284.  
  285.    //activate dma
  286.    struct DMAregs* DMA0 = (struct DMAregs*)&(ACCESS(DMABASE));
  287.    DMA0->CS =1<<31;  // reset
  288.    DMA0->CONBLK_AD=0;
  289.    DMA0->TI=0;
  290.    DMA0->CONBLK_AD = (unsigned int)(instrPage.p);
  291.    DMA0->CS =(1<<0)|(255 <<16);  // enable bit = 0, clear end flag = 1, prio=19-16
  292. }
  293.  
  294.  
  295.  
  296. int main(int argc, char **argv)
  297. {
  298.  
  299.     if (argc>1) {
  300.       setup_fm();
  301.       setupDMA(argc>2?atof(argv[2]):103.3);
  302.       playWav(argv[1], argc>3?atof(argv[3]):22500);
  303.     } else
  304.       fprintf(stderr, "Usage:   program wavfile.wav [freq] [sample rate]\n\nWhere wavfile is 16 bit 22.5kHz Mono.  Set wavfile to '-' to use stdin.\nfreq is in Mhz (default 103.3)\nsample rate of wav file in Hz\n\nPlay an empty file to transmit silence\n");
  305.  
  306.     return 0;
  307.  
  308. } // main
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement