Close

NeuroBytes v0.92 Prototype

A project log for NeuroBytes

Build your own nervous system!

zakqwyzakqwy 02/14/2017 at 22:450 Comments

Today, the NeuroBytes project takes its first baby step into the 21st century.

Okay, ignore the extremely sketchy construction techniques. It's an STM32F0 Discovery board, chosen due to its extremely low cost (under $10) and built-in ST-LINK programmer. My modifications include an RGB LED tied into the output compare registers of TIM1, along with a bottom-mounted 'sled' that includes power/ground rails, a 3.3vdc regulator, voltage dividers for the three dendrite signal/type pairs, and a diode for the bridged axon signal/type lines:

The decision to move to a 32-bit platform, and the selection of the STM32F0 specifically (-L0, potentially), wasn't one I made lightly. I am fairly comfortable with the ATtiny line at this point, a chip I have used for NeuroBytes since v0.4 in 2014. ATtinys are cheap, they have plenty of I/O, they use extremely simple open-source command line tools for programming, and the complete manual is under 250 pages. However, ATtiny88s (my current model of choice) doubled in price last year, meaning the STM32F0 is actually the economical option. And the other advantages of the new chipset -- enough hardware PWM channels for the RGB LED, vastly superior math capabilities due to speed and bit width improvements, online debugging capabilities using st-link and gdb, etc -- make the inconveniences (3.3VDC power, different development environment, etc) worthwhile.

The board is wired up as follows:

PINFUNCTION
PB13 / TIM1_CH1NRGB LED Red
PB14 / TIM1_CH2NRGB LED Green
PB15 / TIM1_CH3NRGB LED Blue
PA6Dendrite 1 Type
PA7Dendrite 1 Signal
PC4Dendrite 2 Type
PC5Dendrite 2 Signal
PB0Dendrite 3 Type
PB1Dendrite 3 Signal
PC8 / TIM3_CH3Axon Type/Signal (all)

The non-standard dendrite/axon quantity isn't important -- it's just based on how much FR4 I wanted to dedicate to this build. The resistor dividers and power supply should make this prototype compatible with existing v0.91 stuff; the diode in the axon circuit is there to protect the processor in case they are hooked up to outputs by mistake.

I've been playing around with libopencm3, an open-source firmware library for a variety of ARM Cortex microcontrollers. Getting the toolchain set up and working reliably wasn't particularly simple, but instructions in the official example repository eventually got me there. I wrote a simple program that uses the TIMER1 peripheral to PWM the LED at an extremely high rate (5 kHz) and resolution (10 bits per channel, gamma-corrected), producing some excellent effects:

above: 1/3 second exposure at F16 (ish) and ISO 200, feat. plenty of board-waving.

Code for the LED PWM test is shown below, including an unnecessarily large gamma correction lookup table. No further commentary at the bottom of the code block, so feel free to stop reading now (recommended).

/*
 * This file is part of the libopencm3 project.
 *
 * Copyright (C) 2013 Chuck McManis 
 * Copyright (C) 2013 Onno Kortmann 
 * Copyright (C) 2013 Frantisek Burian  (merge)
 *
 * This library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This library 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library.  If not, see .
 */

/*
	RGB LED PWM test using an STM32F0 discovery board.
	Red		PB13	TIM1_CH1N
	Green	PB14	TIM1_CH2N
	Blue	PB15	TIM1_CH3N
*/

#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/timer.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/cm3/systick.h>

volatile uint8_t tick = 0;

