Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- NeuroBytes v07 runtime program
- Includes integer-ized and approximated Izhikevich dynamics. Drives the RGB LED using software PWM.
- Copyright 2016, Zach Fredin
- zach@neurotinker.com
- Revision 1/21/2016
- Distributed under terms of the GNU General Public License, version 3.
- This file is part of NeuroBytes.
- NeuroBytes is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- NeuroBytes is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with NeuroBytes. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <avr/io.h>
- #include <avr/interrupt.h>
- /* ________________________________________
- | \
- | |DEND1| |DEND2| NeuroBytes v0.7 \
- | |AXON1| \
- | |
- | D1 |
- | R G B |
- | |AXON2| /
- | |DEND3| |DEND4| /
- |_______________________________________/
- DEND1-SIG PD6 DEND1-TYPE PD5
- DEND2-SIG PB7 DEND2-TYPE PB6
- DEND3-SIG PB0 DEND3-TYPE PD7
- DEND4-SIG PB1 DEND4-TYPE PB2
- AXON1-SIG PD0 AXON1-TYPE PC5
- AXON2-SIG PC1 AXON2-TYPE PC0
- D1-RED PC3
- D1-GRN PC4
- D1-BLU PC2
- */
- /*
- for a=5,b=2,c=-65,d=8,E=6,F=2,G=16,H=161:
- I v (if stable) fire rate (Hz, 1ms cycles, avg over 500)
- 0 -102 0
- 1 -101 0
- 2 -100 0
- 3 -99 0
- 4 -99 0
- 5 -98 0
- 6 -97 0
- 7 -96 0
- 8 -95 0
- 9 -95 0
- 10 -94 0
- 11 -93 0
- 12 -92 0
- 13 -91 0
- 14 -91 0
- 15 -90 0
- 16 -89 0
- 17 -87 0
- 18 -86 0
- 19 -86 0
- 20 -85 0
- 21 -83 0
- 22 -81 0
- 23 -81 0
- 24 -79 0
- 25 -77 0
- 26 -77 0
- 27 -75 0
- 28 -71 0
- 29 -71 0
- 30 n/a 28
- 31 n/a 36
- 32 n/a 36
- 33 n/a 44
- 34 n/a 50
- 35 n/a 50
- 36 n/a 50
- 37 n/a 52
- 38 n/a 60
- 39 n/a 60
- 40 n/a 60
- 41 n/a 68
- 42 n/a 72
- 43 n/a 72
- 44 n/a 76
- 45 n/a 76
- 46 n/a 76
- 47 n/a 76
- 48 n/a 76
- 49 n/a 76
- 50 n/a 80
- */
- /* Izhikevich model parameters and coefficients */
- const int16_t a = 5;
- const int16_t b = 2;
- const int16_t c = -65;
- const int16_t d = 8;
- const int16_t E = 6;
- const int16_t F = 2;
- const int16_t G = 16;
- const int16_t H = 161;
- /* Izhikevich model variables */
- volatile int8_t I;
- volatile int8_t I_rest = 21;
- volatile int16_t v = -65;
- volatile int16_t u = 0;
- /* Intra-model variables */
- volatile int16_t vSq = 0;
- volatile int16_t vF = 0;
- volatile uint8_t modelStage = 0;
- volatile int16_t v_prev;
- volatile int16_t u_prev;
- /* Neuron variables */
- volatile uint8_t firing = 0;
- volatile uint8_t fireTimer = 0;
- const uint8_t axonPulseLength = 5;
- volatile unsigned char dendStatus = 0; //current status of four dendrites, including types
- volatile unsigned char dendStatusPrev = 0; //previous dendrite status/type
- volatile int8_t val_Dend[4]; //keeps track of current dendrite's contribution to I (positive or negative)
- volatile uint8_t stg_Dend[4] = {0,0,0,0}; //current stage of each dendrite impulse: 0 is REST, 1 is HOLD, 2 is DECAY
- const int8_t hldTime_Dend[4] = {20,20,20,20};
- volatile int8_t hldTimeCur_Dend[4];
- const int8_t hldVal_Dend[4] = {10,10,10,10};
- const int8_t dec_Dend[4] = {2,2,2,2};
- /* LED variables */
- volatile uint8_t LED[3] = {0,0,0}; //R,G,B, 0-100
- volatile uint8_t LEDtick = 0; //0-100
- volatile uint8_t LEDfade[3][61] = {//fader for -102 <= v < -41, where I=21 (v=-83) is rest (pure green). R,G,B
- {0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,1 ,2 ,2 ,3 ,3 ,4 ,4 ,5 ,5 ,6 ,6 ,7 ,7 ,8 ,8 ,9 ,9 ,10,10,11,11,12,12,13,13,14,14,15,15,16,16,17,17,18,18,19,19,20},
- {1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10,11,12,13,14,15,16,17,18,19,20,20,19,19,18,18,17,17,16,16,15,15,14,14,13,13,12,12,11,11,10,10,9 ,9 ,8 ,8 ,7 ,7 ,6 ,6 ,5 ,5 ,4 ,4 ,3 ,3 ,2 ,2 ,1 ,1 ,0 ,0 },
- {19,18,17,16,15,14,13,12,11,10,9 ,8 ,7 ,6 ,5 ,4 ,3 ,2 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 }
- };
- /* Timing variables */
- volatile uint8_t tick = 0; //ISR flips this to non-zero to update loop
- const uint8_t modelUpdateFrequency = 255; //how many ticks pass before the model recalculates (higher = slower)
- const uint8_t modelUpdateMultiplier = 3; //multiplier for modelUpdateFrequency (since they're 8-bit unsigned integers)
- void getDendrites(void) {
- /* updates dendStatus to the current input value:
- bit MSB 6 5 4 3 2 1 LSB
- DEND 4 3 2 1 4 3 2 1
- input SIG SIG SIG SIG TYPE TYPE TYPE TYPE
- pin PB1 PB0 PB7 PD6 PB2 PD7 PB6 PD5
- */
- dendStatusPrev = dendStatus;
- dendStatus = 0;
- dendStatus |= ((PINB & (1<<PB1))<<6);
- dendStatus |= ((PINB & (1<<PB0))<<6);
- dendStatus |= ((PINB & (1<<PB7))>>2);
- dendStatus |= ((PIND & (1<<PD6))>>2);
- dendStatus |= ((PINB & (1<<PB2))<<1);
- dendStatus |= ((PIND & (1<<PD7))>>5);
- dendStatus |= ((PINB & (1<<PB6))>>5);
- dendStatus |= ((PIND & (1<<PD5))>>5);
- }
- void calcDend(uint8_t dend) {
- /* checks the dendrites and updates I based on hold and decay variables. function only runs on the
- dendrite indicated in the argument so it can be staged via updateModel(). */
- uint8_t inh = 1; //toggles to 0 if the selected dendrite is excitatory
- if (dendStatus & (1<<dend)) {
- inh = 0;
- }
- switch (stg_Dend[dend]) {
- case 0: //REST
- val_Dend[dend] = 0;
- if (dendStatus & (1<<(dend + 4))) {
- stg_Dend[dend]++;
- }
- break;
- case 1: //HOLD
- val_Dend[dend] = hldVal_Dend[dend];
- if (dendStatus & (1<<(dend + 4))) {
- hldTimeCur_Dend[dend] = hldTime_Dend[dend];
- }
- else {
- hldTimeCur_Dend[dend] -= 1;
- if (hldTimeCur_Dend[dend] == 0) {
- stg_Dend[dend]++;
- }
- }
- break;
- case 2: //DECAY
- if (dendStatus & (1<<(dend + 4))) {
- stg_Dend[dend]--;
- }
- else {
- if ((val_Dend[dend] - dec_Dend[dend]) > 0) {
- val_Dend[dend] -= dec_Dend[dend];
- }
- else {
- stg_Dend[dend] = 0;
- }
- }
- break;
- }
- if (inh == 1) {
- val_Dend[dend] = -val_Dend[dend];
- }
- }
- void calcI(void) {
- /* updates I based on current dendrite values */
- uint8_t i;
- I = I_rest;
- for (i=0;i<4;i++) {
- I += val_Dend[i];
- }
- }
- int16_t square(int16_t input) {
- /* Returns the square of the input if it won't overflow a 16-bit signed integer. If it would, returns 32767.
- NOTE: This function does not shut off interrupts so it may cause atomicity issues! */
- if (input <= 181) {
- return input * input;
- }
- else {
- return 32767;
- }
- }
- void translateColor() {
- /* translates membrane potential (v) values into the RGB array
- resting membrane potential:
- v < -102 pure blue
- -102 <= v < -90 fade blue to green
- v = -90 pure green
- -90 < v =< -71 fade green to red
- v > -71 firing (pure white)
- Execution time: 28.9 us
- */
- if(v < -102) { //3.5 us
- LED[0] = 0;
- LED[1] = 1;
- LED[2] = 20;
- }
- else if((v >= -102) & (v < -41)) { //24.1 us
- LED[0] = LEDfade[0][(uint8_t)(v + 102)];
- LED[1] = LEDfade[1][(uint8_t)(v + 102)];
- LED[2] = LEDfade[2][(uint8_t)(v + 102)];
- }
- else {
- LED[0] = 20;
- LED[1] = 1;
- LED[2] = 0;
- }
- }
- void updateModel(stage) {
- /* This function updates the membrane potential (v) and recovery potential (u) variables based on the
- current I value. Since the Izhikevich model includes a reset check to determine firing, this function
- also updates the 'firing' variable to 1 when the neuron fires. The model uses 16-bit integer math, so
- interrupts are temporarily disabled to maintain atomicity.
- Execution time: 29.0 us max, depending on current stage. */
- uint8_t temp = SREG;
- cli();
- if (stage == 0) {
- getDendrites();
- }
- else if ((stage >= 1) & (stage < 5)) {
- calcDend(stage - 1);
- }
- else if (stage == 5) {
- calcI();
- }
- else if (stage == 6) { //17.4 us
- vSq = square(v);
- }
- else if (stage == 7) {
- v_prev = v;
- u_prev = u;
- vF = v * F;
- }
- else if (stage == 8) { //29.0 us
- if(v_prev > H) {
- v = c;
- u = u_prev + d;
- firing = 1;
- }
- else {
- v = v_prev + (vSq >> E) + vF + G - u_prev + I;
- u = u_prev + (((v_prev >> b) - u_prev) >> a);
- firing = 0;
- }
- }
- else if (stage == 9) { //28.9 us
- translateColor();
- }
- SREG = temp;
- }
- void updateLED(void) {
- /* Updates the RGB LED based on the current membrane potential value. Uses PWM fading and global variables to
- figure out when the various elements should be on. Resolution controlled by LEDtick reset.
- Execution time: 5.6 us */
- if(LED[0] > LEDtick) {
- PORTC &= ~(1<<PC3);
- }
- else {
- PORTC |= (1<<PC3);
- }
- if(LED[1] > LEDtick) {
- PORTC &= ~(1<<PC4);
- }
- else {
- PORTC |= (1<<PC4);
- }
- if(LED[2] > LEDtick) {
- PORTC &= ~(1<<PC2);
- }
- else {
- PORTC |= (1<<PC2);
- }
- LEDtick++;
- if (LEDtick > 100) {
- LEDtick = 0;
- }
- }
- ISR(TIMER0_COMPA_vect) {
- /* Makes ticks fire at a regular interval in the main loop. */
- tick = 1;
- }
- void systemInit(void) {
- /* set up D1 */
- DDRC |= ((1<<PC2) | (1<<PC3) | (1<<PC4));
- PORTC |= ((1<<PC2) | (1<<PC3) | (1<<PC4)); //set pins = LED off
- /* set up dendrites */
- DDRB &= ~((1<<PB7) | (1<<PB0) | (1<<PB1) | (1<<PB6) | (1<<PB2));
- DDRD &= ~((1<<PD5) | (1<<PD6) | (1<<PD7));
- /* set up axons */
- DDRD |= (1<<PD0);
- DDRC |= ((1<<PC1) | (1<<PC0) | (1<<PC5));
- PORTC |= ((1<<PC0) | (1<<PC5)); //set type pins
- /* set up Timer/Counter0 */
- TCCR0A |= ((1<<CTC0) | (1<<CS00) | (1<<CS01)); //CTC, clk/64
- TCNT0 = 0;
- OCR0A = 4; //loop time = 8 * (OCR0A + 1) uS
- TIMSK0 |= (1<<OCIE0A); //enables Output Compare Match A ISR
- /* misc */
- sei(); //enable global interrupts
- }
- void fire(void) {
- PORTC &= ~((1<<PC2) | (1<<PC3) | (1<<PC4));
- fireTimer = axonPulseLength;
- }
- int main(void) {
- systemInit();
- uint8_t i = 0; //counters for update delay
- uint8_t j = 0;
- for(;;) {
- while(tick == 0) { /* idle loop */ }
- /* This stuff happens every 40 uS or so */
- tick = 0;
- if (i < 10) {
- updateModel(i);
- }
- if (firing == 0) {
- updateLED(); //5.6 us
- }
- else {
- fire();
- }
- j++;
- if (j == modelUpdateMultiplier) {
- i++;
- j = 0;
- }
- if (i == modelUpdateFrequency) {
- i = 0;
- if (fireTimer > 0) {
- PORTD |= (1<<PD0);
- PORTC |= (1<<PC1);
- fireTimer--;
- }
- else {
- PORTD &= ~(1<<PD0);
- PORTC &= ~(1<<PC1);
- }
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement