Guest User

PIC12f1822 I2C Slave with PWM

a guest
Jun 4th, 2020
96
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 9.92 KB | None | 0 0
  1. /*
  2.  *  PIC12F1822 I2C SLAVE PWM - PWM(P1A/TMR2), I2C Slave, WDT
  3.  *  File: main.c
  4.  *  Author: munjeni (http://github.com/munjeni)
  5.  *  Date 31.May.2020.
  6.  *
  7.  *                        PIC12F1822-I/P
  8.  *                     -------------------
  9.  *                     |                 |
  10.  *               [VDD] |1               8| [VSS]
  11.  *                     |                 |
  12.  *  PWM <--- P1A [RA5] |2               7| [RA0/ICSPDAT]
  13.  *                     |                 |
  14.  *      ---> AN3 [RA4] |3               6| [RA1/ICSPCLK] <--- SCL
  15.  *                     |                 |
  16.  *      [MCLR/VPP/RA3] |4               5| [RA2] <--> SDA
  17.  *                     |                 |
  18.  *                     -------------------
  19.  *
  20.  */
  21.  
  22. // CONFIG1
  23. #pragma config FOSC = INTOSC    // Oscillator Selection->INTOSC oscillator: I/O function on CLKIN pin
  24. #pragma config WDTE = OFF       // Watchdog Timer Enable->WDT disabled
  25. #pragma config PWRTE = OFF      // Power-up Timer Enable->PWRT disabled
  26. #pragma config MCLRE = OFF      // MCLR Pin Function Select->MCLR/VPP pin function is digital input
  27. #pragma config CP = ON          // Flash Program Memory Code Protection->Program memory code protection is enabled
  28. #pragma config CPD = ON         // Data Memory Code Protection->Data memory code protection is enabled
  29. #pragma config BOREN = ON       // Brown-out Reset Enable->Brown-out Reset enabled
  30. #pragma config CLKOUTEN = OFF   // Clock Out Enable->CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin
  31. #pragma config IESO = ON        // Internal/External Switchover->Internal/External Switchover mode is enabled
  32. #pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable->Fail-Safe Clock Monitor is enabled
  33.  
  34. // CONFIG2
  35. #pragma config WRT = OFF        // Flash Memory Self-Write Protection->Write protection off
  36. #pragma config PLLEN = OFF      // PLL Enable->4x PLL disabled
  37. #pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable->Stack Overflow or Underflow will cause a Reset
  38. #pragma config BORV = LO        // Brown-out Reset Voltage Selection->Brown-out Reset Voltage (Vbor), low trip point selected.
  39. #pragma config LVP = OFF        // Low-Voltage Programming Enable->High-voltage on MCLR/VPP must be used for programming
  40.  
  41. #include <xc.h>
  42.  
  43. #define _XTAL_FREQ 32000000
  44.  
  45. #include <stdint.h>
  46. #include <stdbool.h>
  47.  
  48. // write default values to eeprom during programming
  49. __EEPROM_DATA(0xee, 0xff, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00); // enabled, PR2, CCPR1L
  50.  
  51. #define I2C_SLAVE_ADDRESS   0x2a
  52. #define I2C_SLAVE_MASK  0xff
  53.  
  54. #define I2C_BUFFERSIZE  16
  55.  
  56. #define SSPBUFIDX_SLEEP 0
  57. #define SSPBUFIDX_PR2 1
  58. #define SSPBUFIDX_CCPR1L 2
  59. #define SSPBUFIDX_SAVE 3
  60. #define SSPBUFIDX_MAX 4
  61.  
  62. static const uint8_t ENABLED = 0xee;
  63. static const uint8_t DISABLED = 0xdd;
  64.  
  65. static inline void I2C_SlaveReleaseClock(void)
  66. {
  67.     SSP1CON1bits.CKP = 1;
  68. }
  69.  
  70. #if 0
  71. static inline bool I2C_SlaveIsData()
  72. {
  73.     return SSP1STATbits.D_nA;
  74. }
  75.  
  76. static inline bool I2C_SlaveIsAddr(void)
  77. {
  78.     return !(SSP1STATbits.D_nA);
  79. }
  80.  
  81. static inline bool I2C_SlaveIsRead(void)
  82. {
  83.     return (SSP1STATbits.R_nW);
  84. }
  85.  
  86. static inline bool I2C_SlaveIsWrite(void)
  87. {
  88.     return !(SSP1STATbits.R_nW);
  89. }
  90. #endif
  91. static inline bool I2C_SlaveIsNACK(void)
  92. {
  93.     return SSP1CON2bits.ACKSTAT;
  94. }
  95.  
  96. static inline void mssp_clearIRQ(void)
  97. {
  98.     PIR1bits.SSP1IF = 0;
  99. }
  100.  
  101. static inline void SYS_IO_init(void)
  102. {
  103.     nWPUEN = 0; // Enable all weak pull-up resistors
  104.     ANSELA = 0; // Disable all analog functions first
  105. #if 0
  106.     ANSA4 = 1; // Enable analog on RA4
  107. #endif
  108.     // Configure RA5 pin as a digital output for CCP1/P1A
  109.     TRISA5 = 0;
  110.  
  111.     // Alternate Pin Config: Route CCP1/P1A function to RA5 (PIC12(L)F1822 only)
  112.     CCP1SEL = 1;
  113.  
  114.     // Configure RA1 as digital in (SCL), disable analog, enable weak pull-up
  115.     TRISA1 = 1;
  116.     ANSA1 = 0;
  117.     WPUA1 = 1;
  118.  
  119.     // Configure RA2 as digital in (SDA), disable analog, enable weak pull-up
  120.     TRISA2 = 1;
  121.     ANSA2 = 0;
  122.     WPUA2 = 1;
  123. }
  124.  
  125. static inline void OSCILLATOR_Initialize(void)
  126. {
  127.     // SPLLEN enabled; IRCF 32MHz_HF; SCS Clock determined by FOSC<2:0> in Configuration Word 1;
  128.     OSCCON = 0b11110000;
  129.  
  130.     // TUN 0;
  131.     OSCTUNE = 0x00;
  132.  
  133.     // SBOREN disabled;
  134.     BORCON = 0x00;
  135. }
  136.  
  137. static inline void PIC12_TMR2_init(void)
  138. {
  139.     T2CONbits.T2CKPS = 1;   // Timer prescale value = 4
  140.     // Calculate what PR2 should be to achieve 6kHz
  141.     // Period = {[PR2 + 1] * 4 * Tosc * T2CKPS}
  142.     // (1/6000) = {[PR2 + 1] * 4 * (1 / 8 MHz) * 4}
  143.     PR2 = 0xff;
  144.     TMR2ON = 1; // Enable Timer2
  145. }
  146.  
  147. #define CCP1_OFF 0
  148. #define PWM_P1AP1C_AH 12
  149.  
  150. static inline void PIC12_ECCP_init(void)
  151. {
  152.     CCP1CONbits.P1M = 0; // Single Output; P1A modulated
  153.     CCP1CONbits.DC1B = 0x03; // The two LSB's of the 10-bit duty cycle
  154.     CCP1CONbits.CCP1M = PWM_P1AP1C_AH; // PWM mode: P1A,P1C active high
  155.     CCPR1L = (PR2 >> 1); // PWM duty cycle = 50%
  156. }
  157.  
  158. static inline void PIC12_I2C_Slave_init(void)
  159. {
  160.     // SMP Slew rate control disbled; CKE enabled;
  161.     SSP1STAT = 0xc0;
  162.  
  163.     // SSPEN enabled; CKP enabled; SSPM 7-bit address;
  164.     SSP1CON1 = 0x36;
  165.  
  166.     // ACKEN disabled; GCEN disabled; PEN disabled; ACKDT acknowledge; RSEN disabled; RCEN disabled; SEN disabled;
  167.     SSP1CON2 = 0x00;
  168.  
  169.     // SBCDE enabled; BOEN disabled; SCIE disabled; PCIE disabled; DHEN disabled; SDAHT 100ns; AHEN disabled;
  170.     SSP1CON3 = 0x04;
  171.  
  172.     // SSPMSK;
  173.     SSP1MSK = I2C_SLAVE_MASK;
  174.          
  175.     // SSPADD;
  176.     SSP1ADD = (I2C_SLAVE_ADDRESS << 1);
  177.  
  178.     PIR2bits.BCL1IF = 0; // Clear Bus Collision flag
  179.     PIR1bits.SSP1IF = 0; // Clear SSP interrupt flag
  180.     PIE2bits.BCL1IE = 1; // Enable Bus Collision interrupt
  181.     PIE1bits.SSP1IE = 1; // Enable SSP interrupt
  182. }
  183.  
  184. #define GlobalInterruptEnable() (INTCONbits.GIE = 1)
  185. #define PeripheralInterruptEnable() (INTCONbits.PEIE = 1)
  186.  
  187. #define MWA 0b00001001  // 0x09 master write last was address
  188. #define MWD 0b00101001  // 0x29 master write last was data
  189. #define MRA 0b00001101  // 0x0d master read last was address
  190. #define MRD 0b00101100  // 0x2c master read last was data
  191. #define END 0b00101000  // 0x28 master nack
  192.  
  193. static uint8_t SSP_bufIndex = 0;
  194. static uint8_t SSPBuffer[I2C_BUFFERSIZE];
  195. static uint8_t BUF_temp;
  196. static uint8_t i2cStatus;
  197. static uint8_t registerIndex = 0;
  198.    
  199. void __interrupt() ISR(void)
  200. {
  201.     if (PIR1bits.SSP1IF)
  202.     {
  203.         i2cStatus = (SSP1STAT & 0b00101101);            // Mask out unimportant bits
  204.    
  205.         switch (i2cStatus)
  206.         {
  207.             case MWA:
  208.                 BUF_temp = SSP1BUF;                     // Read out to clear BF        
  209.                 SSP_bufIndex = 0;                       // Initialize index    
  210.                 break;
  211.    
  212.             case MWD:
  213.                 BUF_temp = SSP1BUF;                     // Read out to clear BF
  214.                 switch (SSP_bufIndex)
  215.                 {
  216.                     case 0:
  217.                         registerIndex = BUF_temp;
  218.                         SSP_bufIndex = 1;
  219.                         break;
  220.        
  221.                     case 1:
  222.                         if (registerIndex < (uint8_t)SSPBUFIDX_MAX)
  223.                         {
  224.                             SSPBuffer[registerIndex] = BUF_temp;
  225.                         }
  226.                         break;
  227.        
  228.                     default:
  229.                         break;
  230.                 }
  231.                 break;
  232.    
  233.             case MRA:
  234.                 BUF_temp = SSP1BUF;                     // Read out to clear BF
  235.                 if (!I2C_SlaveIsNACK())                 // Only if acknownledged.
  236.                 {
  237.                     if (registerIndex < (uint8_t)SSPBUFIDX_MAX)
  238.                     {
  239.                         SSP1BUF = SSPBuffer[registerIndex];
  240.                         registerIndex++;                   
  241.                     }
  242.                     else
  243.                     {
  244.                         SSP1BUF = 0x11;           // write dummy data in case index is beyond buffer
  245.                         registerIndex = 0;
  246.                     }
  247.                 }                                                              
  248.                 break;
  249.    
  250.             case MRD:
  251.                 if (!I2C_SlaveIsNACK())                 // Only if acknownledged.
  252.                 {
  253.                     if (registerIndex < (uint8_t)SSPBUFIDX_MAX)
  254.                     {
  255.                         SSP1BUF = SSPBuffer[registerIndex];
  256.                         registerIndex++;
  257.                     }
  258.                     else
  259.                     {
  260.                         SSP1BUF = 0x11;           // write dummy data in case index is beyond buffer
  261.                         registerIndex = 0;
  262.                     }
  263.                 }
  264.                 break;
  265.  
  266.             case END:
  267.                 break;
  268.    
  269.             default:           
  270.                 break;
  271.         }
  272.  
  273.         I2C_SlaveReleaseClock(); // Ensure clock stretching released
  274.  
  275.         if (SSPBuffer[SSPBUFIDX_SLEEP] == ENABLED)
  276.         {
  277.             CCP1CONbits.CCP1M = PWM_P1AP1C_AH;              // PWM mode: P1A,P1C active high
  278.         }
  279.         else
  280.         {
  281.             CCP1CONbits.CCP1M = CCP1_OFF;                   // Capture/Compare/PWM = OFF
  282.             LATA5 = 0;                                      // Force PWM output low
  283.         }
  284.    
  285.         PR2 = SSPBuffer[SSPBUFIDX_PR2];
  286.         CCPR1L = SSPBuffer[SSPBUFIDX_CCPR1L];
  287.  
  288.         if (SSPBuffer[SSPBUFIDX_SAVE] == 's')           // s = save
  289.         {
  290.             eeprom_write(0, SSPBuffer[SSPBUFIDX_SLEEP]);
  291.             eeprom_write(1, SSPBuffer[SSPBUFIDX_PR2]);
  292.             eeprom_write(2, SSPBuffer[SSPBUFIDX_CCPR1L]);
  293.  
  294.             SSPBuffer[SSPBUFIDX_SAVE] = 'd';            // d = done
  295.         }
  296.  
  297.         if (SSPBuffer[SSPBUFIDX_SLEEP] != ENABLED)
  298.         {
  299.             SLEEP();
  300.         }
  301.  
  302.         mssp_clearIRQ(); // clear the interrupt flag
  303.     }
  304.  
  305.     if (PIR2bits.BCL1IF)
  306.     {
  307.         BUF_temp = SSP1BUF; // Clear BF
  308.         I2C_SlaveReleaseClock();
  309.         PIR2bits.BCL1IF = 0;
  310.     }
  311. }
  312.  
  313. int main(void)
  314. {
  315.     SYS_IO_init();                                      // Initialize I/O functions
  316.     OSCILLATOR_Initialize();                            // Initialize System Clock      
  317.     PIC12_TMR2_init();                                  // Initialize Timer2
  318.     PIC12_ECCP_init();                                  // Initialize ECCP module
  319.     PIC12_I2C_Slave_init();                             // Initialize I2C Slave
  320.     GlobalInterruptEnable();                            // Enable Global interrupts
  321.     PeripheralInterruptEnable();                        // Enable Peripheral interrupts
  322.  
  323.     uint8_t status = eeprom_read(0);
  324.  
  325.     if (status == ENABLED || status == DISABLED)
  326.     {
  327.         if (status == ENABLED)
  328.             SSPBuffer[SSPBUFIDX_SLEEP] = ENABLED;
  329.         else
  330.             SSPBuffer[SSPBUFIDX_SLEEP] = DISABLED;
  331.     }
  332.     else
  333.     {
  334.         SSPBuffer[SSPBUFIDX_SLEEP] = ENABLED;
  335.     }
  336.                
  337.     SSPBuffer[SSPBUFIDX_PR2] = eeprom_read(1);
  338.     SSPBuffer[SSPBUFIDX_CCPR1L] = eeprom_read(2);
  339.     SSPBuffer[SSPBUFIDX_SAVE] = 'd';
  340.  
  341.     while(1)
  342.     {
  343.         // loop
  344.     }
  345. }
Add Comment
Please, Sign In to add comment