static const uint16_t gamma_lookup[] = {
/*	gamma = 2, input range = 0-1023, output range = 0-9600 */
    0,  0,  0,  0,  0,  0,  0,  0,  1,  1,  1,  1,  1,  2,  2,  2,
    2,  3,  3,  3,  4,  4,  4,  5,  5,  6,  6,  7,  7,  8,  8,  9,
    9, 10, 11, 11, 12, 13, 13, 14, 15, 15, 16, 17, 18, 19, 19, 20,
   21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
   38, 39, 40, 41, 42, 44, 45, 46, 48, 49, 50, 52, 53, 54, 56, 57,
   59, 60, 62, 63, 65, 66, 68, 69, 71, 73, 74, 76, 78, 79, 81, 83,
   85, 86, 88, 90, 92, 94, 95, 97, 99,101,103,105,107,109,111,113,
  115,117,119,121,123,126,128,130,132,134,137,139,141,143,146,148,
  150,153,155,157,160,162,165,167,170,172,175,177,180,182,185,188,
  190,193,196,198,201,204,206,209,212,215,218,220,223,226,229,232,
  235,238,241,244,247,250,253,256,259,262,265,268,271,275,278,281,
  284,287,291,294,297,301,304,307,311,314,317,321,324,328,331,335,
  338,342,345,349,352,356,360,363,367,371,374,378,382,386,389,393,
  397,401,405,408,412,416,420,424,428,432,436,440,444,448,452,456,
  460,464,469,473,477,481,485,489,494,498,502,507,511,515,520,524,
  528,533,537,542,546,551,555,560,564,569,573,578,583,587,592,596,
  601,606,611,615,620,625,630,634,639,644,649,654,659,664,669,674,
  679,684,689,694,699,704,709,714,719,724,729,735,740,745,750,756,
  761,766,771,777,782,788,793,798,804,809,815,820,826,831,837,842,
  848,853,859,865,870,876,882,887,893,899,904,910,916,922,928,933,
  939,945,951,957,963,969,975,981,987,993,999,1005,1011,1017,1023,1029,
  1036,1042,1048,1054,1060,1067,1073,1079,1086,1092,1098,1105,1111,1117,1124,1130,
  1137,1143,1150,1156,1163,1169,1176,1182,1189,1195,1202,1209,1215,1222,1229,1236,
  1242,1249,1256,1263,1269,1276,1283,1290,1297,1304,1311,1318,1325,1332,1339,1346,
  1353,1360,1367,1374,1381,1388,1395,1402,1410,1417,1424,1431,1439,1446,1453,1460,
  1468,1475,1482,1490,1497,1505,1512,1520,1527,1534,1542,1550,1557,1565,1572,1580,
  1587,1595,1603,1610,1618,1626,1634,1641,1649,1657,1665,1673,1680,1688,1696,1704,
  1712,1720,1728,1736,1744,1752,1760,1768,1776,1784,1792,1800,1808,1817,1825,1833,
  1841,1849,1858,1866,1874,1882,1891,1899,1907,1916,1924,1933,1941,1949,1958,1966,
  1975,1983,1992,2001,2009,2018,2026,2035,2044,2052,2061,2070,2078,2087,2096,2105,
  2114,2122,2131,2140,2149,2158,2167,2176,2185,2194,2202,2211,2220,2230,2239,2248,
  2257,2266,2275,2284,2293,2302,2312,2321,2330,2339,2349,2358,2367,2377,2386,2395,
  2405,2414,2424,2433,2442,2452,2461,2471,2480,2490,2500,2509,2519,2528,2538,2548,
  2557,2567,2577,2586,2596,2606,2616,2626,2635,2645,2655,2665,2675,2685,2695,2705,
  2715,2725,2735,2745,2755,2765,2775,2785,2795,2805,2815,2826,2836,2846,2856,2866,
  2877,2887,2897,2908,2918,2928,2939,2949,2959,2970,2980,2991,3001,3012,3022,3033,
  3043,3054,3065,3075,3086,3097,3107,3118,3129,3139,3150,3161,3172,3182,3193,3204,
  3215,3226,3237,3248,3258,3269,3280,3291,3302,3313,3324,3335,3347,3358,3369,3380,
  3391,3402,3413,3425,3436,3447,3458,3470,3481,3492,3503,3515,3526,3538,3549,3560,
  3572,3583,3595,3606,3618,3629,3641,3652,3664,3676,3687,3699,3711,3722,3734,3746,
  3757,3769,3781,3793,3804,3816,3828,3840,3852,3864,3876,3888,3900,3912,3924,3936,
  3948,3960,3972,3984,3996,4008,4020,4032,4044,4057,4069,4081,4093,4106,4118,4130,
  4142,4155,4167,4180,4192,4204,4217,4229,4242,4254,4267,4279,4292,4304,4317,4329,
  4342,4355,4367,4380,4393,4405,4418,4431,4444,4456,4469,4482,4495,4508,4521,4533,
  4546,4559,4572,4585,4598,4611,4624,4637,4650,4663,4676,4690,4703,4716,4729,4742,
  4755,4769,4782,4795,4808,4822,4835,4848,4862,4875,4888,4902,4915,4929,4942,4956,
  4969,4983,4996,5010,5023,5037,5050,5064,5078,5091,5105,5119,5132,5146,5160,5174,
  5187,5201,5215,5229,5243,5257,5271,5284,5298,5312,5326,5340,5354,5368,5382,5396,
  5411,5425,5439,5453,5467,5481,5495,5510,5524,5538,5552,5567,5581,5595,5610,5624,
  5638,5653,5667,5682,5696,5710,5725,5739,5754,5769,5783,5798,5812,5827,5842,5856,
  5871,5886,5900,5915,5930,5944,5959,5974,5989,6004,6019,6033,6048,6063,6078,6093,
  6108,6123,6138,6153,6168,6183,6198,6213,6228,6243,6259,6274,6289,6304,6319,6335,
  6350,6365,6380,6396,6411,6426,6442,6457,6473,6488,6503,6519,6534,6550,6565,6581,
  6596,6612,6628,6643,6659,6674,6690,6706,6722,6737,6753,6769,6784,6800,6816,6832,
  6848,6864,6879,6895,6911,6927,6943,6959,6975,6991,7007,7023,7039,7055,7071,7088,
  7104,7120,7136,7152,7168,7185,7201,7217,7233,7250,7266,7282,7299,7315,7332,7348,
  7364,7381,7397,7414,7430,7447,7463,7480,7496,7513,7530,7546,7563,7580,7596,7613,
  7630,7646,7663,7680,7697,7714,7730,7747,7764,7781,7798,7815,7832,7849,7866,7883,
  7900,7917,7934,7951,7968,7985,8002,8019,8037,8054,8071,8088,8105,8123,8140,8157,
  8175,8192,8209,8227,8244,8261,8279,8296,8314,8331,8349,8366,8384,8401,8419,8436,
  8454,8472,8489,8507,8525,8542,8560,8578,8595,8613,8631,8649,8667,8685,8702,8720,
  8738,8756,8774,8792,8810,8828,8846,8864,8882,8900,8918,8936,8954,8972,8991,9009,
  9027,9045,9063,9082,9100,9118,9137,9155,9173,9192,9210,9228,9247,9265,9284,9302,
  9321,9339,9358,9376,9395,9413,9432,9450,9469,9488,9506,9525,9544,9563,9581,9600 };

