MPM controlled 16ch RGB LED dimmer
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

298 lines
8.2 KiB

  1. /***************************************************************************
  2. * mpm comms *
  3. * *
  4. * Copyright (C) 2011 - 2012 by Olaf Rempel *
  5. * razzor AT kopf MINUS tisch DOT de *
  6. * *
  7. * This program is free software; you can redistribute it and/or modify *
  8. * it under the terms of the GNU General Public License as published by *
  9. * the Free Software Foundation; version 2 of the License, *
  10. * *
  11. * This program is distributed in the hope that it will be useful, *
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  14. * GNU General Public License for more details. *
  15. * *
  16. * You should have received a copy of the GNU General Public License *
  17. * along with this program; if not, write to the *
  18. * Free Software Foundation, Inc., *
  19. * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
  20. ***************************************************************************/
  21. #include <avr/io.h>
  22. #include <avr/interrupt.h>
  23. #include <avr/wdt.h>
  24. #include <stdio.h> /* NULL */
  25. #include "rgb16mpm.h"
  26. /* Dummy Wait:
  27. * req: <cmd:0x00> <len[2]:0x0000>
  28. * rsp: <cmd:0x00 <cause:0x00> <len[2]:0x0000>
  29. */
  30. #define CMD_WAIT 0x00
  31. /* Switch Run Mode:
  32. * req: <cmd:0x01> <len[2]:0x0001> <mode>
  33. * rsp: <cmd:0x01> <cause> <len[2]:0x0000>
  34. */
  35. #define CMD_SWITCH_MODE 0x01
  36. /* Get Software Version:
  37. * req: <cmd:0x02> <len[2]:0x0000>
  38. * rsp: <cmd:0x02> <cause> <len[2]> <version>
  39. */
  40. #define CMD_GET_VERSION 0x02
  41. /* Write RAW color:
  42. * req: <cmd:0x80> <len[2]> <firstchannel> <RGB[x][3]>
  43. * rsp: <cmd:0x80> <cause>,<len[2]:0x0000>
  44. */
  45. #define CMD_WRITE_RAW_COLOR 0x80
  46. #define CMD_WRITE_INIT_COLOR 0x82
  47. /* Read RAW color:
  48. * req: <cmd:0x80> <len[2]> <firstchannel> <count>
  49. * rsp: <cmd:0x80> <cause>,<len[2]> <RGB[x][3]>
  50. */
  51. #define CMD_READ_RAW_COLOR 0x81
  52. #define CMD_READ_INIT_COLOR 0x83
  53. /* response causes */
  54. #define CAUSE_SUCCESS 0x00
  55. #define CAUSE_NOT_SUPPORTED 0xF0
  56. #define CAUSE_INVALID_PARAMETER 0xF1
  57. #define CAUSE_UNSPECIFIED_ERROR 0xFF
  58. /* parameter for CMD_SWITCH_MODE */
  59. #define BOOTMODE_BOOTLOADER 0x00
  60. #define BOOTMODE_APPLICATION 0x80
  61. #define UART_CALC_BAUDRATE(baudRate) (((uint32_t)F_CPU) / (((uint32_t)baudRate)*16) -1)
  62. static void mpm_reply(uint8_t cmd, uint8_t cause, uint16_t length, uint8_t *data);
  63. const static uint8_t versioninfo[16] = VERSION_STRING;
  64. #define MPM_MAX_DATA_SIZE 64
  65. static uint8_t mpm_data[MPM_MAX_DATA_SIZE];
  66. static uint8_t rx_cmd;
  67. static uint16_t rx_bcnt;
  68. static uint16_t rx_length;
  69. static volatile uint8_t rx_ready;
  70. static uint8_t tx_cmd;
  71. static uint8_t tx_cause;
  72. static uint16_t tx_bcnt;
  73. static uint16_t tx_length;
  74. static uint8_t *tx_data;
  75. ISR(USART_RXC_vect)
  76. {
  77. uint8_t data = UDR;
  78. sei();
  79. /* MPM address stored in TWI address register by bootloader */
  80. if ((UCSRA & (1<<MPCM)) && (data == TWAR)) {
  81. UCSRA &= ~(1<<MPCM);
  82. rx_bcnt = 0;
  83. } else {
  84. if (rx_bcnt == 0) {
  85. rx_cmd = data;
  86. } else if ((rx_bcnt == 1) || (rx_bcnt == 2)) {
  87. rx_length = (rx_length << 8) | data;
  88. } else if ((rx_bcnt -3) < MPM_MAX_DATA_SIZE) {
  89. mpm_data[rx_bcnt -3] = data;
  90. }
  91. /* message complete */
  92. if ((rx_bcnt -2) == rx_length) {
  93. switch (rx_cmd) {
  94. case CMD_WAIT:
  95. case CMD_SWITCH_MODE:
  96. mpm_reply(rx_cmd, CAUSE_SUCCESS, 0, NULL);
  97. break;
  98. case CMD_GET_VERSION:
  99. mpm_reply(rx_cmd, CAUSE_SUCCESS, sizeof(versioninfo), (uint8_t *)versioninfo);
  100. break;
  101. case CMD_WRITE_RAW_COLOR:
  102. case CMD_WRITE_INIT_COLOR:
  103. case CMD_READ_RAW_COLOR:
  104. case CMD_READ_INIT_COLOR:
  105. rx_ready = 1;
  106. break;
  107. default:
  108. mpm_reply(rx_cmd, CAUSE_NOT_SUPPORTED, 0, NULL);
  109. break;
  110. }
  111. }
  112. rx_bcnt++;
  113. }
  114. }
  115. ISR(USART_UDRE_vect)
  116. {
  117. /* enable IRQs again, but prevent multiple UDRE IRQs */
  118. UCSRB &= ~(1<<UDRIE);
  119. sei();
  120. if ((tx_bcnt < 4) || (tx_bcnt -4) < tx_length) {
  121. uint8_t data = 0xAA;
  122. if (tx_bcnt == 1) {
  123. data = tx_cause;
  124. } else if (tx_bcnt == 2) {
  125. data = (tx_length >> 8);
  126. } else if (tx_bcnt == 3) {
  127. data = (tx_length & 0xFF);
  128. } else if ((tx_bcnt -4) < tx_length) {
  129. data = tx_data[tx_bcnt -4];
  130. }
  131. UDR = data;
  132. /* re-enable for next round */
  133. UCSRB |= (1<<UDRIE);
  134. }
  135. tx_bcnt++;
  136. }
  137. ISR(USART_TXC_vect, ISR_NOBLOCK)
  138. {
  139. /* disable RS485 TX */
  140. PORTD &= ~(1<<RXTX);
  141. /* enable MP mode again */
  142. UCSRA |= (1<<MPCM);
  143. #if !(STANDALONE)
  144. /* reset MCU -> enter bootloader */
  145. if ((tx_cmd == CMD_SWITCH_MODE) && (mpm_data[0] == BOOTMODE_BOOTLOADER)) {
  146. wdt_enable(WDTO_15MS);
  147. }
  148. #endif /* !(STANDALONE) */
  149. }
  150. static void mpm_reply(uint8_t cmd, uint8_t cause, uint16_t length, uint8_t *data)
  151. {
  152. /* prepare header */
  153. tx_cmd = cmd;
  154. tx_bcnt = 1;
  155. tx_cause = cause;
  156. tx_length = length;
  157. tx_data = data;
  158. /* enable RS485 TX */
  159. PORTD |= (1<<RXTX);
  160. /* first byte and enable interrupt */
  161. UDR = tx_cmd;
  162. UCSRB |= (1<<UDRIE);
  163. }
  164. void mpm_check_transfer(void)
  165. {
  166. if (rx_ready == 0)
  167. return;
  168. rx_ready = 0;
  169. switch (rx_cmd) {
  170. case CMD_WRITE_RAW_COLOR:
  171. case CMD_WRITE_INIT_COLOR:
  172. {
  173. uint8_t chan = mpm_data[0];
  174. uint8_t pos = 1;
  175. uint8_t cause = CAUSE_SUCCESS;
  176. uint8_t (*baseptr)[16][3] = (rx_cmd == CMD_WRITE_RAW_COLOR) ? &chan_value : &nvram_data.initialRGB;
  177. while ((pos < rx_length)) {
  178. if ((chan >= 16) || ((pos +2) >= MPM_MAX_DATA_SIZE)) {
  179. cause = CAUSE_INVALID_PARAMETER;
  180. break;
  181. }
  182. baseptr[0][chan][COLOR_RED] = mpm_data[pos++];
  183. baseptr[0][chan][COLOR_GREEN] = mpm_data[pos++];
  184. baseptr[0][chan][COLOR_BLUE] = mpm_data[pos++];
  185. chan++;
  186. }
  187. mpm_reply(rx_cmd, cause, 0, NULL);
  188. if (rx_cmd == CMD_WRITE_RAW_COLOR) {
  189. rgb_update(COLOR_MASK, UPDATE_BLOCKING);
  190. } else {
  191. eeprom_write();
  192. }
  193. }
  194. break;
  195. case CMD_READ_RAW_COLOR:
  196. case CMD_READ_INIT_COLOR:
  197. {
  198. uint8_t chan = mpm_data[0];
  199. uint8_t count = mpm_data[1];
  200. uint8_t pos = 0;
  201. uint8_t cause = CAUSE_SUCCESS;
  202. uint8_t (*baseptr)[16][3] = (rx_cmd == CMD_READ_RAW_COLOR) ? &chan_value : &nvram_data.initialRGB;
  203. while (count--) {
  204. if ((chan >= 16) || ((pos +2) >= MPM_MAX_DATA_SIZE)) {
  205. cause = CAUSE_INVALID_PARAMETER;
  206. break;
  207. }
  208. mpm_data[pos++] = baseptr[0][chan][COLOR_RED];
  209. mpm_data[pos++] = baseptr[0][chan][COLOR_GREEN];
  210. mpm_data[pos++] = baseptr[0][chan][COLOR_BLUE];
  211. chan++;
  212. }
  213. mpm_reply(rx_cmd, cause, pos, mpm_data);
  214. }
  215. break;
  216. default:
  217. mpm_reply(rx_cmd, CAUSE_UNSPECIFIED_ERROR, 0, NULL);
  218. break;
  219. }
  220. }
  221. void mpm_init(void)
  222. {
  223. PORTD &= ~(1<<RXTX);
  224. DDRD |= (1<<RXTX);
  225. /* USART config */
  226. /* Multi Drop Mode, 9n1 */
  227. UCSRA = (1<<MPCM);
  228. UCSRB = (1<<RXEN) | (1<<TXEN) | (1<<RXCIE) | (1<<TXCIE) | (1<<UCSZ2);
  229. UCSRC = (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0);
  230. #if (STANDALONE)
  231. OSCCAL = OSCCAL_VALUE;
  232. UBRRH = (UART_CALC_BAUDRATE(BAUDRATE)>>8) & 0xFF;
  233. UBRRL = (UART_CALC_BAUDRATE(BAUDRATE) & 0xFF);
  234. /* MPM address stored in TWI address register */
  235. TWAR = MPM_ADDRESS;
  236. #endif /* (STANDALONE) */
  237. }