├── Makefile ├── README.md ├── demos ├── README.MD ├── dogshow.nua ├── macbreath.nua ├── rainbow.nua ├── sos.nua └── strobe.nua ├── neopixel rasppi connections.png └── neouart.c /Makefile: -------------------------------------------------------------------------------- 1 | neouart: neouart.c 2 | gcc -o neouart neouart.c 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | NeoUart 2 | ======= 3 | 4 | Drives a WS2812B NeoPixel connected to Raspberry Pi pin P1-08 5 | 6 | Connect a single NeoPixel to your PI as shown in the included image. 7 | 8 | 9 | Syntax: 10 | 11 | Accepts pixelspecs in the format [DD]RRGGBB where... 12 | [DD] is an optional durration in 1/100s of seconds, and 13 | and RR, GG, and BB are the briness level for red, green, and blue respecively. 14 | 15 | All values are hex numbers in the range 00-ff. 16 | 17 | Examples of pixelspecs: 18 | 000000=Black, 05FFFF=white for .05s, 800080=50% blue for 1.28s 19 | 20 | You can specify one or more pixelspecs on the command line, or run the 21 | program without parameters and it will read and execute pixelspecs from 22 | stdin. 23 | 24 | Example: 25 | 26 | neouart 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include // Needed for memset() 7 | #include // Needed for ishexdigit() and isdigit() in parser 8 | #include // Needed for bool type 9 | 10 | /** 11 | * 12 | /* 13 | * NeoUART - A utility for controlling a NeoPixel from a Raspberry PI 14 | * 15 | * Connect... 16 | * NeoPixel data pin -> Raspberry PI GPIO 14 (pin P1-08) 17 | * Neopixel GND pin -> Raspberry PI any ground (pin P1-06, for example) 18 | * NeoPixel VCC pin -> Raspberry PI 3V3 pin (P1-01) 19 | * 20 | * 21 | */ 22 | 23 | // Timing constants 24 | 25 | #define CPU_FRQ (250000000) // Main system clock at 250mhz 26 | #define BIT_FRQ (1000000000/400) // Target output bit width = 400ns as per WS2812data sheet. Note that we will make the H and L data pulses out of 1 or 2 of these bits 27 | 28 | #define UART_FRQ (BIT_FRQ*8) // There are uart 8 cycles per bit on the mini uart BAP11 29 | #define BAUD_DIV (CPU_FRQ/UART_FRQ) // How many cpu cycles per UART cycle? 30 | // Note that this calculation rounds down to a divisor of 12, which yields a bit width of 480ns which is perfect for NeoPixels! 31 | 32 | #define NEOPIXEL_RES_US (50) // How long to hold the data line low to latch a NeoPixel as per WS2812B data sheet 33 | // Note: imperically I have found this only needs to be about 10us, but we will go by the book here. 34 | 35 | 36 | // Lots of extremely helpful and well-written code & ideas borrowed from... 37 | // 38 | // https://github.com/richardghirst/PiBits/blob/master/ServoBlaster/user/servod.c 39 | // Example of clean way to map and access peripherals 40 | 41 | // https://github.com/dwelch67/raspberrypi/blob/master/uart01/uart01.c 42 | // Example of how to access the mini-UART 43 | 44 | // All code below is written for clarity and not speed. There are so many places here that would 45 | // be so satisfying to rewrite in super-optimized, hand crafted ARM assembler. Fight the urge, 46 | // becuase this program spends 99.999% of its time waiting for the UART, so all the work would be for nothing. 47 | 48 | // All references in the form BAPn mean refer to page n in the BCM2835 ARM Peripherals guide here... 49 | // http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf 50 | 51 | 52 | // Note that all addresses here are physical as needed by memmap() 53 | // so 0x7E00 0000 in the Broadcom data sheet = 0x2000 0000 here 54 | 55 | 56 | // For defining peripheral register locations, we first define the 57 | // physical base address as xxx_BASE and then then each register is 58 | // defined as the offset from the base. xxx_LEN sets the length of the peripheral 59 | // address space. The AP() macro is used to apply an offset to a map_peripheral returned pointer. 60 | 61 | 62 | 63 | // General Purpose I/O (GPIO) 64 | #define GPIO_BASE 0x20200000 // BAP-89 65 | 66 | #define GPFSEL1 0x04 67 | #define GPSET0 0x1C 68 | #define GPCLR0 0x28 69 | #define GPPUD 0x94 70 | #define GPPUDCLK0 0x98 71 | #define GPIO_LEN 0xB4 72 | 73 | 74 | // Auxiliary peripherals Register Map 75 | #define AUX_BASE 0x20215000 // BAP-8 76 | 77 | #define AUX_ENABLES 0x04 // BAP9 78 | 79 | #define AUX_ENABLES_SPI2 (BV(2)) // Enable AUX SPI2 80 | #define AUX_ENABLES_SPI1 (BV(1)) // Enable AUX SPI1 81 | #define AUX_ENABLES_UART (BV(0)) // Enable AUX mini UART 82 | 83 | #define AUX_MU_IO_REG 0x40 84 | #define AUX_MU_IER_REG 0x44 85 | 86 | #define AUX_MU_IER_REG_RXCLEAR (BV(1)) // Clear RX FIFO BAP13 87 | #define AUX_MU_IER_REG_TXCLEAR (BV(2)) // Clear RX FIFO BAP13 88 | 89 | 90 | #define AUX_MU_IIR_REG 0x48 91 | 92 | #define AUX_MU_IIR_REG_CLR_TX_FIFO (BV(2)) // BAP 13 Clear the transmit fifo 93 | #define AUX_MU_IIR_REG_CLR_RX_FIFO (BV(1)) // BAP 13 Clear the receive fifo 94 | 95 | 96 | #define AUX_MU_LCR_REG 0x4C 97 | 98 | #define AUX_MU_LCR_REG_8BIT (BV(0)|BV(1)) // 8 bit data size (docs wrong) BAP14 99 | #define AUX_MU_LCR_REG_BREAK (BV(6)) // If set high the UART1_TX line is pulled low continuously BAP14 100 | 101 | 102 | #define AUX_MU_MCR_REG 0x50 103 | 104 | #define AUX_MU_LSR_TX_ILDE (BV(6)) // BAP 15 This bit is set if the transmit FIFO is empty and the transmitter is idle. (Finished shifting out the last bit). 105 | #define AUX_MU_LSR_TX_EMPTY (BV(5)) // BAP 15 This bit is set if the transmit FIFO can accept at least one byte. 106 | #define AUX_MU_LSR_RX_OVERRUN (BV(1)) // BAP 15 This bit is set if there was a receiver overrun. 107 | #define AUX_MU_LSR_RX_READY (BV(0)) // BAP 15 This bit is set if the receive FIFO holds at least 1 symbol. 108 | 109 | 110 | #define AUX_MU_LSR_REG 0x54 111 | 112 | 113 | #define AUX_MU_MSR_REG 0x58 114 | #define AUX_MU_SCRATCH 0x5C 115 | #define AUX_MU_CNTL_REG 0x60 116 | 117 | #define AUX_MU_CNTL_REG_TXEN (BV(1)) // Enable the transmitter BAP17 118 | 119 | 120 | #define AUX_MU_STAT_REG 0x64 121 | 122 | #define AUX_MU_STAT_TX_DONE (BV(9)) // BAP18 This bit is set if the transmitter is idle and the transmit FIFO is empty. It is a logic AND of bits 2 and 8 123 | #define AUX_MU_STAT_TX_EMPTY (BV(8)) // BAP18 If this bit is set the transmitter FIFO is empty. Thus it can accept 8 symbols. 124 | #define AUX_MU_STAT_TX_FULL (BV(5)) // BAP18 Transmit FIFO is full. This is the inverse of bit 1. 125 | #define AUX_MU_STAT_TX_ILDE (BV(3)) // BAP18 This bit tells if the transmitter is idle. Note that the bit will set only for a short time if the transmit FIFO contains data. Normally you want to use bit 9: Transmitter done. 126 | #define AUX_MU_STAT_TX_SPACE (BV(1)) // BAP18 If this bit is set the mini UART transmitter FIFO can accept at least one more symbol. 127 | 128 | 129 | #define AUX_MU_BAUD_REG 0x68 130 | #define AUX_LEN 0x6B 131 | 132 | 133 | 134 | // AP = Access Peripheral 135 | // Takes a base address returned from map_peripheral and an offset in bytes from the base 136 | 137 | // We divide offset by 4 because it is given in bytes, but the pointer is to 32 bit words which are each 4 bytes wide 138 | // so pointer arithmetic will multiply anything added to the base by 4 139 | 140 | #define AP( base , offset ) (*(base+(offset/4))) 141 | 142 | 143 | // Convenience function for bit value 144 | #define BV(bit) (1<bytes; 258 | 259 | 260 | AP( aux , AUX_MU_CNTL_REG ) = 0; // disable transmitter. BAP16 261 | // this will let us queue up bytes in the FIFO without 262 | // worrying about them draining while we do it 263 | // (remember that we could get interrupted anywhere while doing it and the fifo would still drain). 264 | 265 | // Clear out anything that might be lingering in the TX FIFO 266 | 267 | AP( aux , AUX_MU_IIR_REG) = AUX_MU_IER_REG_TXCLEAR; 268 | 269 | // stuff the FIFO with our bytes so they are ready to go out as soon as we enable the transmitter.. 270 | 271 | int len = sizeof( encodedNeoBuffer->bytes ); 272 | 273 | while ( len ) { 274 | 275 | AP( aux , AUX_MU_IO_REG) = *data; 276 | 277 | data++; 278 | len--; 279 | 280 | } 281 | 282 | // Ok, this little turn-on dance is a bit complicated 283 | // Coming into here, the transmitter is disabled, so the data is waiting in the FIFO to go out 284 | // We also assume that the break is aserted, so the signal is low. We need to transition to sending the data 285 | // but don't want to glitch to high before the data starts going out. 286 | 287 | // We do this by fist setting the baud rate to the lowest possible value, which will mean that the 288 | 289 | 290 | AP( aux, AUX_MU_BAUD_REG) = 0xffff; // Direct access the 16 bit baud rate counter, the register gets the divisor - 1 as per BAP19 291 | // 292 | // Set to the slowest possible speed. This will give us plenty of time between when the transmitter starts 293 | // sending the 1st stop bit (which should be a 0) and when we de-assert break and update the baud to the 294 | // real rate. We need this time because it is possible we could get prempted by an interrupt between 295 | // these steps. 296 | 297 | // with a baudreate_reg of ((2^16)-1), which is 65535, we each bit will last.... 298 | // 299 | // (1/250mhz) * 8 * (baudrate_reg+1) = 2.097152 milliseconds 300 | // This should be long enough to reide though any interrupts or task switches that might preempt us 301 | // between when we enable the transmitter and when we disable the break generation. 302 | // 303 | // As long as we make sure the 1st bit out the gate is a 0, and that we wait until the transmitter 304 | // starts sending that 0 bit then the transition from break to data should 305 | // be seamless and glitch free. 306 | // 307 | // Note that having additional 0 bits at the begining of the 1st byte will give us even more time to 308 | // to ride out a possible interruptions. The encoding style used in NeoUART ensures that the first bit of 309 | // of every byte is 0, so we will actually get >4ms of ride though time. 310 | 311 | 312 | AP( aux , AUX_MU_CNTL_REG ) = AUX_MU_CNTL_REG_TXEN; // Bit 1 = enable transmitter. BAP16 313 | // Note that the transmitter will immediately start sending the 1st byte in the fifo, but we cant see it 314 | // on the pin yet because the break is still asserted and that covers any data that the UART maybe sending 315 | 316 | 317 | while ( AP( aux , AUX_MU_STAT_REG ) & AUX_MU_STAT_TX_FULL ); // Wait for the 1st byte in the FIFO to get loaded into the transmitter 318 | // which indicates two things... 319 | // 1) there is now a zero start bit (slowly) being transmitted on the UART, so we are 320 | // safe to de-assert the break signal without causing a glitch 321 | // 2) there is now a free byte at the end of the FIFO that we can stuff 322 | // with a trailing 0 as a buffer 323 | 324 | AP( aux , AUX_MU_IO_REG) = 0; // Stuff a trailing 0 in the very end of the buffer as padding. This makes sure that there is an extra 325 | // start bit and some zero bits at the end of our real data. Imagine that the very last bit of real data is a 326 | // 0 and just after that bit goes out, we get pre-empted before we can asser tthe break signal. That 0 bit could now 327 | // become a 1. This trailing 0 will ensure that there is an extra 0 bit (9 of them, actually) to properly 328 | // terminate that final real 0 data bit. If something prevents us from asserting break in time after this 329 | // trailing 0 byte, the final stop bit could get expanded into a neopixel 1 bit, but that is ok 330 | // becuase the neopixel we are controlling will have already seen his 24 bits of good data and will 331 | // ignore that extra 1 bit. 332 | 333 | 334 | 335 | AP( aux, AUX_MU_LCR_REG) = AUX_MU_LCR_REG_8BIT; // This will de-assert the break signal, so now the pin will now show whatever the uart is actually transmitting, 336 | // which should be the beginning of the 1st byte in the fifo which should be going out very slowly... 337 | 338 | AP( aux, AUX_MU_BAUD_REG) = BAUD_DIV-1; // direct access to the 16 bit baud rate counter, the register gets the divisor - 1 as per BAP19 339 | // Set the real target baud rate, which will speed up the remaining bits in the transmitter 340 | 341 | 342 | 343 | while ( ! (AP( aux , AUX_MU_STAT_REG ) & AUX_MU_STAT_TX_EMPTY) ); // Wait for all the bytes in the FIFO to be transmitted out... 344 | // Note that there will still be our trailing byte of 0 padding in the actual transmitter 345 | // even though the FIFO is empty. It is no problem if we assert break while those 0 bits are 346 | // are still being transmitted becuase the line will just be going from low (beucase of 347 | // of the tailing 0 bits) to low (becuase of the break signal). 348 | 349 | // If we get interrupted here it is no big deal because all the pixels already have thier data 350 | 351 | AP( aux, AUX_MU_LCR_REG) = AUX_MU_LCR_REG_BREAK | AUX_MU_LCR_REG_8BIT;// BAP14 turn break on - make pin goto 0 volts 352 | 353 | usleep( NEOPIXEL_RES_US ); // Hold low for at least this long to let the NeoPixels latch 354 | // Almost certainly not needed, but good from to have so the code will still work 355 | // when the ARM77 @ 2.3THz comes out.... 356 | 357 | } // senddata 358 | 359 | 360 | 361 | // Encode a 24 bit value into 8 bytes where each byte has the format... 362 | // 0b?0?10?10 363 | // Where each ? is a bit from the orginal 24 bit value. 364 | // This wonky format is designed to generate correct NeoPixel 365 | // signals when sent out a serial port and surrounded with stop and start bits. 366 | 367 | void encodebits( unsigned x , EncodedNeoBuffer *buffer ) { 368 | 369 | int bits=24; 370 | 371 | unsigned char *b = buffer->bytes; 372 | 373 | while (bits) { 374 | 375 | // Process 3 bits of the input into 1 byte on the output 376 | // 377 | // Note that we processes the input by shifting up and checking the high bit (#23) 378 | // This is becuase NeoPixels actually take thier data in MSB first order 379 | // while serial ports send in LSB first order 380 | 381 | unsigned char t=0b00010010; // initialize with all the known 1's 382 | 383 | if (x & (1<<23) ) { 384 | t |= 0b00000100; 385 | } 386 | 387 | x <<= 1 ; 388 | bits--; 389 | 390 | 391 | if (x & (1<<23) ) { 392 | t |= 0b00100000; 393 | } 394 | 395 | x <<= 1 ; 396 | bits--; 397 | 398 | 399 | if (x & (1<<23) ) { 400 | t |= 0b10000000; 401 | } 402 | 403 | x <<= 1 ; 404 | bits--; 405 | 406 | *b = t; 407 | 408 | b++; 409 | 410 | } 411 | 412 | } 413 | 414 | // Display the passed color for durration on the attached NeoPixel 415 | // Passed number in the format 0xDDRRGGBB 416 | // DD = 0-ff durration in 1/100's of seconds 417 | // RR = 0-ff red brightness 418 | // GG = 0-ff green brightness 419 | // BB = 0-ff blue brightness 420 | // 421 | // Nice format becuase durration defaults to 0 if ommited, and then you just have RRGGBB. 422 | 423 | 424 | bool ws2811mode = false; // For WS28122 NeoPixels, the bytes are sent in RGB order. 425 | 426 | bool verbosemode = true; // print messages 427 | 428 | 429 | void showpixel( unsigned pixelspec ) { 430 | 431 | EncodedNeoBuffer buffer; 432 | 433 | 434 | unsigned pixeldata; 435 | 436 | if (ws2811mode) { // WS2811 expects data in RGB order 437 | 438 | pixeldata = (pixelspec & 0xFFFFFF); 439 | 440 | } else { // WS2812 expects data in GRB order 441 | 442 | pixeldata = ( (pixelspec & 0x00FF00) << 8 ) | ( ( pixelspec & 0xFF0000 ) >> 8 ) | ( pixelspec & 0x0000FF) ; 443 | 444 | } 445 | 446 | 447 | encodebits( pixeldata , &buffer ); 448 | 449 | sendUart1( &buffer ); 450 | 451 | // Get durration byte. (I know AND not nessisary, but cleaner code) 452 | 453 | unsigned duration = ( ( pixelspec & 0xff000000 ) >> 24); 454 | 455 | if (verbosemode) { 456 | printf("showing pixel r=%02x g=%02x b=%02x d=%0d.%02ds\n", (pixelspec & 0xff0000) >> 16 , (pixelspec & 0xff00) >> 8 , (pixelspec & 0xff) , duration / 100 , duration % 100 ); 457 | } 458 | 459 | if (duration) usleep( duration * 10000); // Convert 1/100s to us 460 | 461 | } 462 | 463 | signed smoothsteps=0; // number of hundreths of seconds to smooth consecutive colors 464 | // This has to be signed becuase c does not do int/unsigned=int 465 | 466 | // Previous pixel - start at zero 467 | int r_previous=0; 468 | int g_previous=0; 469 | int b_previous=0; 470 | 471 | // show next pixel with smoothing between consecutive pixels. 472 | 473 | void shownextpixel( unsigned pixelspec ) { 474 | 475 | if (smoothsteps) { 476 | 477 | // Don't let all this bitshifting scare you - the ARM can do it exreemely efficiently 478 | 479 | int r_new = (pixelspec & 0xff0000) >> 16; 480 | int g_new = (pixelspec & 0x00FF00) >> 8; 481 | int b_new = (pixelspec & 0x0000FF); 482 | 483 | // Intermediate values held as 1/256th fixed point 484 | // Since the most number of steps is 255, this should avoid rounding errors 485 | 486 | int r_current_256ths = r_previous * 256; 487 | int g_current_256ths = g_previous * 256; 488 | int b_current_256ths = b_previous * 256; 489 | 490 | // needed this intermediate step to force the calculation to be signed. 491 | // I could not figure out a way to make a signed int liteereal without a cast. Can u? 492 | 493 | int r_diff = (r_new - r_previous); 494 | int g_diff = (g_new - g_previous); 495 | int b_diff = (b_new - b_previous); 496 | 497 | int r_step_256ths = (r_diff*256) / smoothsteps; 498 | int g_step_256ths = (g_diff*256) / smoothsteps; 499 | int b_step_256ths = (b_diff*256) / smoothsteps; 500 | 501 | int i; 502 | 503 | for( i= 0 ; i<(smoothsteps-1) ; i++ ) { // Stop one step before final value. We will send final speorately to make suire we land directly on it rather than accumulaing rounding errors. 504 | 505 | r_current_256ths += r_step_256ths; 506 | g_current_256ths += g_step_256ths; 507 | b_current_256ths += b_step_256ths; 508 | 509 | int r = r_current_256ths/256; 510 | int g = g_current_256ths/256; 511 | int b = b_current_256ths/256; 512 | 513 | // Show each pixel step for 1/100th of a second 514 | unsigned pixelspec = ( 0x01 << 24 ) | ( r << 16) | ( (g << 8 ) | b ); 515 | 516 | showpixel( pixelspec ); 517 | 518 | } 519 | 520 | r_previous = r_new; 521 | g_previous = g_new; 522 | b_previous = b_new; 523 | 524 | } 525 | 526 | showpixel(pixelspec); 527 | 528 | } 529 | 530 | 531 | 532 | int main(int argc, char **argv) 533 | { 534 | 535 | int arg=1; 536 | 537 | while (arg