void sys_tick_handler(void)
{
	tick = 1;
}

static void systick_setup(int freq) 
{
	systick_set_clocksource(STK_CSR_CLKSOURCE_AHB);
	STK_CVR = 0;
	systick_set_reload(rcc_ahb_frequency / freq);
	systick_counter_enable();
	systick_interrupt_enable();
}

static void clock_setup(void)
{
	rcc_clock_setup_in_hsi_out_48mhz();
}

static void gpio_setup(void)
{
	/* Enable GPIOB clock */
	rcc_periph_clock_enable(RCC_GPIOB);

	/* Set PB13, PB14, and PB15 to alternative function with no 
	   pullup/pulldown */
	gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO13 | 
					GPIO14 | GPIO15);

	/* Set PB13, PB14, and PB15 output options: push-pull, high speed */
	gpio_set_output_options(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_HIGH,
							GPIO13 | GPIO14 | GPIO15);

	/* Set PB13, PB14, and PB15 to alternative function AF2 to enable 
	   TIM1_CH1N, TIM1_CH2N, and TIM1_CH3N outputs */
	gpio_set_af(GPIOB, GPIO_AF2, GPIO13 | GPIO14 | GPIO15);
}

static void tim_setup(void)
{
	/* Enable and reset TIM1 clock */
	rcc_periph_clock_enable(RCC_TIM1);
	timer_reset(TIM1);

	/* Set TIM1 mode to no clock divider ratio, edge alignment, and
	   up direction */
	timer_set_mode(TIM1, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE,
				   TIM_CR1_DIR_UP);
	
	/* Set prescaler to 0: 48 MHz clk */
	timer_set_prescaler(TIM1, 0);
	
	/* Set timer period to 9600: 5 kHz PWM with 9600 steps */
	timer_set_period(TIM1, 9600);

	/* Must be called for advanced timers */
	timer_enable_break_main_output(TIM1);

	/* Set TIM1 Output Compare mode to PWM2 on channels 1N, 2N, and 3N */
	timer_set_oc_mode(TIM1, TIM_OC1, TIM_OCM_PWM1);
	timer_set_oc_mode(TIM1, TIM_OC2, TIM_OCM_PWM1);
	timer_set_oc_mode(TIM1, TIM_OC3, TIM_OCM_PWM1);

	/* Set starting output compare values */
	timer_set_oc_value(TIM1, TIM_OC1, 0);
	timer_set_oc_value(TIM1, TIM_OC2, 0);
	timer_set_oc_value(TIM1, TIM_OC3, 0);
	
	/* Enable outputs */
	timer_enable_oc_output(TIM1, TIM_OC1N);
	timer_enable_oc_output(TIM1, TIM_OC2N);
	timer_enable_oc_output(TIM1, TIM_OC3N);

	/* Enable counter */
	timer_enable_counter(TIM1);
}

static void led_rgb(uint16_t r, uint16_t g, uint16_t b)
{
	timer_set_oc_value(TIM1, TIM_OC1, gamma_lookup[r]);
	timer_set_oc_value(TIM1, TIM_OC2, gamma_lookup[g]);
	timer_set_oc_value(TIM1, TIM_OC3, gamma_lookup[b]);
}

uint8_t i;

static void breakpoint(void) 
{
	if (i == 0)
	{
		i = 1;
	}
	else 
	{
		i = 0;
	}
}

int main(void)
{
	clock_setup();
	gpio_setup();
	tim_setup();
	systick_setup(100000);

	uint16_t r = 1023;
	uint16_t g = 0;
	uint16_t b = 0;
	uint8_t stage = 0;

	while (1) 
	{	
		while (tick == 0) {}
		tick = 0;
		led_rgb(r,g,b);
		switch (stage) 
		{
			case 0: //red to green
			{
				r--;
				g++;	
				if (r == 0) 
				{
					stage++;
				}
				break;
			}
			case 1: //green to blue
			{
				g--;
				b++;
				if (g == 0) 
				{
					stage++;
				}
				break;
			}
			case 2: //blue to red
			{
				b--;
				r++;
				if (b == 0) 
				{
					breakpoint();
					stage = 0;
				}
				break;
			}
		}
	}
}

Discussions