daily pastebin goal
8%
SHARE
TWEET

pi-fm-transmitter

a guest Jan 23rd, 2013 3,033 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top