diff --git a/README.md b/README.md index 664eb4dee8d12c87f400be3b07f412c2d422373b..2f7a73884935c818ca2311440908fcbf4d9a7ee5 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,12 @@ Other third party tools are adding lots of examples, etc. See the following rep You can open a github ticket or join my Discord in the #ch32v003fun channel. https://discord.gg/CCeyWyZ +## General notes about the CH32V003. + +CPI/Processor Speed: + +Ignoring branches and load/stores, compressed instructions run at 1 CPI. Non-compressed instructions run at 1 CPI for the first 2 instructions, then further ones take 2 CPI regardless of how many more you have. Running from RAM and running from FLASH have slightly different performance characteristics depending on wait states that should be measured in-situation. + ### Footnotes/links * https://raw.githubusercontent.com/openwch/ch32v003/main/RISC-V%20QingKeV2%20Microprocessor%20Debug%20Manual.pdf Debugging Manual diff --git a/ch32v003fun/ch32v003fun.c b/ch32v003fun/ch32v003fun.c index f7f86c5b4f769b14fbd7b9a8542d88dc3ce46825..ad17c14cf6ac01715afc52e5a4a5131b50728d09 100644 --- a/ch32v003fun/ch32v003fun.c +++ b/ch32v003fun/ch32v003fun.c @@ -895,7 +895,7 @@ int putchar(int c) void handle_debug_input( int numbytes, uint8_t * data ) __attribute__((weak)); void handle_debug_input( int numbytes, uint8_t * data ) { } -static void internal_handle_input( uint32_t * dmdata0 ) +static void internal_handle_input( volatile uint32_t * dmdata0 ) { uint32_t dmd0 = *dmdata0; int bytes = (dmd0 & 0x3f) - 4; @@ -908,11 +908,12 @@ static void internal_handle_input( uint32_t * dmdata0 ) void poll_input() { - uint32_t lastdmd = (*DMDATA0); - if( !(lastdmd & 0x80) ) + volatile uint32_t * dmdata0 = (volatile uint32_t *)DMDATA0; + if( ((*dmdata0) & 0x80) == 0 ) { - internal_handle_input( (uint32_t*)DMDATA0 ); - *DMDATA0 = 0x84; // Negative + internal_handle_input( dmdata0 ); + // Should be 0x80 or so, but for some reason there's a bug that retriggers. + *dmdata0 = 0x00; } } @@ -980,10 +981,6 @@ int putchar(int c) if( timeout-- == 0 ) return 0; // Simply seeking input. - lastdmd = (*DMDATA0); - if( lastdmd ) internal_handle_input( (uint32_t*)DMDATA0 ); - - while( (lastdmd = (*DMDATA0)) & 0x80 ) if( timeout-- == 0 ) return 0; if( lastdmd ) internal_handle_input( (uint32_t*)DMDATA0 ); *DMDATA0 = 0x85 | ((const char)c<<8); return 1; diff --git a/ch32v003fun/ch32v003fun.h b/ch32v003fun/ch32v003fun.h index a6e7666f6cef2e93b1362d02c3cab5b1067ee079..a285ef282754e63e6e7b1a6128f37abbf480e3f4 100644 --- a/ch32v003fun/ch32v003fun.h +++ b/ch32v003fun/ch32v003fun.h @@ -4563,6 +4563,45 @@ RV_STATIC_INLINE void __disable_irq() __asm volatile ("csrw mstatus, %0" : : "r" (result) ); } +/********************************************************************* + * @fn __isenabled_irq + * + * @brief Is Global Interrupt enabled + * + * @return 1: yes, 0: no + */ +RV_STATIC_INLINE uint8_t __isenabled_irq(void) +{ + uint32_t result; + + __asm volatile( +#if __GNUC__ > 10 + ".option arch, +zicsr\n" +#endif + "csrr %0," "mstatus": "=r"(result)); + return (result & 0x08) != 0u; +} + +/********************************************************************* + * @fn __get_cpu_sp + * + * @brief Get stack pointer + * + * @return stack pointer + */ +RV_STATIC_INLINE uint32_t __get_cpu_sp(void); +RV_STATIC_INLINE uint32_t __get_cpu_sp(void) +{ + uint32_t result; + + __asm volatile( +#if __GNUC__ > 10 + ".option arch, +zicsr\n" +#endif + "mv %0, sp" : "=r"(result)); + return result; +} + /********************************************************************* * @fn __NOP * diff --git a/examples/adc_fixed_fs/adc_fixed_fs.c b/examples/adc_fixed_fs/adc_fixed_fs.c index c69b7ba6e965586b038ecc9b2d3e7f07bca1cf44..758b13c5d73a83cd4ad2a84771594d3d1e3ba4dd 100644 --- a/examples/adc_fixed_fs/adc_fixed_fs.c +++ b/examples/adc_fixed_fs/adc_fixed_fs.c @@ -68,8 +68,7 @@ void init_adc() { RCC->CFGR0 |= RCC_ADCPRE_DIV2; // set it to 010xx for /2. // Keep CALVOL register with initial value - ADC1->CTLR1 |= ADC_ExternalTrigConv_T1_TRGO; - ADC1->CTLR2 = ADC_ADON | ADC_DMA | ADC_EXTTRIG; + ADC1->CTLR2 = ADC_ADON | ADC_DMA | ADC_EXTTRIG | ADC_ExternalTrigConv_T1_TRGO; // Possible times: 0->3,1->9,2->15,3->30,4->43,5->57,6->73,7->241 cycles ADC1->SAMPTR2 = 0/*3 cycles*/ << (3/*offset per channel*/ * 2/*channel*/); diff --git a/examples/cap_touch_adc/cap_touch_adc.c b/examples/cap_touch_adc/cap_touch_adc.c index 2bde0c92bc5091432592a47b6279288302836424..98457e9a96ddf11e3823df665af0b5d05bd5a3d4 100644 --- a/examples/cap_touch_adc/cap_touch_adc.c +++ b/examples/cap_touch_adc/cap_touch_adc.c @@ -21,101 +21,7 @@ #include "ch32v003fun.h" #include <stdio.h> -#define ADC_SAMPLE_TIME 2 // Tricky: Don't change this without a lot of experimentation. -#define MAX_SCALECHECK 4 - -// Can either be 0 or 1. -// If 0: Measurement low and rises high. So more pressed is smaller number. -// If 1: Higher number = harder press. Good to pair with TOUCH_FLAT. -// If you are doing more prox, use mode 0, otherwise, use mode 1. -#define TOUCH_SLOPE 1 - -// If you set this to 1, it will glitch the line, so it will only read -// anything reasonable if the capacitance can overcome that initial spike. -// Typically, it seems if you use this you probbly don't need to do -// any pre-use calibration. -#define TOUCH_FLAT 0 - -void InitTouchADC( ) -{ - // ADCCLK = 24 MHz => RCC_ADCPRE = 0: divide sys clock by 2 - RCC->CFGR0 &= ~(0x1F<<11); - - // Set up single conversion on chl 2 - ADC1->RSQR1 = 0; - ADC1->RSQR2 = 0; - - // turn on ADC and set rule group to sw trig - ADC1->CTLR2 |= ADC_ADON | ADC_EXTSEL; - - // Reset calibration - ADC1->CTLR2 |= ADC_RSTCAL; - while(ADC1->CTLR2 & ADC_RSTCAL); - - // Calibrate - ADC1->CTLR2 |= ADC_CAL; - while(ADC1->CTLR2 & ADC_CAL); -} - -// Run from RAM to get even more stable timing. -// This function call takes about 8.1uS to execute. -uint32_t ReadTouchPin( GPIO_TypeDef * io, int portpin, int adcno, int iterations ) __attribute__((noinline, section(".srodata"))); -uint32_t ReadTouchPin( GPIO_TypeDef * io, int portpin, int adcno, int iterations ) -{ - uint32_t ret = 0; - - ADC1->RSQR3 = adcno; - ADC1->SAMPTR2 = ADC_SAMPLE_TIME<<(3*adcno); - - uint32_t CFGBASE = io->CFGLR & (~(0xf<<(4*portpin))); - uint32_t CFGFLOAT = ((GPIO_CFGLR_IN_PUPD)<<(4*portpin)) | CFGBASE; - uint32_t CFGDRIVE = (GPIO_CFGLR_OUT_2Mhz_PP)<<(4*portpin) | CFGBASE; - - // If we run multiple times with slightly different wait times, we can - // reduce the impact of the ADC's DNL. - - -#if TOUCH_FLAT == 1 -#define RELEASEIO io->OUTDR = 1<<(portpin+16*TOUCH_SLOPE); io->CFGLR = CFGFLOAT; -#else -#define RELEASEIO io->CFGLR = CFGFLOAT; io->OUTDR = 1<<(portpin+16*TOUCH_SLOPE); -#endif - -#define INNER_LOOP( n ) \ - { \ - /* Only lock IRQ for a very narrow window. */ \ - __disable_irq(); \ - \ - /* Tricky - we start the ADC BEFORE we transition the pin. By doing \ - this We are catching it onthe slope much more effectively. */ \ - ADC1->CTLR2 = ADC_SWSTART | ADC_ADON | ADC_EXTSEL; \ - \ - ADD_N_NOPS( n ) \ - \ - RELEASEIO \ - \ - /* Sampling actually starts here, somewhere, so we can let other \ - interrupts run */ \ - __enable_irq(); \ - while(!(ADC1->STATR & ADC_EOC)); \ - io->CFGLR = CFGDRIVE; \ - io->OUTDR = 1<<(portpin+(16*(1-TOUCH_SLOPE))); \ - ret += ADC1->RDATAR; \ - } - - int i; - for( i = 0; i < iterations; i++ ) - { - // Wait a variable amount of time based on loop iteration, in order - // to get a variety of RC points and minimize DNL. - - INNER_LOOP( 0 ); - INNER_LOOP( 2 ); - INNER_LOOP( 4 ); - } - - return ret; -} +#include "ch32v003_touch.h" int main() { @@ -133,8 +39,6 @@ int main() { uint32_t sum[8] = { 0 }; - int j; - uint32_t start = SysTick->CNT; // Sampling all touch pads, 3x should take 6030 cycles, and runs at about 8kHz @@ -156,28 +60,3 @@ int main() } } -/* - * MIT License - * - * Copyright (c) 2023 Valve Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - - diff --git a/examples/color_lcd/Makefile b/examples/color_lcd/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..c2417fba9d1ccb8e0fffd6e13978439be546ee73 --- /dev/null +++ b/examples/color_lcd/Makefile @@ -0,0 +1,10 @@ +all : flash + +TARGET:=color_lcd + +include ../../ch32v003fun/ch32v003fun.mk + +flash : cv_flash +clean : cv_clean + + diff --git a/examples/color_lcd/ch32v_hal.h b/examples/color_lcd/ch32v_hal.h new file mode 100644 index 0000000000000000000000000000000000000000..e4bc3650bf3533697ca30c58af4ad852d2dbd866 --- /dev/null +++ b/examples/color_lcd/ch32v_hal.h @@ -0,0 +1,69 @@ +// +// CH32V Hardware Abstraction layer +// written by Larry Bank +// bitbank@pobox.com +// +// Copyright 2023 BitBank Software, Inc. All Rights Reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef CH32V_HAL_H_ +#define CH32V_HAL_H_ + +#include "ch32v003fun.h" +#include <stdio.h> +#include <stdint.h> + +#define BITBANG +// GPIO pin states +enum { + OUTPUT = 0, + INPUT, + INPUT_PULLUP, + INPUT_PULLDOWN +}; + +#define PROGMEM +#define memcpy_P memcpy +#define pgm_read_byte(s) *(uint8_t *)s +#define pgm_read_word(s) *(uint16_t *)s + +// Wrapper methods +void delay(int i); +// +// Digital pin functions use a numbering scheme to make it easier to map the +// pin number to a port name and number +// The GPIO ports A-D become the most significant nibble of the pin number +// for example, to use Port C pin 7 (C7), use the pin number 0xC7 +// +void pinMode(uint8_t u8Pin, int iMode); +uint8_t digitalRead(uint8_t u8Pin); +void digitalWrite(uint8_t u8Pin, uint8_t u8Value); + +// The Wire library is a C++ class; I've created a work-alike to my +// BitBang_I2C API which is a set of C functions to simplify I2C +void I2CInit(uint8_t u8SDA, uint8_t u8SCL, int iSpeed); +void I2CWrite(uint8_t u8Addr, uint8_t *pData, int iLen); +int I2CRead(uint8_t u8Addr, uint8_t *pData, int iLen); +void I2CReadRegister(uint8_t u8Addr, uint8_t u8Reg, uint8_t *pData, int iLen); +int I2CTest(uint8_t u8Addr); +void I2CSetSpeed(int iSpeed); + +// SPI1 (polling mode) +void SPI_write(uint8_t *pData, int iLen); +void SPI_begin(int iSpeed, int iMode); + + +// Random stuff +void Standby82ms(uint8_t iTicks); +void breatheLED(uint8_t u8Pin, int iPeriod); + + +#endif /* CH32V_HAL_H_ */ diff --git a/examples/color_lcd/ch32v_hal.inl b/examples/color_lcd/ch32v_hal.inl new file mode 100644 index 0000000000000000000000000000000000000000..ff58e27932e39ba3224ad11e18f94eca808955cb --- /dev/null +++ b/examples/color_lcd/ch32v_hal.inl @@ -0,0 +1,492 @@ +// +// CH32V Hardware Abstraction layer +// written by Larry Bank +// bitbank@pobox.com +// +// Copyright 2023 BitBank Software, Inc. All Rights Reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "ch32v_hal.h" + +#ifdef BITBANG +uint8_t u8SDA_Pin, u8SCL_Pin; +int iDelay = 1; +#endif + +void delay(int i) +{ + Delay_Ms(i); +} +// Arduino-like API defines and function wrappers for WCH MCUs + +void pinMode(uint8_t u8Pin, int iMode) +{ +GPIO_TypeDef *pGPIO; + + if (u8Pin < 0xa0 || u8Pin > 0xdf) return; // invalid pin number + + switch (u8Pin & 0xf0) { + case 0xa0: + RCC->APB2PCENR |= RCC_APB2Periph_GPIOA; + pGPIO = GPIOA; + break; + case 0xc0: + RCC->APB2PCENR |= RCC_APB2Periph_GPIOC; + pGPIO = GPIOC; + break; + case 0xd0: + RCC->APB2PCENR |= RCC_APB2Periph_GPIOD; + pGPIO = GPIOD; + break; + } + u8Pin &= 0xf; // isolate the pin from this port + pGPIO->CFGLR &= ~(0xf << (4 * u8Pin)); // unset all flags + + switch (iMode) { + case OUTPUT: + pGPIO->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP) << (4*u8Pin); + break; + case INPUT: + pGPIO->CFGLR |= (GPIO_CNF_IN_FLOATING << (4*u8Pin)); + break; + case INPUT_PULLUP: + pGPIO->CFGLR |= (GPIO_CNF_IN_PUPD << (4*u8Pin)); + pGPIO->BSHR = (1 << u8Pin); + break; + case INPUT_PULLDOWN: + pGPIO->CFGLR |= (GPIO_CNF_IN_PUPD << (4 * u8Pin)); + pGPIO->BCR = (1 << u8Pin); + break; + } // switch on iMode +} /* pinMode() */ + +uint8_t digitalRead(uint8_t u8Pin) +{ + uint32_t u32GPIO = 1 << (u8Pin & 0xf); + uint32_t u32Value = 0; + switch (u8Pin & 0xf0) { + case 0xa0: + u32Value = GPIOA->INDR & u32GPIO; + break; + case 0xc0: + u32Value = GPIOC->INDR & u32GPIO; + break; + case 0xd0: + u32Value = GPIOD->INDR & u32GPIO; + break; + } + return (u32Value == 0); +} /* digitalRead() */ + +void digitalWrite(uint8_t u8Pin, uint8_t u8Value) +{ + uint32_t u32Value = 1 << (u8Pin & 0xf); // turn on bit + if (!u8Value) + u32Value <<= 16; // turn off bit + + switch (u8Pin & 0xf0) { + case 0xa0: + GPIOA->BSHR = u32Value; + break; + case 0xc0: + GPIOC->BSHR = u32Value; + break; + case 0xd0: + GPIOD->BSHR = u32Value; + break; + } +} /* digitalWrite() */ + +#ifdef BITBANG +uint8_t SDA_READ(void) +{ + return digitalRead(u8SDA_Pin); +} +void SDA_HIGH(void) +{ + pinMode(u8SDA_Pin, INPUT); +} +void SDA_LOW(void) +{ + pinMode(u8SDA_Pin, OUTPUT); + digitalWrite(u8SDA_Pin, 0); +} +void SCL_HIGH(void) +{ + pinMode(u8SCL_Pin, INPUT); +} +void SCL_LOW(void) +{ + pinMode(u8SCL_Pin, OUTPUT); + digitalWrite(u8SCL_Pin, 0); +} +void I2CSetSpeed(int iSpeed) +{ + if (iSpeed >= 400000) iDelay = 1; + else if (iSpeed >= 100000) iDelay = 10; + else iDelay = 20; +} +void I2CInit(uint8_t u8SDA, uint8_t u8SCL, int iSpeed) +{ + u8SDA_Pin = u8SDA; + u8SCL_Pin = u8SCL; + if (iSpeed >= 400000) iDelay = 1; + else if (iSpeed >= 100000) iDelay = 10; + else iDelay = 20; +} /* I2CInit() */ + +void my_sleep_us(int iDelay) +{ + Delay_Us(iDelay); +} +// Transmit a byte and read the ack bit +// if we get a NACK (negative acknowledge) return 0 +// otherwise return 1 for success +// + +int i2cByteOut(uint8_t b) +{ +uint8_t i, ack; + +for (i=0; i<8; i++) +{ +// my_sleep_us(iDelay); + if (b & 0x80) + SDA_HIGH(); // set data line to 1 + else + SDA_LOW(); // set data line to 0 + b <<= 1; +// my_sleep_us(iDelay); + SCL_HIGH(); // clock high (slave latches data) + my_sleep_us(iDelay); + SCL_LOW(); // clock low + my_sleep_us(iDelay); +} // for i +//my_sleep_us(iDelay); +// read ack bit +SDA_HIGH(); // set data line for reading +//my_sleep_us(iDelay); +SCL_HIGH(); // clock line high +my_sleep_us(iDelay); // DEBUG - delay/2 +ack = SDA_READ(); +//my_sleep_us(iDelay); +SCL_LOW(); // clock low +my_sleep_us(iDelay); // DEBUG - delay/2 +SDA_LOW(); // data low +return (ack == 0); // a low ACK bit means success +} /* i2cByteOut() */ + +// +// Receive a byte and read the ack bit +// if we get a NACK (negative acknowledge) return 0 +// otherwise return 1 for success +// +uint8_t i2cByteIn(uint8_t bLast) +{ +uint8_t i; +uint8_t b = 0; + + SDA_HIGH(); // set data line as input + for (i=0; i<8; i++) + { + my_sleep_us(iDelay); // wait for data to settle + SCL_HIGH(); // clock high (slave latches data) + my_sleep_us(iDelay); + b <<= 1; + if (SDA_READ() != 0) // read the data bit + b |= 1; // set data bit + SCL_LOW(); // clock low + } // for i + if (bLast) + SDA_HIGH(); // last byte sends a NACK + else + SDA_LOW(); +// my_sleep_us(iDelay); + SCL_HIGH(); // clock high + my_sleep_us(iDelay); + SCL_LOW(); // clock low to send ack + my_sleep_us(iDelay); +// SDA_HIGH(); + SDA_LOW(); // data low + return b; +} /* i2cByteIn() */ +// +// Send I2C STOP condition +// +void i2cEnd(void) +{ + SDA_LOW(); // data line low + my_sleep_us(iDelay); + SCL_HIGH(); // clock high + my_sleep_us(iDelay); + SDA_HIGH(); // data high + my_sleep_us(iDelay); +} /* i2cEnd() */ + +int i2cBegin(uint8_t addr, uint8_t bRead) +{ + int rc; +// SCL_HIGH(); +// my_sleep_us(iDelay); + SDA_LOW(); // data line low first + my_sleep_us(iDelay); + SCL_LOW(); // then clock line low is a START signal + addr <<= 1; + if (bRead) + addr++; // set read bit + rc = i2cByteOut(addr); // send the slave address and R/W bit + return rc; +} /* i2cBegin() */ + +void I2CWrite(uint8_t addr, uint8_t *pData, int iLen) +{ +uint8_t b; +int rc; + + i2cBegin(addr, 0); + rc = 1; + while (iLen && rc == 1) + { + b = *pData++; + rc = i2cByteOut(b); + if (rc == 1) // success + { + iLen--; + } + } // for each byte + i2cEnd(); +//return (rc == 1) ? (iOldLen - iLen) : 0; // 0 indicates bad ack from sending a byte +} /* I2CWrite() */ + +int I2CRead(uint8_t addr, uint8_t *pData, int iLen) +{ + i2cBegin(addr, 1); + while (iLen--) + { + *pData++ = i2cByteIn(iLen == 0); + } // for each byte + i2cEnd(); + return 1; +} /* I2CRead() */ + +int I2CTest(uint8_t addr) +{ +int response = 0; + + if (i2cBegin(addr, 0)) // try to write to the given address + { + response = 1; + } + i2cEnd(); +return response; +} /* I2CTest() */ + +#else // hardware I2C + +void I2CSetSpeed(int iSpeed) +{ + I2C_InitTypeDef I2C_InitTSturcture={0}; + + I2C_InitTSturcture.I2C_ClockSpeed = iSpeed; + I2C_InitTSturcture.I2C_Mode = I2C_Mode_I2C; + I2C_InitTSturcture.I2C_DutyCycle = I2C_DutyCycle_16_9; + I2C_InitTSturcture.I2C_OwnAddress1 = 0x02; //address; sender's unimportant address + I2C_InitTSturcture.I2C_Ack = I2C_Ack_Enable; + I2C_InitTSturcture.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; + I2C_Init( I2C1, &I2C_InitTSturcture ); +} /* I2CSetSpeed() */ + +void I2CInit(uint8_t iSDA, uint8_t iSCL, int iSpeed) +{ + (void)iSDA; (void)iSCL; + + GPIO_InitTypeDef GPIO_InitStructure={0}; + + // Fixed to pins C1/C2 for now + RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE ); + RCC_APB1PeriphClockCmd( RCC_APB1Periph_I2C1, ENABLE ); + + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_Init( GPIOC, &GPIO_InitStructure ); + + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_Init( GPIOC, &GPIO_InitStructure ); + + I2C_DeInit(I2C1); + I2CSetSpeed(iSpeed); + + I2C_Cmd( I2C1, ENABLE ); + + I2C_AcknowledgeConfig( I2C1, ENABLE ); + while( I2C_GetFlagStatus( I2C1, I2C_FLAG_BUSY ) != RESET ); +} /* I2CInit() */ + +// +// Returns 0 for timeout error +// returns 1 for success +// +int I2CRead(uint8_t u8Addr, uint8_t *pData, int iLen) +{ + int iTimeout = 0; + + I2C_GenerateSTART( I2C1, ENABLE ); + while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_MODE_SELECT ) ); + + I2C_Send7bitAddress( I2C1, u8Addr<<1, I2C_Direction_Receiver ); + + while(iTimeout < 10000 && !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED ) ) { + iTimeout++; + } + if (iTimeout >= 10000) return 0; // error + + iTimeout = 0; + while(iLen && iTimeout < 10000) + { + if( I2C_GetFlagStatus( I2C1, I2C_FLAG_RXNE ) != RESET ) + { + iTimeout = 0; + pData[0] = I2C_ReceiveData( I2C1 ); + pData++; + iLen--; + } else { + iTimeout++; + } + } + + I2C_GenerateSTOP( I2C1, ENABLE ); + return (iLen == 0); + +} /* I2CRead() */ + +void I2CWrite(uint8_t u8Addr, uint8_t *pData, int iLen) +{ + I2C_GenerateSTART( I2C1, ENABLE ); + while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_MODE_SELECT ) ); + + I2C_Send7bitAddress( I2C1, u8Addr<<1, I2C_Direction_Transmitter ); + + while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) ); + + while(iLen) + { + if( I2C_GetFlagStatus( I2C1, I2C_FLAG_TXE ) != RESET ) + { + I2C_SendData( I2C1, pData[0] ); + pData++; + iLen--; + } + } + + while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED ) ); + I2C_GenerateSTOP( I2C1, ENABLE ); + +} /* I2CWrite() */ + +int I2CTest(uint8_t u8Addr) +{ + int iTimeout = 0; + + I2C_ClearFlag(I2C1, I2C_FLAG_AF); + I2C_GenerateSTART( I2C1, ENABLE ); + while(iTimeout < 10000 && !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_MODE_SELECT ) ) { + iTimeout++; + } + if (iTimeout >= 10000) return 0; // no pull-ups, open bus + + I2C_Send7bitAddress( I2C1, u8Addr<<1, I2C_Direction_Transmitter ); + + while(iTimeout < 10000 && !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) ) { + iTimeout++; + } + if (iTimeout >= 10000) return 0; // no device at that address; the MTMS flag will never get set + + I2C_GenerateSTOP( I2C1, ENABLE ); + // check ACK failure flag + return (I2C_GetFlagStatus(I2C1, /*I2C_FLAG_TXE*/I2C_FLAG_AF) == RESET); // 0 = fail, 1 = succeed + +} /* I2CTest() */ +#endif // !BITBANG + +// +// Read N bytes starting at a specific I2C internal register +// returns 1 for success, 0 for error +// +void I2CReadRegister(uint8_t iAddr, uint8_t u8Register, uint8_t *pData, int iLen) +{ + I2CWrite(iAddr, &u8Register, 1); + I2CRead(iAddr, pData, iLen); +} /* I2CReadRegister() */ + +void SPI_begin(int iSpeed, int iMode) +{ +uint32_t u32Prescaler = 0; + + if (iSpeed >= (FUNCONF_SYSTEM_CORE_CLOCK/2)) + u32Prescaler = SPI_BaudRatePrescaler_2; + else if (iSpeed >= (FUNCONF_SYSTEM_CORE_CLOCK/4)) + u32Prescaler = SPI_BaudRatePrescaler_4; + else if (iSpeed >= (FUNCONF_SYSTEM_CORE_CLOCK/8)) + u32Prescaler = SPI_BaudRatePrescaler_8; + else if (iSpeed >= (FUNCONF_SYSTEM_CORE_CLOCK/16)) + u32Prescaler = SPI_BaudRatePrescaler_16; + else if (iSpeed >= (FUNCONF_SYSTEM_CORE_CLOCK/32)) + u32Prescaler = SPI_BaudRatePrescaler_32; + else if (iSpeed >= (FUNCONF_SYSTEM_CORE_CLOCK/64)) + u32Prescaler = SPI_BaudRatePrescaler_64; + else if (iSpeed >= (FUNCONF_SYSTEM_CORE_CLOCK/128)) + u32Prescaler = SPI_BaudRatePrescaler_128; + else + u32Prescaler = SPI_BaudRatePrescaler_256; + + // Enable GPIOC and SPI + RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_SPI1; + + // PC5 is SCK, 10MHz Output, alt func, p-p + GPIOC->CFGLR &= ~(0xf<<(4*5)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*5); + + // PC6 is MOSI, 10MHz Output, alt func, p-p + GPIOC->CFGLR &= ~(0xf<<(4*6)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*6); + + // Configure SPI + SPI1->CTLR1 = + SPI_NSS_Soft | SPI_CPHA_1Edge | SPI_CPOL_Low | SPI_DataSize_8b | + SPI_Mode_Master | SPI_Direction_1Line_Tx | + u32Prescaler; + + // Enable DMA on SPI + SPI1->CTLR2 |= SPI_I2S_DMAReq_Tx; + + // enable SPI port + SPI1->CTLR1 |= CTLR1_SPE_Set; + + //SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE); // enable DMA on transmit + + //SPI_Cmd( SPI1, ENABLE ); +} /* SPI_begin() */ + +// polling write +void SPI_write(uint8_t *pData, int iLen) +{ +int i = 0; + + while (i < iLen) { + while(!(SPI1->STATR & SPI_STATR_TXE)); // wait for TXE + SPI1->DATAR = *pData++; // send data + i++; + } + while(SPI1->STATR & SPI_STATR_BSY); // wait for not busy +} /* SPI_write() */ diff --git a/examples/color_lcd/color_lcd.c b/examples/color_lcd/color_lcd.c new file mode 100644 index 0000000000000000000000000000000000000000..0856503db67f8b97037e9157d8b3c9bb0b16a8ae --- /dev/null +++ b/examples/color_lcd/color_lcd.c @@ -0,0 +1,80 @@ +// +// color LCD demo +// written by Larry Bank +// bitbank@pobox.com +// +// Copyright 2023 BitBank Software, Inc. All Rights Reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "ch32v_hal.inl" +#include "spi_lcd.inl" +#include <stdlib.h> + +// Pin definitions for the LCD project protoboard +//#define BL_PIN 0xd2 +//#define CS_PIN 0xff +#define BL_PIN 0xd5 +#define CS_PIN 0xd2 +#define DC_PIN 0xd3 +#define RST_PIN 0xd4 + +static uint16_t usPal[8] = {COLOR_BLACK, COLOR_WHITE, COLOR_RED, COLOR_GREEN, + COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_YELLOW}; + +/* White Noise Generator State */ +#define NOISE_BITS 8 +#define NOISE_MASK ((1<<NOISE_BITS)-1) +#define NOISE_POLY_TAP0 31 +#define NOISE_POLY_TAP1 21 +#define NOISE_POLY_TAP2 1 +#define NOISE_POLY_TAP3 0 +uint32_t lfsr = 1; + +/* + * random byte generator + */ +uint8_t rand8(void) +{ + uint8_t bit; + uint32_t new_data; + + for(bit=0;bit<NOISE_BITS;bit++) + { + new_data = ((lfsr>>NOISE_POLY_TAP0) ^ + (lfsr>>NOISE_POLY_TAP1) ^ + (lfsr>>NOISE_POLY_TAP2) ^ + (lfsr>>NOISE_POLY_TAP3)); + lfsr = (lfsr<<1) | (new_data&1); + } + + return lfsr&NOISE_MASK; +} + +int main(void) +{ +int i, dx, iColor; + + SystemInit(); + + lcdInit(LCD_ST7735_80x160, 24000000, CS_PIN, DC_PIN, RST_PIN, BL_PIN); + lcdFill(COLOR_GREEN); + i = 1; dx = 1; + while (1) { + lcdRectangle(rand8() & 127, rand8() & 63, rand8() & 63, rand8() & 31, usPal[(iColor+1)&7], 1); + lcdEllipse(rand8() & 127, rand8() & 63, rand8() & 63, rand8() & 31, usPal[iColor & 7], 1); + iColor++; + lcdWriteString(0,i,"CH32V003 is fast", COLOR_BLUE, COLOR_GREEN, FONT_8x8); + lcdWriteString(0,i+8,"enough for me!", COLOR_RED, COLOR_GREEN, FONT_8x8); + lcdWriteString(0,i+16,"Large Font!", COLOR_WHITE, COLOR_MAGENTA, FONT_12x16); + i += dx; + if (i >= 39 || i == 1) dx = -dx; + } +} diff --git a/examples/color_lcd/funconfig.h b/examples/color_lcd/funconfig.h new file mode 100644 index 0000000000000000000000000000000000000000..998cf76fede1479f162c72ef34d3a4d747430093 --- /dev/null +++ b/examples/color_lcd/funconfig.h @@ -0,0 +1,7 @@ +#ifndef _FUNCONFIG_H +#define _FUNCONFIG_H + +#define CH32V003 1 + +#endif + diff --git a/examples/color_lcd/spi_lcd.h b/examples/color_lcd/spi_lcd.h new file mode 100644 index 0000000000000000000000000000000000000000..03f9d407c1f3d568636ef0e1b5301f302e28113f --- /dev/null +++ b/examples/color_lcd/spi_lcd.h @@ -0,0 +1,113 @@ +// +// spi_lcd.h +// a Sitronix LCD display library +// Created on: Sep 11, 2023 +// written by Larry Bank (bitbank@pobox.com) +// +// +// Copyright 2023 BitBank Software, Inc. All Rights Reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//=========================================================================== +// + +#ifndef USER_SPI_LCD_H_ +#define USER_SPI_LCD_H_ + +#include "funconfig.h" +#include <stdint.h> +#include "ch32v_hal.h" +#include <string.h> + +enum { + LCD_ST7735_80x160 = 0, + LCD_ST7735_80x160_B, + LCD_ST7735_128x128, + LCD_ST7735_128x160, + LCD_ST7789_135x240, + LCD_ST7789_172x320, + LCD_ST7789_240x240, + LCD_ST7789_240x280, + LCD_ST7789_240x320, + LCD_GC9107_128x128, + LCD_ILI9341_240x320, + LCD_COUNT +}; + +enum { + ORIENTATION_0 = 0, + ORIENTATION_90, + ORIENTATION_180, + ORIENTATION_270 +}; + +#ifdef CH32V003 +// 2 complete lines across a 320 pixel color LCD +#define CACHE_SIZE (320*2) +#else // larger RAM systems +#define CACHED_LINES 16 +// enough memory to hold 16 lines of the display for fast character drawing +#define CACHE_SIZE (320*CACHED_LINES) +#endif +// memory offset of visible area (80x160 out of 240x320) + +// Proportional font data taken from Adafruit_GFX library +/// Font data stored PER GLYPH +typedef struct { + uint16_t bitmapOffset; ///< Pointer into GFXfont->bitmap + uint8_t width; ///< Bitmap dimensions in pixels + uint8_t height; ///< Bitmap dimensions in pixels + uint8_t xAdvance; ///< Distance to advance cursor (x axis) + int8_t xOffset; ///< X dist from cursor pos to UL corner + int8_t yOffset; ///< Y dist from cursor pos to UL corner +} GFXglyph; + +typedef struct { + uint8_t *bitmap; ///< Glyph bitmaps, concatenated + GFXglyph *glyph; ///< Glyph array + uint8_t first; ///< ASCII extents (first char) + uint8_t last; ///< ASCII extents (last char) + uint8_t yAdvance; ///< Newline distance (y axis) +} GFXfont; + +void lcdFill(uint16_t u16Color); +void lcdInit(int iLCDType, uint32_t u32Speed, uint8_t u8CSPin, uint8_t u8DCPin, uint8_t u8RSTPin, uint8_t u8BLPin); +void lcdWriteCMD(uint8_t ucCMD); +void lcdWriteDATA(uint8_t *pData, int iLen); +int lcdWriteString(int x, int y, char *szMsg, uint16_t usFGColor, uint16_t usBGColor, int iFontSize); +int lcdDrawTile(int x, int y, int iTileWidth, int iTileHeight, unsigned char *pTile, int iPitch); +int lcdWriteStringCustom(GFXfont *pFont, int x, int y, char *szMsg, uint16_t usFGColor, uint16_t usBGColor, int bBlank); +void lcdOrientation(int iOrientation); +void lcdRectangle(int x, int y, int cx, int cy, uint16_t usColor, int bFill); +void lcdEllipse(int centerX, int centerY, int radiusX, int radiusY, uint16_t color, int bFilled); + +void memset16(uint16_t *u16Dest, uint16_t u16Pattern, int iLen); +#define COLOR_BLACK 0 +#define COLOR_WHITE 0xffff +#define COLOR_RED 0xf800 +#define COLOR_GREEN 0x7e0 +#define COLOR_BLUE 0x1f +#define COLOR_MAGENTA 0xf81f +#define COLOR_CYAN 0x7ff +#define COLOR_YELLOW 0xffe0 + +// MADCTL flip bits +#define MADCTL_YFLIP 0x80 +#define MADCTL_XFLIP 0x40 +#define MADCTL_VFLIP 0x20 + +enum { + FONT_6x8 = 0, + FONT_8x8, + FONT_12x16, + FONT_COUNT +}; + +#endif /* USER_SPI_LCD_H_ */ diff --git a/examples/color_lcd/spi_lcd.inl b/examples/color_lcd/spi_lcd.inl new file mode 100644 index 0000000000000000000000000000000000000000..796267668336054f62bb6ff2509cbf8ee820becf --- /dev/null +++ b/examples/color_lcd/spi_lcd.inl @@ -0,0 +1,1198 @@ +// +// spi_lcd.c +// a Sitronix LCD display library +// Created on: Sep 11, 2023 +// written by Larry Bank (bitbank@pobox.com) +// +// +// Copyright 2023 BitBank Software, Inc. All Rights Reserved. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//=========================================================================== +// + +#include "spi_lcd.h" + +static uint8_t u8CS, u8DC, u8BL; +static uint8_t u8MADCTL; // original value +static int iCursorX, iCursorY; +static int iNativeWidth, iNativeHeight, iNativeXOff, iNativeYOff, iLCDWidth, iLCDHeight, iLCDPitch, iLCDXOff, iLCDYOff; +static uint8_t u8Cache0[CACHE_SIZE]; // ping-pong data buffers +static uint8_t u8Cache1[CACHE_SIZE]; +static uint8_t *pCache0 = u8Cache0, *pCache1 = u8Cache1; +volatile int bDMA = 0; + +const uint8_t ucILI9341InitList[] = { + 4, 0xEF, 0x03, 0x80, 0x02, + 4, 0xCF, 0x00, 0XC1, 0X30, + 5, 0xED, 0x64, 0x03, 0X12, 0X81, + 4, 0xE8, 0x85, 0x00, 0x78, + 6, 0xCB, 0x39, 0x2C, 0x00, 0x34, 0x02, + 2, 0xF7, 0x20, + 3, 0xEA, 0x00, 0x00, + 2, 0xc0, 0x23, // Power control + 2, 0xc1, 0x10, // Power control + 3, 0xc5, 0x3e, 0x28, // VCM control + 2, 0xc7, 0x86, // VCM control2 + 2, 0x36, 0x48, // Memory Access Control + 1, 0x20, // non inverted + 2, 0x3a, 0x55, + 3, 0xb1, 0x00, 0x18, + 4, 0xb6, 0x08, 0x82, 0x27, // Display Function Control + 2, 0xF2, 0x00, // Gamma Function Disable + 2, 0x26, 0x01, // Gamma curve selected + 16, 0xe0, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, + 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, // Set Gamma + 16, 0xe1, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, + 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, // Set Gamma + 3, 0xb1, 0x00, 0x10, // FrameRate Control 119Hz + 0 +}; + +const uint8_t uc240x240InitList[] = { + 1, 0x13, // partial mode off + 1, 0x21, // display inversion off + 2, 0x36,0x60, // memory access 0xc0 for 180 degree flipped + 2, 0x3a,0x55, // pixel format; 5=RGB565 + 3, 0x37,0x00,0x00, // + 6, 0xb2,0x0c,0x0c,0x00,0x33,0x33, // Porch control + 2, 0xb7,0x35, // gate control + 2, 0xbb,0x1a, // VCOM + 2, 0xc0,0x2c, // LCM + 2, 0xc2,0x01, // VDV & VRH command enable + 2, 0xc3,0x0b, // VRH set + 2, 0xc4,0x20, // VDV set + 2, 0xc6,0x0f, // FR control 2 + 3, 0xd0, 0xa4, 0xa1, // Power control 1 + 15, 0xe0, 0x00,0x19,0x1e,0x0a,0x09,0x15,0x3d,0x44,0x51,0x12,0x03, + 0x00,0x3f,0x3f, // gamma 1 + 15, 0xe1, 0x00,0x18,0x1e,0x0a,0x09,0x25,0x3f,0x43,0x52,0x33,0x03, + 0x00,0x3f,0x3f, // gamma 2 + 1, 0x29, // display on + 0 +}; + +const uint8_t ucGC9107InitList[] = { + 1, 0x13, // partial mode off + 1, 0x21, // display inversion off + 2, 0x36,0x68, // memory access 0xc0 for 180 degree flipped + 2, 0x3a,0x55, // pixel format; 5=RGB565 + 3, 0x37,0x00,0x00, // + 6, 0xb2,0x0c,0x0c,0x00,0x33,0x33, // Porch control + 2, 0xb7,0x35, // gate control + 2, 0xbb,0x1a, // VCOM + 2, 0xc0,0x2c, // LCM + 2, 0xc2,0x01, // VDV & VRH command enable + 2, 0xc3,0x0b, // VRH set + 2, 0xc4,0x20, // VDV set + 2, 0xc6,0x0f, // FR control 2 + 3, 0xd0, 0xa4, 0xa1, // Power control 1 + 15, 0xe0, 0x00,0x19,0x1e,0x0a,0x09,0x15,0x3d,0x44,0x51,0x12,0x03, + 0x00,0x3f,0x3f, // gamma 1 + 15, 0xe1, 0x00,0x18,0x1e,0x0a,0x09,0x25,0x3f,0x43,0x52,0x33,0x03, + 0x00,0x3f,0x3f, // gamma 2 + 1, 0x29, // display on + 0 +}; + +const uint8_t uc80InitList[] = { + 2, 0x3a, 0x05, // pixel format RGB565 + 2, 0x36, 0x68, // MADCTL (0/90/180/270 and color/inversion) + 17, 0xe0, 0x09, 0x16, 0x09,0x20, + 0x21,0x1b,0x13,0x19, + 0x17,0x15,0x1e,0x2b, + 0x04,0x05,0x02,0x0e, // gamma sequence + 17, 0xe1, 0x0b,0x14,0x08,0x1e, + 0x22,0x1d,0x18,0x1e, + 0x1b,0x1a,0x24,0x2b, + 0x06,0x06,0x02,0x0f, + 1, 0x20, // display inversion off + 1, 0x29, // display on + 0 +}; + +const uint8_t uc160InitList[] = { + 2, 0x3a, 0x05, // pixel format RGB565 + 2, 0x36, 0x60, // MADCTL + 17, 0xe0, 0x09, 0x16, 0x09,0x20, + 0x21,0x1b,0x13,0x19, + 0x17,0x15,0x1e,0x2b, + 0x04,0x05,0x02,0x0e, // gamma sequence + 17, 0xe1, 0x0b,0x14,0x08,0x1e, + 0x22,0x1d,0x18,0x1e, + 0x1b,0x1a,0x24,0x2b, + 0x06,0x06,0x02,0x0f, + 1, 0x20, // display inversion off + 1, 0x29, // display on + 0 +}; +const uint8_t uc128InitList[] = { + 2, 0x3a, 0x05, // pixel format RGB565 + 2, 0x36, 0x68, // MADCTL + 17, 0xe0, 0x09, 0x16, 0x09,0x20, + 0x21,0x1b,0x13,0x19, + 0x17,0x15,0x1e,0x2b, + 0x04,0x05,0x02,0x0e, // gamma sequence + 17, 0xe1, 0x0b,0x14,0x08,0x1e, + 0x22,0x1d,0x18,0x1e, + 0x1b,0x1a,0x24,0x2b, + 0x06,0x06,0x02,0x0f, + 1, 0x20, // display inversion off + 1, 0x29, // display on + 0 +}; +const uint8_t ucFont[]PROGMEM = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x5f,0x5f,0x06,0x00, + 0x00,0x07,0x07,0x00,0x07,0x07,0x00,0x14,0x7f,0x7f,0x14,0x7f,0x7f,0x14, + 0x24,0x2e,0x2a,0x6b,0x6b,0x3a,0x12,0x46,0x66,0x30,0x18,0x0c,0x66,0x62, + 0x30,0x7a,0x4f,0x5d,0x37,0x7a,0x48,0x00,0x04,0x07,0x03,0x00,0x00,0x00, + 0x00,0x1c,0x3e,0x63,0x41,0x00,0x00,0x00,0x41,0x63,0x3e,0x1c,0x00,0x00, + 0x08,0x2a,0x3e,0x1c,0x3e,0x2a,0x08,0x00,0x08,0x08,0x3e,0x3e,0x08,0x08, + 0x00,0x00,0x80,0xe0,0x60,0x00,0x00,0x00,0x08,0x08,0x08,0x08,0x08,0x08, + 0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x30,0x18,0x0c,0x06,0x03,0x01, + 0x3e,0x7f,0x59,0x4d,0x47,0x7f,0x3e,0x40,0x42,0x7f,0x7f,0x40,0x40,0x00, + 0x62,0x73,0x59,0x49,0x6f,0x66,0x00,0x22,0x63,0x49,0x49,0x7f,0x36,0x00, + 0x18,0x1c,0x16,0x53,0x7f,0x7f,0x50,0x27,0x67,0x45,0x45,0x7d,0x39,0x00, + 0x3c,0x7e,0x4b,0x49,0x79,0x30,0x00,0x03,0x03,0x71,0x79,0x0f,0x07,0x00, + 0x36,0x7f,0x49,0x49,0x7f,0x36,0x00,0x06,0x4f,0x49,0x69,0x3f,0x1e,0x00, + 0x00,0x00,0x00,0x66,0x66,0x00,0x00,0x00,0x00,0x80,0xe6,0x66,0x00,0x00, + 0x08,0x1c,0x36,0x63,0x41,0x00,0x00,0x00,0x14,0x14,0x14,0x14,0x14,0x14, + 0x00,0x41,0x63,0x36,0x1c,0x08,0x00,0x00,0x02,0x03,0x59,0x5d,0x07,0x02, + 0x3e,0x7f,0x41,0x5d,0x5d,0x5f,0x0e,0x7c,0x7e,0x13,0x13,0x7e,0x7c,0x00, + 0x41,0x7f,0x7f,0x49,0x49,0x7f,0x36,0x1c,0x3e,0x63,0x41,0x41,0x63,0x22, + 0x41,0x7f,0x7f,0x41,0x63,0x3e,0x1c,0x41,0x7f,0x7f,0x49,0x5d,0x41,0x63, + 0x41,0x7f,0x7f,0x49,0x1d,0x01,0x03,0x1c,0x3e,0x63,0x41,0x51,0x33,0x72, + 0x7f,0x7f,0x08,0x08,0x7f,0x7f,0x00,0x00,0x41,0x7f,0x7f,0x41,0x00,0x00, + 0x30,0x70,0x40,0x41,0x7f,0x3f,0x01,0x41,0x7f,0x7f,0x08,0x1c,0x77,0x63, + 0x41,0x7f,0x7f,0x41,0x40,0x60,0x70,0x7f,0x7f,0x0e,0x1c,0x0e,0x7f,0x7f, + 0x7f,0x7f,0x06,0x0c,0x18,0x7f,0x7f,0x1c,0x3e,0x63,0x41,0x63,0x3e,0x1c, + 0x41,0x7f,0x7f,0x49,0x09,0x0f,0x06,0x1e,0x3f,0x21,0x31,0x61,0x7f,0x5e, + 0x41,0x7f,0x7f,0x09,0x19,0x7f,0x66,0x26,0x6f,0x4d,0x49,0x59,0x73,0x32, + 0x03,0x41,0x7f,0x7f,0x41,0x03,0x00,0x7f,0x7f,0x40,0x40,0x7f,0x7f,0x00, + 0x1f,0x3f,0x60,0x60,0x3f,0x1f,0x00,0x3f,0x7f,0x60,0x30,0x60,0x7f,0x3f, + 0x63,0x77,0x1c,0x08,0x1c,0x77,0x63,0x07,0x4f,0x78,0x78,0x4f,0x07,0x00, + 0x47,0x63,0x71,0x59,0x4d,0x67,0x73,0x00,0x7f,0x7f,0x41,0x41,0x00,0x00, + 0x01,0x03,0x06,0x0c,0x18,0x30,0x60,0x00,0x41,0x41,0x7f,0x7f,0x00,0x00, + 0x08,0x0c,0x06,0x03,0x06,0x0c,0x08,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x00,0x00,0x03,0x07,0x04,0x00,0x00,0x20,0x74,0x54,0x54,0x3c,0x78,0x40, + 0x41,0x7f,0x3f,0x48,0x48,0x78,0x30,0x38,0x7c,0x44,0x44,0x6c,0x28,0x00, + 0x30,0x78,0x48,0x49,0x3f,0x7f,0x40,0x38,0x7c,0x54,0x54,0x5c,0x18,0x00, + 0x48,0x7e,0x7f,0x49,0x03,0x06,0x00,0x98,0xbc,0xa4,0xa4,0xf8,0x7c,0x04, + 0x41,0x7f,0x7f,0x08,0x04,0x7c,0x78,0x00,0x44,0x7d,0x7d,0x40,0x00,0x00, + 0x60,0xe0,0x80,0x84,0xfd,0x7d,0x00,0x41,0x7f,0x7f,0x10,0x38,0x6c,0x44, + 0x00,0x41,0x7f,0x7f,0x40,0x00,0x00,0x7c,0x7c,0x18,0x78,0x1c,0x7c,0x78, + 0x7c,0x78,0x04,0x04,0x7c,0x78,0x00,0x38,0x7c,0x44,0x44,0x7c,0x38,0x00, + 0x84,0xfc,0xf8,0xa4,0x24,0x3c,0x18,0x18,0x3c,0x24,0xa4,0xf8,0xfc,0x84, + 0x44,0x7c,0x78,0x4c,0x04,0x0c,0x18,0x48,0x5c,0x54,0x74,0x64,0x24,0x00, + 0x04,0x04,0x3e,0x7f,0x44,0x24,0x00,0x3c,0x7c,0x40,0x40,0x3c,0x7c,0x40, + 0x1c,0x3c,0x60,0x60,0x3c,0x1c,0x00,0x3c,0x7c,0x60,0x30,0x60,0x7c,0x3c, + 0x44,0x6c,0x38,0x10,0x38,0x6c,0x44,0x9c,0xbc,0xa0,0xa0,0xfc,0x7c,0x00, + 0x4c,0x64,0x74,0x5c,0x4c,0x64,0x00,0x08,0x08,0x3e,0x77,0x41,0x41,0x00, + 0x00,0x00,0x00,0x77,0x77,0x00,0x00,0x41,0x41,0x77,0x3e,0x08,0x08,0x00, + 0x02,0x03,0x01,0x03,0x02,0x03,0x01,0x70,0x78,0x4c,0x46,0x4c,0x78,0x70}; + // 5x7 font (in 6x8 cell) +const uint8_t ucSmallFont[] PROGMEM = { +0x00,0x00,0x00,0x00,0x00, +0x00,0x06,0x5f,0x06,0x00, +0x07,0x03,0x00,0x07,0x03, +0x24,0x7e,0x24,0x7e,0x24, +0x24,0x2b,0x6a,0x12,0x00, +0x63,0x13,0x08,0x64,0x63, +0x36,0x49,0x56,0x20,0x50, +0x00,0x07,0x03,0x00,0x00, +0x00,0x3e,0x41,0x00,0x00, +0x00,0x41,0x3e,0x00,0x00, +0x08,0x3e,0x1c,0x3e,0x08, +0x08,0x08,0x3e,0x08,0x08, +0x00,0xe0,0x60,0x00,0x00, +0x08,0x08,0x08,0x08,0x08, +0x00,0x60,0x60,0x00,0x00, +0x20,0x10,0x08,0x04,0x02, +0x3e,0x51,0x49,0x45,0x3e, +0x00,0x42,0x7f,0x40,0x00, +0x62,0x51,0x49,0x49,0x46, +0x22,0x49,0x49,0x49,0x36, +0x18,0x14,0x12,0x7f,0x10, +0x2f,0x49,0x49,0x49,0x31, +0x3c,0x4a,0x49,0x49,0x30, +0x01,0x71,0x09,0x05,0x03, +0x36,0x49,0x49,0x49,0x36, +0x06,0x49,0x49,0x29,0x1e, +0x00,0x6c,0x6c,0x00,0x00, +0x00,0xec,0x6c,0x00,0x00, +0x08,0x14,0x22,0x41,0x00, +0x24,0x24,0x24,0x24,0x24, +0x00,0x41,0x22,0x14,0x08, +0x02,0x01,0x59,0x09,0x06, +0x3e,0x41,0x5d,0x55,0x1e, +0x7e,0x11,0x11,0x11,0x7e, +0x7f,0x49,0x49,0x49,0x36, +0x3e,0x41,0x41,0x41,0x22, +0x7f,0x41,0x41,0x41,0x3e, +0x7f,0x49,0x49,0x49,0x41, +0x7f,0x09,0x09,0x09,0x01, +0x3e,0x41,0x49,0x49,0x7a, +0x7f,0x08,0x08,0x08,0x7f, +0x00,0x41,0x7f,0x41,0x00, +0x30,0x40,0x40,0x40,0x3f, +0x7f,0x08,0x14,0x22,0x41, +0x7f,0x40,0x40,0x40,0x40, +0x7f,0x02,0x04,0x02,0x7f, +0x7f,0x02,0x04,0x08,0x7f, +0x3e,0x41,0x41,0x41,0x3e, +0x7f,0x09,0x09,0x09,0x06, +0x3e,0x41,0x51,0x21,0x5e, +0x7f,0x09,0x09,0x19,0x66, +0x26,0x49,0x49,0x49,0x32, +0x01,0x01,0x7f,0x01,0x01, +0x3f,0x40,0x40,0x40,0x3f, +0x1f,0x20,0x40,0x20,0x1f, +0x3f,0x40,0x3c,0x40,0x3f, +0x63,0x14,0x08,0x14,0x63, +0x07,0x08,0x70,0x08,0x07, +0x71,0x49,0x45,0x43,0x00, +0x00,0x7f,0x41,0x41,0x00, +0x02,0x04,0x08,0x10,0x20, +0x00,0x41,0x41,0x7f,0x00, +0x04,0x02,0x01,0x02,0x04, +0x80,0x80,0x80,0x80,0x80, +0x00,0x03,0x07,0x00,0x00, +0x20,0x54,0x54,0x54,0x78, +0x7f,0x44,0x44,0x44,0x38, +0x38,0x44,0x44,0x44,0x28, +0x38,0x44,0x44,0x44,0x7f, +0x38,0x54,0x54,0x54,0x08, +0x08,0x7e,0x09,0x09,0x00, +0x18,0xa4,0xa4,0xa4,0x7c, +0x7f,0x04,0x04,0x78,0x00, +0x00,0x00,0x7d,0x40,0x00, +0x40,0x80,0x84,0x7d,0x00, +0x7f,0x10,0x28,0x44,0x00, +0x00,0x00,0x7f,0x40,0x00, +0x7c,0x04,0x18,0x04,0x78, +0x7c,0x04,0x04,0x78,0x00, +0x38,0x44,0x44,0x44,0x38, +0xfc,0x44,0x44,0x44,0x38, +0x38,0x44,0x44,0x44,0xfc, +0x44,0x78,0x44,0x04,0x08, +0x08,0x54,0x54,0x54,0x20, +0x04,0x3e,0x44,0x24,0x00, +0x3c,0x40,0x20,0x7c,0x00, +0x1c,0x20,0x40,0x20,0x1c, +0x3c,0x60,0x30,0x60,0x3c, +0x6c,0x10,0x10,0x6c,0x00, +0x9c,0xa0,0x60,0x3c,0x00, +0x64,0x54,0x54,0x4c,0x00, +0x08,0x3e,0x41,0x41,0x00, +0x00,0x00,0x77,0x00,0x00, +0x00,0x41,0x41,0x3e,0x08, +0x02,0x01,0x02,0x01,0x00, +0x3c,0x26,0x23,0x26,0x3c}; + +void DMA1_Channel3_IRQHandler(void) __attribute__((interrupt)); + +// +// This function gets called when the current DMA transaction completes +// We use this to disable the chip select (CS) signal on the LCD +// and clear our volatile flag (DMA busy) +// +void DMA1_Channel3_IRQHandler(void) +{ + // why is this needed? Can't just direct compare the reg in tests below + volatile uint16_t intfr = DMA1->INTFR; + + if (intfr & DMA1_IT_TC3) { + DMA1->INTFCR = DMA1_IT_TC3; + DMA1_Channel3->CFGR &= ~DMA_CFGR1_EN; + if (u8CS != 0xff) + digitalWrite(u8CS, 1); // de-activate CS + bDMA = 0; // no longer active DMA transaction + } +} /* DMA1_Channel3_IRQHandler() */ + +// +// Initialize the DMA channel for SPI transmit (3) +// +void DMA_Tx_Init(DMA_Channel_TypeDef *DMA_CHx, u32 ppadr, u32 memadr, u16 bufsize) +{ + RCC->AHBPCENR |= RCC_AHBPeriph_DMA1; // Enable DMA peripheral + //DMA1_Channel3 is for SPI + DMA1_Channel3->PADDR = ppadr; + DMA1_Channel3->MADDR = memadr; + DMA1_Channel3->CNTR = bufsize; + DMA1_Channel3->CFGR = + DMA_M2M_Disable | + DMA_Priority_VeryHigh | + DMA_MemoryDataSize_Byte | + DMA_PeripheralDataSize_Byte | + DMA_MemoryInc_Enable | + DMA_Mode_Normal | + DMA_DIR_PeripheralDST | + DMA_IT_TC; + + NVIC_EnableIRQ( DMA1_Channel3_IRQn ); + DMA1_Channel3->CFGR |= DMA_CFGR1_EN; +} /* DMA_Tx_Init() */ +// +// Faster way to write a 16-bit pattern into memory +// +void memset16(uint16_t *u16Dest, uint16_t u16Pattern, int iLen) +{ + uint32_t *u32D, u32; + u32 = u16Pattern | (u16Pattern << 16); + if (((uint32_t)u16Dest & 3) == 0) { + u32D = (uint32_t *)u16Dest; + while (iLen >= 2) { + *u32D++ = u32; + iLen -= 2; + } + u16Dest = (uint16_t *)u32D; + } + while (iLen) { + *u16Dest += u16Pattern; + iLen--; + } +} /* memset16() */ + +// +// Write a command byte to the LCD (D/C = LOW) +// +void lcdWriteCMD(uint8_t ucCMD) +{ + while (bDMA) {}; // wait for old transaction to complete + digitalWrite(u8DC, 0); + if (u8CS != 0xff) + digitalWrite(u8CS, 0); + SPI_write(&ucCMD, 1); + if (u8CS != 0xff) + digitalWrite(u8CS, 1); + digitalWrite(u8DC, 1); + +} /* lcdWriteCMD() */ +// +// Write a block of data to the LCD (D/C = high) +// Use DMA if the block is longer than a certain threshold +// +void lcdWriteDATA(uint8_t *pData, int iLen) +{ + uint8_t *p; + while (bDMA) {}; // wait for old transaction to complete + if (iLen >= 32) { // arbitrary cutoff point + if (u8CS != 0xff) + digitalWrite(u8CS, 0); // activate CS + DMA1_Channel3->CNTR = iLen; + DMA1_Channel3->MADDR = (uint32_t)pCache0; + + DMA1_Channel3->CFGR |= DMA_CFGR1_EN; // DMA_Cmd(DMA1_Channel3, ENABLE); // have DMA send the data + bDMA = 1; // tell our code that DMA is currently active for next time + // swap buffers + p = pCache0; + pCache0 = pCache1; + pCache1 = p; + } else { + if (u8CS != 0xff) + digitalWrite(u8CS, 0); + SPI_write(pData, iLen); + if (u8CS != 0xff) + digitalWrite(u8CS, 1); + } +} /* lcdWriteDATA() */ + +// +// Initialize the GPIOs needed to control the LCD +// and send the command sequence to configure the display +// +void lcdInit(int iLCDType, uint32_t u32Speed, uint8_t u8CSPin, uint8_t u8DCPin, uint8_t u8RSTPin, uint8_t u8BLPin) +{ +// uint8_t iBGR = 0; + uint8_t *s = NULL; + int iCount; + + if (iLCDType < 0 || iLCDType >= LCD_COUNT) return; + switch (iLCDType) { + case LCD_ST7789_135x240: + iNativeWidth = iLCDWidth = 240; // initialize in landscape mode + iNativeHeight = iLCDHeight = 135; + iNativeXOff = iLCDXOff = 40; + iNativeYOff = iLCDYOff = 53; + u8MADCTL = 0x60; + s = (unsigned char *)uc240x240InitList; + break; + case LCD_ST7789_172x320: + iNativeWidth = iLCDWidth = 320; // initialize in landscape mode + iNativeHeight = iLCDHeight = 172; + iNativeXOff = iLCDXOff = 0; + iNativeYOff = iLCDYOff = 34; + u8MADCTL = 0x60; + s = (unsigned char *)uc240x240InitList; + break; + case LCD_ST7789_240x280: + iNativeWidth = iLCDWidth = 280; // initialize in landscape mode + iNativeHeight = iLCDHeight = 240; + iNativeXOff = iLCDXOff = 20; + iNativeYOff = iLCDYOff = 0; + u8MADCTL = 0x60; + s = (unsigned char *)uc240x240InitList; + break; + case LCD_ST7735_80x160: + iNativeWidth = iLCDWidth = 160; // initialize in landscape mode + iNativeHeight = iLCDHeight = 80; + iNativeXOff = iLCDXOff = 0; + iNativeYOff = iLCDYOff = 24; + u8MADCTL = 0x68; + s = (unsigned char *)uc80InitList; + break; + case LCD_ILI9341_240x320: + iNativeWidth = iLCDWidth = 240; + iNativeHeight = iLCDHeight = 320; + iNativeXOff = iLCDXOff = 0; + iNativeYOff = iLCDYOff = 0; + u8MADCTL = 0x60; + s = (unsigned char *)ucILI9341InitList; + break; + case LCD_GC9107_128x128: + iNativeWidth = iLCDWidth = 128; + iNativeHeight = iLCDHeight = 128; + iNativeXOff = iLCDXOff = 1; + iNativeYOff = iLCDYOff = 2; + u8MADCTL = 0x68; + s = (unsigned char *)ucGC9107InitList; + break; + case LCD_ST7735_128x128: + iNativeWidth = iLCDWidth = 128; + iNativeHeight = iLCDHeight = 128; + iNativeXOff = iLCDXOff = 1; + iNativeYOff = iLCDYOff = 0; + u8MADCTL = 0x68; + s = (unsigned char *)uc128InitList; + break; + case LCD_ST7735_128x160: + iNativeWidth = iLCDWidth = 160; + iNativeHeight = iLCDHeight = 128; + iNativeXOff = iLCDXOff = 0; + iNativeYOff = iLCDYOff = 0; + u8MADCTL = 0x60; + s = (unsigned char *)uc160InitList; + break; + } // switch on LCD type + iLCDPitch = iLCDWidth*2; + u8CS = u8CSPin; + if (u8CSPin != 0xff) { + pinMode(u8CSPin, OUTPUT); + digitalWrite(u8CSPin, 1); + } + pinMode(u8RSTPin, OUTPUT); + digitalWrite(u8RSTPin, 0); // reset the display controller + Delay_Ms(100); + digitalWrite(u8RSTPin, 1); + Delay_Ms(200); + + SPI_begin(u32Speed, 0); + u8DC = u8DCPin; + pinMode(u8DCPin, OUTPUT); + u8BL = u8BLPin; + pinMode(u8BL, OUTPUT); + digitalWrite(u8BL, 1); // turn on backlight +// if (pLCD->iLCDFlags & FLAGS_SWAP_RB) +// iBGR = 8; + lcdWriteCMD(0x01); // SW reset + Delay_Ms(200); + lcdWriteCMD(0x11); // sleep out + Delay_Ms(100); + iCount = 1; + while (s && iCount) + { + iCount = *s++; + if (iCount != 0) + { + lcdWriteCMD(s[0]); + lcdWriteDATA(&s[1], iCount-1); + s += iCount; + } // if count + }// while + DMA_Tx_Init(DMA1_Channel3, (u32)&SPI1->DATAR, (u32)pCache0, 0); + +} /* lcdInit() */ + +// +// Sitronix LCDs support 4 possible orientations through use of the +// X, Y and V mirror/flip bits +// V swaps the X/Y axis +// +void lcdOrientation(int iOrientation) +{ + uint8_t u8 = u8MADCTL; // original value + + switch (iOrientation) { + case ORIENTATION_0: // use original MADCTL value + iLCDWidth = iNativeWidth; + iLCDHeight = iNativeHeight; + iLCDPitch = iLCDWidth * 2; + iLCDXOff = iNativeXOff; + iLCDYOff = iNativeYOff; + break; + case ORIENTATION_90: + u8 ^= MADCTL_XFLIP; + u8 ^= MADCTL_VFLIP; + iLCDWidth = iNativeHeight; + iLCDHeight = iNativeWidth; + iLCDPitch = iLCDWidth * 2; + iLCDXOff = iNativeYOff; + iLCDYOff = iNativeXOff; + break; + case ORIENTATION_180: + u8 ^= MADCTL_XFLIP; + u8 ^= MADCTL_YFLIP; + iLCDWidth = iNativeWidth; + iLCDHeight = iNativeHeight; + iLCDPitch = iLCDWidth * 2; + iLCDXOff = iNativeXOff; + iLCDYOff = iNativeYOff; + break; + case ORIENTATION_270: + u8 ^= MADCTL_YFLIP; + u8 ^= MADCTL_VFLIP; + iLCDWidth = iNativeHeight; + iLCDHeight = iNativeWidth; + iLCDPitch = iLCDWidth * 2; + iLCDXOff = iNativeYOff; + iLCDYOff = iNativeXOff; + break; + } + lcdWriteCMD(0x36); // MADCTL + lcdWriteDATA(&u8, 1); +} /* lcdOrientation() */ + +// +// Set the memory window (AKA write position) +// +void lcdSetPosition(int x, int y, int w, int h) +{ +uint8_t ucBuf[8]; + + x += iLCDXOff; + y += iLCDYOff; + ucBuf[0] = (unsigned char)(x >> 8); + ucBuf[1] = (unsigned char)x; + x = x + w - 1; + ucBuf[2] = (unsigned char)(x >> 8); + ucBuf[3] = (unsigned char)x; + lcdWriteCMD(0x2a); // column address cmd + lcdWriteDATA(ucBuf, 4); + ucBuf[0] = (unsigned char)(y >> 8); + ucBuf[1] = (unsigned char)y; + y = y + h - 1; + ucBuf[2] = (unsigned char)(y >> 8); + ucBuf[3] = (unsigned char)y; + lcdWriteCMD(0x2b); // row address cmd + lcdWriteDATA(ucBuf, 4); + lcdWriteCMD(0x2c); // RAMWR - start writing +} /* lcdSetPosition() */ + +// +// For drawing ellipses, a circle is drawn and the x and y pixels are scaled by a 16-bit integer fraction +// This function draws a single pixel and scales its position based on the x/y fraction of the ellipse +// +void DrawScaledPixel(int32_t iCX, int32_t iCY, int32_t x, int32_t y, int32_t iXFrac, int32_t iYFrac, uint16_t usColor) +{ + if (iXFrac != 0x10000) x = (x * iXFrac) >> 16; + if (iYFrac != 0x10000) y = (y * iYFrac) >> 16; + x += iCX; y += iCY; + if (x < 0 || x >= iLCDWidth || y < 0 || y >= iLCDHeight) + return; // off the screen + lcdSetPosition(x, y, 1, 1); + lcdWriteDATA((uint8_t *)&usColor, 2); +} /* DrawScaledPixel() */ +// +// Draw the given x/y symmetrically across the center point +// as a continuous line +// +void DrawScaledLine(int32_t iCX, int32_t iCY, int32_t x, int32_t y, int32_t iXFrac, int32_t iYFrac, uint16_t usColor) +{ + int32_t iLen, x2; + if (iXFrac != 0x10000) x = (x * iXFrac) >> 16; + if (iYFrac != 0x10000) y = (y * iYFrac) >> 16; + x2 = iCX + x; + x = iCX - x; + y += iCY; + if (x < 0) x = 0; + if (x2 >= iLCDWidth) x2 = iLCDWidth-1; + iLen = x2 - x + 1; // new length + lcdSetPosition(x, y, iLen, 1); + memset16((uint16_t *)pCache0, usColor, iLen); + lcdWriteDATA(pCache0, iLen*2); +} /* DrawScaledLine() */ +// +// Draw the 8 pixels around the Bresenham circle +// (scaled to make an ellipse) +// +void BresenhamCircle(int32_t iCX, int32_t iCY, int32_t x, int32_t y, int32_t iXFrac, int32_t iYFrac, uint16_t usColor, int bFill) +{ + if (bFill) // draw a filled ellipse + { + // for a filled ellipse, draw 4 lines instead of 8 pixels + DrawScaledLine(iCX, iCY, y, x, iXFrac, iYFrac, usColor); + DrawScaledLine(iCX, iCY, y, -x, iXFrac, iYFrac, usColor); + DrawScaledLine(iCX, iCY, x, y, iXFrac, iYFrac, usColor); + DrawScaledLine(iCX, iCY, x, -y, iXFrac, iYFrac, usColor); + } + else // draw 8 pixels around the edges + { + DrawScaledPixel(iCX, iCY, x, y, iXFrac, iYFrac, usColor); + DrawScaledPixel(iCX, iCY, -x, y, iXFrac, iYFrac, usColor); + DrawScaledPixel(iCX, iCY, x, -y, iXFrac, iYFrac, usColor); + DrawScaledPixel(iCX, iCY, -x, -y, iXFrac, iYFrac, usColor); + DrawScaledPixel(iCX, iCY, y, x, iXFrac, iYFrac, usColor); + DrawScaledPixel(iCX, iCY, -y, x, iXFrac, iYFrac, usColor); + DrawScaledPixel(iCX, iCY, y, -x, iXFrac, iYFrac, usColor); + DrawScaledPixel(iCX, iCY, -y, -x, iXFrac, iYFrac, usColor); + } +} /* BresenhamCircle() */ +// +// Draw an ellipse (a circle if Rx==Ry) +// +void lcdEllipse(int iCenterX, int iCenterY, int iRadiusX, int iRadiusY, uint16_t usColor, int bFill) +{ + int32_t iRadius, iXFrac, iYFrac; + int32_t iDelta, x, y; + + if (iRadiusX > iRadiusY) // use X as the primary radius + { + iRadius = iRadiusX; + iXFrac = 65536; + iYFrac = (iRadiusY * 65536) / iRadiusX; + } + else + { + iRadius = iRadiusY; + iXFrac = (iRadiusX * 65536) / iRadiusY; + iYFrac = 65536; + } + usColor = __builtin_bswap16(usColor); // swap byte order + iDelta = 3 - (2 * iRadius); + x = 0; y = iRadius; + while (x < y) + { + BresenhamCircle(iCenterX, iCenterY, x, y, iXFrac, iYFrac, usColor, bFill); + x++; + if (iDelta < 0) + { + iDelta += (4*x) + 6; + } + else + { + iDelta += 4 * (x-y) + 10; + y--; + } + } +} /* lcdEllipse() */ +// +// Draw an outline or filled rectangle +// requesting a rectangle which draws past an edge will result it in being clipped to the edge +// +void lcdRectangle(int x, int y, int cx, int cy, uint16_t usColor, int bFill) +{ + int ty; + if (x >= iLCDWidth || y >= iLCDHeight) return; // not visible + if (x < 0) { + cx += x; + x = 0; + } else if (x + cx > iLCDWidth) cx = iLCDWidth - cx; + if (y < 0) { + cy += y; + y = 0; + } else if (y + cy > iLCDHeight) cy = iLCDHeight - cy; + usColor = __builtin_bswap16(usColor); + if (bFill) { // draw a filled rectangle + lcdSetPosition(x, y, cx, cy); + for (ty = 0; ty < cy; ty++) { + if (ty < 2) memset16((uint16_t *)pCache0, usColor, cx); // need to do it 2 times for buffers + lcdWriteDATA(pCache0, cx*2); + } // for ty + } else { // outline rectangle + // each side is drawn as a straight line + lcdSetPosition(x, y, cx, 1); // top line + memset16((uint16_t *)pCache0, usColor, cx); + lcdWriteDATA(pCache0, cx*2); + lcdSetPosition(x, y+cy-1, cx, 1); // bottom line + memset16((uint16_t *)pCache0, usColor, cx); + lcdWriteDATA(pCache0, cx*2); + lcdSetPosition(x, y, 1, cy); // left line + memset16((uint16_t *)pCache0, usColor, cy); + lcdWriteDATA(pCache0, cy*2); + lcdSetPosition(x+cx-1, y, 1, cy); // right line + memset16((uint16_t *)pCache0, usColor, cy); + lcdWriteDATA(pCache0, cy*2); + } +} /* lcdRectangle() */ +// +// Draw a NxN RGB565 tile +// This reverses the pixel byte order and sets a memory "window" +// of pixels so that the write can occur in one shot +// +int lcdDrawTile(int x, int y, int iTileWidth, int iTileHeight, unsigned char *pTile, int iPitch) +{ + int i, j; + uint16_t *s16, *d16; + + if (iTileWidth*iTileHeight*2 > CACHE_SIZE) { + return -1; // tile must fit in SPI cache + } + // First convert to big-endian order + d16 = (uint16_t *)pCache0; + for (j=0; j<iTileHeight; j++) + { + s16 = (uint16_t*)&pTile[j*iPitch]; + for (i=0; i<iTileWidth; i++) + { + *d16++ = __builtin_bswap16(*s16++); + } // for i; + } // for j + lcdSetPosition(x, y, iTileWidth, iTileHeight); + lcdWriteDATA(pCache0, iTileWidth*iTileHeight*2); + return 0; +} /* lcdDrawTile() */ +// +// Fill the display with a solid color +// +void lcdFill(uint16_t usData) +{ + int cx, cy; + uint16_t *d; + + usData = (usData >> 8) | (usData << 8); // swap hi/lo byte for LCD + lcdSetPosition(0,0, iLCDWidth, iLCDHeight); + // fit within our temp buffer + for (cy = 0; cy < iLCDHeight; cy++) { + if (cy < 2) { // both buffers need a copy + d = (uint16_t *)pCache0; // pointer swapped after each write + for (cx = 0; cx < iLCDWidth; cx++) { + d[cx] = usData; + } + } + lcdWriteDATA(pCache0, iLCDWidth*2); // fill with data words + } // for y + +} /* lcdFill() */ + +// +// Draw a 1-bpp pattern with the given color and translucency +// 1 bits are drawn as color, 0 are transparent +// The translucency value can range from 1 (barely visible) to 32 (fully opaque) +// If there is a backbuffer, the bitmap is draw only into memory +// If there is no backbuffer, the bitmap is drawn on the screen with a black background color +// +void spilcdDrawPattern(uint8_t *pPattern, int iSrcPitch, int iDestX, int iDestY, int iCX, int iCY, uint16_t usColor) +{ + int x, y; + uint8_t *s, uc, ucMask; + uint16_t *d, u16Clr; + + if (iDestX+iCX > iLCDWidth) // trim to fit on display + iCX = (iLCDWidth - iDestX); + if (iDestY+iCY > iLCDHeight) + iCY = (iLCDHeight - iDestY); + if (pPattern == NULL || iDestX < 0 || iDestY < 0 || iCX <=0 || iCY <= 0) + return; + u16Clr = (usColor >> 8) | (usColor << 8); // swap low/high bytes + lcdSetPosition(iDestX, iDestY, iCX, iCY); + for (y=0; y<iCY; y++) + { + s = &pPattern[y * iSrcPitch]; + ucMask = uc = 0; + d = (uint16_t *)pCache0; + for (x=0; x<iCX; x++) + { + ucMask >>= 1; + if (ucMask == 0) + { + ucMask = 0x80; + uc = *s++; + } + if (uc & ucMask) // active pixel + *d++ = u16Clr; + else + *d++ = 0; + } // for x + lcdWriteDATA(pCache0, iCX*2); + } // for y +} /* spilcdDrawPattern() */ + +// +// Draw a string of text with the built-in fonts +// +#if (CACHE_SIZE < 4096) +// This slower version uses one DMA transaction per character +int lcdWriteString(int x, int y, char *szMsg, uint16_t usFGColor, uint16_t usBGColor, int iFontSize) +{ +int i, j, k, iLen; +unsigned char *s; +unsigned short usFG = (usFGColor >> 8) | (usFGColor << 8); +unsigned short usBG = (usBGColor >> 8) | (usBGColor << 8); + + if (x == -1) + x = iCursorX; + if (y == -1) + y = iCursorY; + if (x < 0 || y < 0) return -1; + iLen = strlen(szMsg); + if (iFontSize == FONT_8x8 || iFontSize == FONT_6x8) // draw the 6x8 or 8x8 font + { + uint16_t *usD; + int cx; + uint8_t *pFont; + + cx = (iFontSize == FONT_8x8) ? 8:6; + pFont = (iFontSize == FONT_8x8) ? (uint8_t *)ucFont : (uint8_t *)ucSmallFont; + if ((cx*iLen) + x > iLCDWidth) iLen = (iLCDWidth - x)/cx; // can't display it all + if (iLen < 0)return -1; + + for (i=0; i<iLen; i++) + { + s = &pFont[((unsigned char)szMsg[i]-32) * (cx-1)]; + usD = (uint16_t *)pCache0; + lcdSetPosition(x+(i*cx), y, cx, 8); + uint8_t ucMask = 1; + for (k=0; k<8; k++) // for each scanline + { + for (j=0; j<cx-1; j++) + { + if (s[j] & ucMask) + *usD++ = usFG; + else + *usD++ = usBG; + } // for j + *usD++ = usBG; // last column is blank + ucMask <<= 1; + } // for k + // write the data in one shot + lcdWriteDATA(pCache0, cx*2*8); + } // for each character + x += (i*cx); + } // 6x8 and 8x8 + if (iFontSize == FONT_12x16) // 6x8 stretched to 12x16 (with smoothing) + { + uint16_t *usD; + + if ((12*iLen) + x > iLCDWidth) iLen = (iLCDWidth - x)/12; // can't display it all + if (iLen < 0) return -1; + + for (i=0; i<iLen; i++) + { + s = (uint8_t *)&ucSmallFont[((unsigned char)szMsg[i]-32) * 5]; + usD = (uint16_t *)pCache0;; + lcdSetPosition(x+(i*12), y, 12, 16); + uint8_t ucMask = 1; + for (k=0; k<12*16; k++) + usD[k] = usBG; + for (k=0; k<8; k++) // for each scanline + { + uint8_t c0, c1; + for (j=0; j<5; j++) + { + c0 = s[j]; + if (c0 & ucMask) + usD[0] = usD[1] = usD[12] = usD[13] = usFG; + // test for smoothing diagonals + if (k < 7 && j < 5) { + uint8_t ucMask2 = ucMask << 1; + c1 = s[j+1]; + if ((c0 & ucMask) && (~c1 & ucMask) && (~c0 & ucMask2) && (c1 & ucMask2)) // first diagonal condition + usD[14] = usD[25] = usFG; + else if ((~c0 & ucMask) && (c1 & ucMask) && (c0 & ucMask2) && (~c1 & ucMask2)) + usD[13] = usD[26] = usFG; + } // if not on last row and last col + usD+=2; + } // for j + usD[0] = usD[1] = usD[12] = usD[13] = usBG; // last column is blank + usD += 2; + usD += 12; // skip the extra line + ucMask <<= 1; + } // for k + // write the data in one shot + lcdWriteDATA(pCache0, 12*16*2); + } // for each character + x += i*12; + } // FONT_12x16 + iCursorX = x; + iCursorY = y; + return 0; +} /* lcdWriteString() */ +#else +// This faster version uses one DMA transaction per line of text +int lcdWriteString(int x, int y, char *szMsg, uint16_t usFGColor, uint16_t usBGColor, int iFontSize) +{ +int i, j, k, iLen; +int iStride; +uint8_t *s; +uint16_t usFG = (usFGColor >> 8) | ((usFGColor & -1)<< 8); +uint16_t usBG = (usBGColor >> 8) | ((usBGColor & -1)<< 8); +uint16_t *usD; +int cx; +uint8_t *pFont; + + if (iFontSize < 0 || iFontSize >= FONT_COUNT) + return -1; // invalid size + if (x == -1) + x = iCursorX; + if (y == -1) + y = iCursorY; + if (x < 0) return -1; + iLen = strlen(szMsg); + + if (iFontSize == FONT_12x16) { + if ((12*iLen) + x > iLCDWidth) iLen = (iLCDWidth - x)/12; // can't display it all + if (iLen < 0) return -1; + iStride = iLen*12; + lcdSetPosition(x, y, iStride, 16); + usD = (uint16_t *)pCache0; + for (i=0; i<iStride*16; i++) + usD[i] = usBG; // set to background color first + for (k = 0; k<8; k++) { // create a pair of scanlines from each original + uint8_t ucMask = (1 << k); + usD = (unsigned short *)&pCache0[k*iStride*4]; + for (i=0; i<iLen; i++) + { + uint8_t c0, c1; + s = (uint8_t *)&ucSmallFont[((unsigned char)szMsg[i]-32) * 5]; + for (j=1; j<6; j++) + { + uint8_t ucMask1 = ucMask << 1; + uint8_t ucMask2 = ucMask >> 1; + c0 = s[j-1]; + if (c0 & ucMask) + usD[0] = usD[1] = usD[iStride] = usD[iStride+1] = usFG; + // test for smoothing diagonals + if (j < 5) { + c1 = s[j]; + if ((c0 & ucMask) && (~c1 & ucMask) && (~c0 & ucMask1) && (c1 & ucMask1)) { // first diagonal condition + usD[iStride+2] = usFG; + } else if ((~c0 & ucMask) && (c1 & ucMask) && (c0 & ucMask1) && (~c1 & ucMask1)) { // second condition + usD[iStride+1] = usFG; + } + if ((c0 & ucMask2) && (~c1 & ucMask2) && (~c0 & ucMask) && (c1 & ucMask)) { // repeat for previous line + usD[1] = usFG; + } else if ((~c0 & ucMask2) && (c1 & ucMask2) && (c0 & ucMask) && (~c1 & ucMask)) { + usD[2] = usFG; + } + } + usD+=2; + } // for j + usD += 2; // leave "6th" column blank + } // for each character + } // for each scanline + lcdWriteDATA(pCache0, iStride*32); + return 0; + } // 12x16 + + cx = (iFontSize == FONT_8x8) ? 8:6; + pFont = (iFontSize == FONT_8x8) ? (uint8_t *)ucFont : (uint8_t *)ucSmallFont; + if ((cx*iLen) + x > iLCDWidth) iLen = (iLCDWidth - x)/cx; // can't display it all + iStride = iLen * cx*2; + for (i=0; i<iLen; i++) + { + s = &pFont[((unsigned char)szMsg[i]-32) * (cx-1)]; + uint8_t ucMask = 1; + for (k=0; k<8; k++) // for each scanline + { + usD = (unsigned short *)&pCache0[(k*iStride) + (i * cx*2)]; + for (j=0; j<cx-1; j++) + { + if (s[j] & ucMask) + *usD++ = usFG; + else + *usD++ = usBG; + } // for j + *usD++ = usBG; // blank column + ucMask <<= 1; + } // for k + } // for i + // write the data in one shot + lcdSetPosition(x, y, cx*iLen, 8); + lcdWriteDATA(pCache0, iLen*cx*16); + iCursorX = x + (cx*iLen); + iCursorY = y; + return 0; +} /* lcdWriteString() */ +#endif // cache size +// +// Draw a string in a proportional font you supply +// +int lcdWriteStringCustom(GFXfont *pFont, int x, int y, char *szMsg, uint16_t usFGColor, uint16_t usBGColor, int bBlank) +{ +int i, /*j, iLen, */ k, dx, dy, cx, cy, c, iBitOff; +int tx, ty; +uint8_t *s, bits, uc; +GFXfont font; +GFXglyph glyph, *pGlyph; +#define TEMP_BUF_SIZE 64 +#define TEMP_HIGHWATER (TEMP_BUF_SIZE-8) +uint16_t *d; + + if (pFont == NULL) + return -1; + if (x == -1) + x = iCursorX; + if (y == -1) + y = iCursorY; + if (x < 0) + return -1; + // in case of running on AVR, get copy of data from FLASH + memcpy(&font, pFont, sizeof(font)); + pGlyph = &glyph; + usFGColor = (usFGColor >> 8) | (usFGColor << 8); // swap h/l bytes + usBGColor = (usBGColor >> 8) | (usBGColor << 8); + + i = 0; + while (szMsg[i] && x < iLCDWidth) + { + c = szMsg[i++]; + if (c < font.first || c > font.last) // undefined character + continue; // skip it + c -= font.first; // first char of font defined + memcpy_P(&glyph, &font.glyph[c], sizeof(glyph)); + // set up the destination window (rectangle) on the display + dx = x + pGlyph->xOffset; // offset from character UL to start drawing + dy = y + pGlyph->yOffset; + cx = pGlyph->width; + cy = pGlyph->height; + iBitOff = 0; // bitmap offset (in bits) + if (dy + cy > iLCDHeight) + cy = iLCDHeight - dy; // clip bottom edge + else if (dy < 0) { + cy += dy; + iBitOff += (pGlyph->width * (-dy)); + dy = 0; + } + if (dx + cx > iLCDWidth) + cx = iLCDWidth - dx; // clip right edge + s = font.bitmap + pGlyph->bitmapOffset; // start of bitmap data + // Bitmap drawing loop. Image is MSB first and each pixel is packed next + // to the next (continuing on to the next character line) + bits = uc = 0; // bits left in this font byte + + if (bBlank) { // erase the areas around the char to not leave old bits + int miny, maxy; + c = '0' - font.first; + miny = y + pGlyph->yOffset; + c = 'y' - font.first; + maxy = miny + pGlyph->height; + if (maxy > iLCDHeight) + maxy = iLCDHeight; + cx = pGlyph->xAdvance; + if (cx + x > iLCDWidth) { + cx = iLCDWidth - x; + } + lcdSetPosition(x, miny, cx, maxy-miny); + // blank out area above character +// cy = font.yAdvance - pGlyph->height; +// for (ty=miny; ty<miny+cy && ty < maxy; ty++) { +// for (tx=0; tx<cx; tx++) +// u16Temp[tx] = usBGColor; +// myspiWrite(pLCD, (uint8_t *)u16Temp, cx*sizeof(uint16_t), MODE_DATA, iFlags); +// } // for ty + // character area (with possible padding on L+R) + for (ty=0; ty<pGlyph->height && ty+miny < maxy; ty++) { + d = (uint16_t *)pCache0; + for (tx=0; tx<pGlyph->xOffset && tx < cx; tx++) { // left padding + *d++ = usBGColor; + } + // character bitmap (center area) + for (tx=0; tx<pGlyph->width; tx++) { + if (bits == 0) { // need more data + uc = s[iBitOff>>3]; + bits = 8; + iBitOff += bits; + } + if (tx + pGlyph->xOffset < cx) { + *d++ = (uc & 0x80) ? usFGColor : usBGColor; + } + bits--; + uc <<= 1; + } // for tx + // right padding + k = pGlyph->xAdvance - (int)(d - (uint16_t*)pCache0); // remaining amount + for (tx=0; tx<k && (tx+pGlyph->xOffset+pGlyph->width) < cx; tx++) + *d++ = usBGColor; + lcdWriteDATA(pCache0, cx*sizeof(uint16_t)); + } // for ty + // padding below the current character + ty = y + pGlyph->yOffset + pGlyph->height; + for (; ty < maxy; ty++) { + d = (uint16_t *)pCache0; + for (tx=0; tx<cx; tx++) + d[tx] = usBGColor; + lcdWriteDATA(pCache0, cx*sizeof(uint16_t)); + } // for ty + } else if (usFGColor == usBGColor) { // transparent + int iCount; // opaque pixel count + d = (uint16_t*)pCache0; + for (iCount=0; iCount < cx; iCount++) + d[iCount] = usFGColor; // set up a line of solid color + iCount = 0; // number of sequential opaque pixels + for (ty=0; ty<cy; ty++) { + for (tx=0; tx<pGlyph->width; tx++) { + if (bits == 0) { // need to read more font data + uc = s[iBitOff>>3]; // get more font bitmap data + bits = 8 - (iBitOff & 7); // we might not be on a byte boundary + iBitOff += bits; // because of a clipped line + uc <<= (8-bits); + } // if we ran out of bits + if (tx < cx) { + if (uc & 0x80) { + iCount++; // one more opaque pixel + } else { // any opaque pixels to write? + if (iCount) { + lcdSetPosition(dx+tx-iCount, dy+ty, iCount, 1); + d = (uint16_t *)pCache0; // point to start of output buffer + lcdWriteDATA(pCache0, iCount*sizeof(uint16_t)); + iCount = 0; + } // if opaque pixels to write + } // if transparent pixel hit + } + bits--; // next bit + uc <<= 1; + } // for tx + } // for ty + // quicker drawing + } else { // just draw the current character box fast + lcdSetPosition(dx, dy, cx, cy); + d = (uint16_t *)pCache0; // point to start of output buffer + for (ty=0; ty<cy; ty++) { + for (tx=0; tx<pGlyph->width; tx++) { + if (bits == 0) { // need to read more font data + uc = s[iBitOff>>3]; // get more font bitmap data + bits = 8 - (iBitOff & 7); // we might not be on a byte boundary + iBitOff += bits; // because of a clipped line + uc <<= (8-bits); + k = (int)(d-(uint16_t*)pCache0); // number of words in output buffer + if (k >= TEMP_HIGHWATER) { // time to write it + lcdWriteDATA(pCache0, k*sizeof(uint16_t)); + d = (uint16_t*)pCache0; + } + } // if we ran out of bits + if (tx < cx) { + *d++ = (uc & 0x80) ? usFGColor : usBGColor; + } + bits--; // next bit + uc <<= 1; + } // for tx + } // for ty + k = (int)(d-(uint16_t*)pCache0); + if (k) // write any remaining data + lcdWriteDATA(pCache0, k*sizeof(uint16_t)); + } // quicker drawing + x += pGlyph->xAdvance; // width of this character + } // while drawing characters + iCursorX = x; + iCursorY = y; + return 0; +} /* lcdWriteStringCustom() */ +// end of spi_lcd.inl diff --git a/examples/direct_gpio/direct_gpio.c b/examples/direct_gpio/direct_gpio.c index 313ef15be1a9d48c6c34154c2eb148005e144044..884b3c5636ddf6c27133aa32b18dbebca708e44b 100644 --- a/examples/direct_gpio/direct_gpio.c +++ b/examples/direct_gpio/direct_gpio.c @@ -1,8 +1,6 @@ #include "ch32v003fun.h" #include <stdio.h> -uint32_t count; - int main() { SystemInit(); @@ -41,7 +39,6 @@ int main() GPIOC->OUTDR &= ~(1<<(4)); // CLEAR GPIO C4 Delay_Ms( 50 ); - count++; } } diff --git a/examples/spi_max7219/Makefile b/examples/spi_max7219/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..4017a9d934dfa7593e6cef4f7978e23c2507a522 --- /dev/null +++ b/examples/spi_max7219/Makefile @@ -0,0 +1,10 @@ +all : flash + +TARGET:=spi_max7219 + +include ../../ch32v003fun/ch32v003fun.mk + +flash : cv_flash +clean : cv_clean + + diff --git a/examples/spi_max7219/README.md b/examples/spi_max7219/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2605e21cfb9e5dd76301cfa2a13b61cb164a0094 --- /dev/null +++ b/examples/spi_max7219/README.md @@ -0,0 +1,19 @@ +# MAX7219 8 digit 7 segment display demo + +This example for the `max7219_spi_driver` and `max7219_spi_driver_extended` library demonstrates controlling a MAX7219 or MAX7221 based display over SPI with basic and advanced text writing, and at least one example of every available display function in some capacity. + +--- + +The MAX7219 and MAX7221 chipsets are used in many 8 character 7 segment and 8x8 single colour dot matrix displays which can be purchased preassembled on eBay and AliExpress very cheaply. The abundance and low cost of these displays makes them a great companion for small projects requiring a display output that is more capable than single LEDs but not as complex as something like an LCD. + +--- + +The example expects you to connect a MAX7219 based 7 segment 8 character display to your CH32V003 like so: +- `DIN` / `MOSI` to `PC6` +- `SCLK` to `PC5` +- `CS` to `PD0` + +You can choose which examples will be shown on the display by changing the `spi_max7219.c` file in the marked demo selection section. All examples are enabled by default. + +Once running, you'll see one of the many examples being displayed like so: + \ No newline at end of file diff --git a/examples/spi_max7219/demo_pic.jpg b/examples/spi_max7219/demo_pic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..33b15eb2cab5b746ab7105910e78c82bf92cab2e Binary files /dev/null and b/examples/spi_max7219/demo_pic.jpg differ diff --git a/examples/spi_max7219/funconfig.h b/examples/spi_max7219/funconfig.h new file mode 100644 index 0000000000000000000000000000000000000000..37a2af8d0bacc627e3dc7745feecaf1bed95d6ae --- /dev/null +++ b/examples/spi_max7219/funconfig.h @@ -0,0 +1,6 @@ +#ifndef _FUNCONFIG_H +#define _FUNCONFIG_H + +#define CH32V003 1 + +#endif diff --git a/examples/spi_max7219/max7219_spi_driver.h b/examples/spi_max7219/max7219_spi_driver.h new file mode 100644 index 0000000000000000000000000000000000000000..0a2ec35729de3d32b16df6302cc7c9bce38a5866 --- /dev/null +++ b/examples/spi_max7219/max7219_spi_driver.h @@ -0,0 +1,236 @@ +/* + * SPI based driver for the MAX7219 display driver (https://www.analog.com/media/en/technical-documentation/data-sheets/MAX7219-MAX7221.pdf) + * + * The driver can be used for 64 discrete LED control, or for 8 digits of 7 segment (+ decimal) displays. It includes a basic 7 segment display font called Code B + * which is made available with this library, as well as various other functions such as multiplexer scan limiting, digital brightness control and font decoding. + * + * The one-byte segment arrangement in the digit registers appear like so: + * + * A + * --- + * F | G | B + * --- + * E | | C + * --- + * D . DP + * + * +-----+----+----+----+----+----+----+----+----+ + * | Bit | B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 | + * +-----+----+----+----+----+----+----+----+----+ + * | Seg | DP | A | B | C | D | E | F | G | + * +-----+----+----+----+----+----+----+----+----+ + * + * On standard 8 digit boards, the 0th digit (MAX7219_REGISTER_DIGIT0) is on the right and the 7th digit (MAX7219_REGISTER_DIGIT7) is on the left. + */ + +//Include guard +#ifndef MAX7219_SPI_DRIVER_H +#define MAX7219_SPI_DRIVER_H + +//Includes +#include "ch32v003_SPI.h" +#include <stdbool.h> + +//Instance struct +struct MAX7219_Display +{ + uint8_t displays_in_chain; + + GPIO_TypeDef* chip_select_port; + uint8_t chip_select_pin; + + uint8_t last_set_decode_mode; +}; + +//Chip select methods +void MAX7219_select(struct MAX7219_Display display) +{ + display.chip_select_port->BSHR |= 0b00000001 << display.chip_select_pin << 16; //Reset pin (active low) +} + +void MAX7219_deselect(struct MAX7219_Display display) +{ + display.chip_select_port->BSHR |= 0b00000001 << display.chip_select_pin; //Set pin (active low) +} + +//Raw communication +#define MAX7219_REGISTER_NOOP 0x00 +/*#define MAX7219_REGISTER_DIGIT0 0x01 +#define MAX7219_REGISTER_DIGIT1 0x02 +#define MAX7219_REGISTER_DIGIT2 0x03 +#define MAX7219_REGISTER_DIGIT3 0x04 +#define MAX7219_REGISTER_DIGIT4 0x05 +#define MAX7219_REGISTER_DIGIT5 0x06 +#define MAX7219_REGISTER_DIGIT6 0x07 +#define MAX7219_REGISTER_DIGIT7 0x08*/ +#define MAX7219_REGISTER_DECODE_MODE 0x09 +#define MAX7219_REGISTER_INTENSITY 0x0A +#define MAX7219_REGISTER_SCANLIMIT 0x0B +#define MAX7219_REGISTER_SHUTDOWN 0x0C +#define MAX7219_REGISTER_DISPLAYTEST 0x0F + +void MAX7219_write_register(struct MAX7219_Display display, uint8_t reg, uint8_t data) +{ + reg &= 0b00001111; //Remove the top 4 bits as they are not used for the register, and only retain the last 4 bits + + uint16_t packet = reg << 8; //Apply the register address to the final packet in the top 8 bits + + packet |= data; //Apply the data to the final packet in the bottom 8 bits + + MAX7219_select(display); //Select the chip select line + + //Write the packet to the display + SPI_begin_16(); + + SPI_write_16(packet); + SPI_wait_transmit_finished(); + + SPI_end(); + + MAX7219_deselect(display); //Deselect the chip select line +} + +//Register helpers +void MAX7219_shutdown(struct MAX7219_Display display, bool set) +{ + MAX7219_write_register(display, MAX7219_REGISTER_SHUTDOWN, !set); +} + +void MAX7219_test(struct MAX7219_Display display, bool set) +{ + MAX7219_write_register(display, MAX7219_REGISTER_DISPLAYTEST, set); +} + +#define MAX7219_DECODE_MODE_NONE 0x00 +#define MAX7219_DECODE_MODE_0_ONLY 0x01 +#define MAX7219_DECODE_MODE_0_TO_3_ONLY 0x0F +#define MAX7219_DECODE_MODE_ALL 0xFF + +void MAX7219_set_decode_mode(struct MAX7219_Display display, uint8_t mode) +{ + MAX7219_write_register(display, MAX7219_REGISTER_DECODE_MODE, mode); + display.last_set_decode_mode = mode; +} + +#define MAX7219_SCANLIMIT_0_ONLY 0x00 +#define MAX7219_SCANLIMIT_01 0x01 +#define MAX7219_SCANLIMIT_012 0x02 +#define MAX7219_SCANLIMIT_0123 0x03 +#define MAX7219_SCANLIMIT_01234 0x04 +#define MAX7219_SCANLIMIT_012345 0x05 +#define MAX7219_SCANLIMIT_0123456 0x06 +#define MAX7219_SCANLIMIT_ALL 0x07 + +void MAX7219_set_scan_limit(struct MAX7219_Display display, uint8_t limit) +{ + limit &= 0b00000111; //Only accept the 3 lsbs for this register + MAX7219_write_register(display, MAX7219_REGISTER_SCANLIMIT, limit); +} + +#define MAX7219_BRIGHTNESS_MAX 0x0F +#define MAX7219_MRIGHTNESS_MIN 0x00 + +void MAX7219_set_brightness(struct MAX7219_Display display, uint8_t brightness) +{ + brightness &= 0b00001111; //Only accept the 4 lsbs for this register + MAX7219_write_register(display, MAX7219_REGISTER_INTENSITY, brightness); +} + +//Built in CODE B font +#define MAX7219_CODEB_ADD_DECPOINT 0x80 +#define MAX7219_CODEB_DASH 0x0A +#define MAX7219_CODEB_BLANK 0x0F +#define MAX7219_CODEB_0 0x00 +#define MAX7219_CODEB_1 0x01 +#define MAX7219_CODEB_2 0x02 +#define MAX7219_CODEB_3 0x03 +#define MAX7219_CODEB_4 0x04 +#define MAX7219_CODEB_5 0x05 +#define MAX7219_CODEB_6 0x06 +#define MAX7219_CODEB_7 0x07 +#define MAX7219_CODEB_8 0x08 +#define MAX7219_CODEB_9 0x09 +#define MAX7219_CODEB_E 0x0B +#define MAX7219_CODEB_H 0x0C +#define MAX7219_CODEB_L 0x0D +#define MAX7219_CODEB_P 0x0E + +//Raw segment +#define MAX7219_SEGMENT_DP 0b10000000 +#define MAX7219_SEGMENT_A 0b01000000 +#define MAX7219_SEGMENT_B 0b00100000 +#define MAX7219_SEGMENT_C 0b00010000 +#define MAX7219_SEGMENT_D 0b00001000 +#define MAX7219_SEGMENT_E 0b00000100 +#define MAX7219_SEGMENT_F 0b00000010 +#define MAX7219_SEGMENT_G 0b00000001 + +void MAX7219_set_digit(struct MAX7219_Display display, uint32_t digit, uint8_t value) +{ + uint32_t literalDigit = digit % 8; + //uint32_t displayIndex = digit / 8; + + MAX7219_write_register(display, literalDigit + 1, value); +} + +void MAX7219_reset(struct MAX7219_Display display) +{ + //Set display brightness to maximum + MAX7219_set_brightness(display, MAX7219_BRIGHTNESS_MAX); + + //Set the scan limit to all 8 digits enabled + MAX7219_set_scan_limit(display, MAX7219_SCANLIMIT_ALL); + + //Enable Code-B decode on all digits + MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_ALL); + + //Clear all digits + for (size_t digitPos = 0; digitPos < display.displays_in_chain * 8; digitPos++) + { + MAX7219_set_digit(display, digitPos, MAX7219_CODEB_BLANK); + } + + //Take the display out of shutdown + MAX7219_shutdown(display, false); + + //Take the display out of test mode + MAX7219_test(display, false); +} + +void MAX7219_init(struct MAX7219_Display display) +{ + //Default deselected + MAX7219_deselect(display); + + //Ensure port is enabled + uint32_t selectedPortAddress = (uint32_t)display.chip_select_port; + + switch (selectedPortAddress) + { + case GPIOA_BASE: + RCC->APB2PCENR |= RCC_APB2Periph_GPIOA; + break; + + case GPIOC_BASE: + RCC->APB2PCENR |= RCC_APB2Periph_GPIOC; + break; + + case GPIOD_BASE: + RCC->APB2PCENR |= RCC_APB2Periph_GPIOD; + break; + + default: break; + } + + //Enable push-pull on pin + display.chip_select_port->CFGLR &= ~(0xf<<(4*display.chip_select_pin)); + display.chip_select_port->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*display.chip_select_pin); + + //Initialise SPI + SPI_init(); + + //Clear display to driver defaults + MAX7219_reset(display); +} + +#endif //MAX7219_SPI_DRIVER_H include guard \ No newline at end of file diff --git a/examples/spi_max7219/max7219_spi_driver_extended.h b/examples/spi_max7219/max7219_spi_driver_extended.h new file mode 100644 index 0000000000000000000000000000000000000000..cb791c2236ca6a0e9ea4758f887728fe651d7b59 --- /dev/null +++ b/examples/spi_max7219/max7219_spi_driver_extended.h @@ -0,0 +1,60 @@ +//Include guard +#ifndef MAX7219_SPI_DRIVER_EXTENDED_H +#define MAX7219_SPI_DRIVER_EXTENDED_H + +//Includes +#include "max7219_spi_driver.h" + +//---Extended Font--- +//Letters +#define MAX7219_EXTFONT_A MAX7219_SEGMENT_A | MAX7219_SEGMENT_B | MAX7219_SEGMENT_C | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_B MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_C MAX7219_SEGMENT_A | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F +#define MAX7219_EXTFONT_C_LOWER MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_D MAX7219_SEGMENT_B | MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_E MAX7219_SEGMENT_A | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_F MAX7219_SEGMENT_A | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_H MAX7219_SEGMENT_B | MAX7219_SEGMENT_C | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_H_LOWER MAX7219_SEGMENT_C | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_I MAX7219_SEGMENT_E | MAX7219_SEGMENT_F +#define MAX7219_EXTFONT_I_LOWER MAX7219_SEGMENT_E +#define MAX7219_EXTFONT_L MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F +#define MAX7219_EXTFONT_L_LOWER MAX7219_SEGMENT_E | MAX7219_SEGMENT_F +#define MAX7219_EXTFONT_N MAX7219_SEGMENT_C | MAX7219_SEGMENT_E | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_O MAX7219_SEGMENT_A | MAX7219_SEGMENT_B | MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F +#define MAX7219_EXTFONT_O_LOWER MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_P MAX7219_SEGMENT_A | MAX7219_SEGMENT_B | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_S MAX7219_SEGMENT_A | MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_T MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_U MAX7219_SEGMENT_B | MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F +#define MAX7219_EXTFONT_U_LOWER MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E +#define MAX7219_EXTFONT_X MAX7219_SEGMENT_C | MAX7219_SEGMENT_F +#define MAX7219_EXTFONT_Y MAX7219_SEGMENT_B | MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_Z MAX7219_SEGMENT_A | MAX7219_SEGMENT_B | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_G + +//Numbers +#define MAX7219_EXTFONT_0 MAX7219_SEGMENT_A | MAX7219_SEGMENT_B | MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F +#define MAX7219_EXTFONT_1 MAX7219_SEGMENT_B | MAX7219_SEGMENT_C +#define MAX7219_EXTFONT_2 MAX7219_SEGMENT_A | MAX7219_SEGMENT_B | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_3 MAX7219_SEGMENT_A | MAX7219_SEGMENT_B | MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_4 MAX7219_SEGMENT_B | MAX7219_SEGMENT_C | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_5 MAX7219_SEGMENT_A | MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_6 MAX7219_SEGMENT_A | MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_7 MAX7219_SEGMENT_A | MAX7219_SEGMENT_B | MAX7219_SEGMENT_C +#define MAX7219_EXTFONT_8 MAX7219_SEGMENT_A | MAX7219_SEGMENT_B | MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_E | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_9 MAX7219_SEGMENT_A | MAX7219_SEGMENT_B | MAX7219_SEGMENT_C | MAX7219_SEGMENT_D | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G + +//Symbols +#define MAX7219_EXTFONT_BLANK 0x00 +#define MAX7219_EXTFONT_DEGREES MAX7219_SEGMENT_A | MAX7219_SEGMENT_B | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_EQUALS_TOP MAX7219_SEGMENT_A | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_EQUALS_BOTTOM MAX7219_SEGMENT_D | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_TRIEQUALS MAX7219_SEGMENT_A | MAX7219_SEGMENT_D | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_HYPHEN MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_UNDERSCORE MAX7219_SEGMENT_D +#define MAX7219_EXTFONT_OVERSCORE MAX7219_SEGMENT_A +#define MAX7219_EXTFONT_FORWARDSLASH MAX7219_SEGMENT_B | MAX7219_SEGMENT_E | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_BACKSLASH MAX7219_SEGMENT_C | MAX7219_SEGMENT_F | MAX7219_SEGMENT_G +#define MAX7219_EXTFONT_DECIMAL MAX7219_SEGMENT_DP + +#endif //MAX7219_SPI_DRIVER_EXTENDED_H include guard \ No newline at end of file diff --git a/examples/spi_max7219/spi_max7219.c b/examples/spi_max7219/spi_max7219.c new file mode 100644 index 0000000000000000000000000000000000000000..a430a2c5b89337dc7c463cf6ac7e364473aa14a0 --- /dev/null +++ b/examples/spi_max7219/spi_max7219.c @@ -0,0 +1,691 @@ +#define CH32V003_SPI_SPEED_HZ 1000000 + +#define CH32V003_SPI_IMPLEMENTATION +#define CH32V003_SPI_DIRECTION_1LINE_TX +#define CH32V003_SPI_CLK_MODE_POL0_PHA0 +#define CH32V003_SPI_NSS_SOFTWARE_ANY_MANUAL + +//Control which demos you want to see here +#define DEMO_DISPLAY_TEST +#define DEMO_CODEB +#define DEMO_EXTENDED_FONT +#define DEMO_HELLO_BLINK +#define DEMO_FIGURES_OF_EIGHT +#define DEMO_TOGETHER_TWIRLS +#define DEMO_COUNTER_TWIRLS +#define DEMO_KNIGHTRIDER_DECIMAL +#define DEMO_KNIGHTRIDER_DASH +#define DEMO_INTENSITY_FADE_DECIMALS +#define DEMO_INTENSITY_FADE_DIGITS +#define DEMO_LED_CHASE + +#include "ch32v003fun.h" +#include <stdio.h> +#include "max7219_spi_driver.h" +#include "max7219_spi_driver_extended.h" + +//MOSI on PC6, SCLK on PC5, software controlled CS on PD0 + +int main() +{ + SystemInit(); + + struct MAX7219_Display display = + { + .displays_in_chain = 1, + + .chip_select_port = GPIOD, + .chip_select_pin = 0 + }; + + MAX7219_init(display); + + while(1) + { + #ifdef DEMO_DISPLAY_TEST + //Display test on + MAX7219_test(display, true); + Delay_Ms(1000); + + //Display test off + MAX7219_test(display, false); + #endif + + #ifdef DEMO_CODEB + //Code B non-decimal + MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_ALL); + + MAX7219_set_digit(display, 7, MAX7219_CODEB_0); + MAX7219_set_digit(display, 6, MAX7219_CODEB_1); + MAX7219_set_digit(display, 5, MAX7219_CODEB_2); + MAX7219_set_digit(display, 4, MAX7219_CODEB_3); + MAX7219_set_digit(display, 3, MAX7219_CODEB_4); + MAX7219_set_digit(display, 2, MAX7219_CODEB_5); + MAX7219_set_digit(display, 1, MAX7219_CODEB_6); + MAX7219_set_digit(display, 0, MAX7219_CODEB_7); + + Delay_Ms(3000); + + MAX7219_set_digit(display, 7, MAX7219_CODEB_8); + MAX7219_set_digit(display, 6, MAX7219_CODEB_9); + MAX7219_set_digit(display, 5, MAX7219_CODEB_DASH); + MAX7219_set_digit(display, 4, MAX7219_CODEB_H); + MAX7219_set_digit(display, 3, MAX7219_CODEB_E); + MAX7219_set_digit(display, 2, MAX7219_CODEB_L); + MAX7219_set_digit(display, 1, MAX7219_CODEB_P); + MAX7219_set_digit(display, 0, MAX7219_CODEB_BLANK); + + Delay_Ms(3000); + + //Code B decimal + MAX7219_set_digit(display, 7, MAX7219_CODEB_0 | MAX7219_CODEB_ADD_DECPOINT); + MAX7219_set_digit(display, 6, MAX7219_CODEB_1 | MAX7219_CODEB_ADD_DECPOINT); + MAX7219_set_digit(display, 5, MAX7219_CODEB_2 | MAX7219_CODEB_ADD_DECPOINT); + MAX7219_set_digit(display, 4, MAX7219_CODEB_3 | MAX7219_CODEB_ADD_DECPOINT); + MAX7219_set_digit(display, 3, MAX7219_CODEB_4 | MAX7219_CODEB_ADD_DECPOINT); + MAX7219_set_digit(display, 2, MAX7219_CODEB_5 | MAX7219_CODEB_ADD_DECPOINT); + MAX7219_set_digit(display, 1, MAX7219_CODEB_6 | MAX7219_CODEB_ADD_DECPOINT); + MAX7219_set_digit(display, 0, MAX7219_CODEB_7 | MAX7219_CODEB_ADD_DECPOINT); + + Delay_Ms(3000); + + MAX7219_set_digit(display, 7, MAX7219_CODEB_8 | MAX7219_CODEB_ADD_DECPOINT); + MAX7219_set_digit(display, 6, MAX7219_CODEB_9 | MAX7219_CODEB_ADD_DECPOINT); + MAX7219_set_digit(display, 5, MAX7219_CODEB_DASH | MAX7219_CODEB_ADD_DECPOINT); + MAX7219_set_digit(display, 4, MAX7219_CODEB_H | MAX7219_CODEB_ADD_DECPOINT); + MAX7219_set_digit(display, 3, MAX7219_CODEB_E | MAX7219_CODEB_ADD_DECPOINT); + MAX7219_set_digit(display, 2, MAX7219_CODEB_L | MAX7219_CODEB_ADD_DECPOINT); + MAX7219_set_digit(display, 1, MAX7219_CODEB_P | MAX7219_CODEB_ADD_DECPOINT); + MAX7219_set_digit(display, 0, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT); + + Delay_Ms(3000); + #endif + + #ifdef DEMO_EXTENDED_FONT + //Extended font + MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_NONE); + + MAX7219_set_digit(display, 7, MAX7219_EXTFONT_A); + MAX7219_set_digit(display, 6, MAX7219_EXTFONT_B); + MAX7219_set_digit(display, 5, MAX7219_EXTFONT_C); + MAX7219_set_digit(display, 4, MAX7219_EXTFONT_C_LOWER); + MAX7219_set_digit(display, 3, MAX7219_EXTFONT_D); + MAX7219_set_digit(display, 2, MAX7219_EXTFONT_E); + MAX7219_set_digit(display, 1, MAX7219_EXTFONT_F); + MAX7219_set_digit(display, 0, MAX7219_EXTFONT_H); + + Delay_Ms(3000); + + MAX7219_set_digit(display, 7, MAX7219_EXTFONT_H_LOWER); + MAX7219_set_digit(display, 6, MAX7219_EXTFONT_I); + MAX7219_set_digit(display, 5, MAX7219_EXTFONT_I_LOWER); + MAX7219_set_digit(display, 4, MAX7219_EXTFONT_L); + MAX7219_set_digit(display, 3, MAX7219_EXTFONT_L_LOWER); + MAX7219_set_digit(display, 2, MAX7219_EXTFONT_N); + MAX7219_set_digit(display, 1, MAX7219_EXTFONT_O); + MAX7219_set_digit(display, 0, MAX7219_EXTFONT_O_LOWER); + + Delay_Ms(3000); + + MAX7219_set_digit(display, 7, MAX7219_EXTFONT_P); + MAX7219_set_digit(display, 6, MAX7219_EXTFONT_S); + MAX7219_set_digit(display, 5, MAX7219_EXTFONT_T); + MAX7219_set_digit(display, 4, MAX7219_EXTFONT_U); + MAX7219_set_digit(display, 3, MAX7219_EXTFONT_U_LOWER); + MAX7219_set_digit(display, 2, MAX7219_EXTFONT_X); + MAX7219_set_digit(display, 1, MAX7219_EXTFONT_Y); + MAX7219_set_digit(display, 0, MAX7219_EXTFONT_Z); + + Delay_Ms(3000); + + MAX7219_set_digit(display, 7, MAX7219_EXTFONT_0); + MAX7219_set_digit(display, 6, MAX7219_EXTFONT_1); + MAX7219_set_digit(display, 5, MAX7219_EXTFONT_2); + MAX7219_set_digit(display, 4, MAX7219_EXTFONT_3); + MAX7219_set_digit(display, 3, MAX7219_EXTFONT_4); + MAX7219_set_digit(display, 2, MAX7219_EXTFONT_5); + MAX7219_set_digit(display, 1, MAX7219_EXTFONT_6); + MAX7219_set_digit(display, 0, MAX7219_EXTFONT_7); + + Delay_Ms(3000); + + MAX7219_set_digit(display, 7, MAX7219_EXTFONT_8); + MAX7219_set_digit(display, 6, MAX7219_EXTFONT_9); + MAX7219_set_digit(display, 5, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 4, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 3, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 2, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 1, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 0, MAX7219_EXTFONT_BLANK); + + Delay_Ms(3000); + + MAX7219_set_digit(display, 7, MAX7219_EXTFONT_DEGREES); + MAX7219_set_digit(display, 6, MAX7219_EXTFONT_EQUALS_TOP); + MAX7219_set_digit(display, 5, MAX7219_EXTFONT_EQUALS_BOTTOM); + MAX7219_set_digit(display, 4, MAX7219_EXTFONT_TRIEQUALS); + MAX7219_set_digit(display, 3, MAX7219_EXTFONT_HYPHEN); + MAX7219_set_digit(display, 2, MAX7219_EXTFONT_UNDERSCORE); + MAX7219_set_digit(display, 1, MAX7219_EXTFONT_OVERSCORE); + MAX7219_set_digit(display, 0, MAX7219_EXTFONT_FORWARDSLASH); + + Delay_Ms(3000); + + MAX7219_set_digit(display, 7, MAX7219_EXTFONT_BACKSLASH); + MAX7219_set_digit(display, 6, MAX7219_EXTFONT_DECIMAL); + MAX7219_set_digit(display, 5, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 4, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 3, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 2, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 1, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 0, MAX7219_EXTFONT_BLANK); + + Delay_Ms(3000); + #endif + + #ifdef DEMO_HELLO_BLINK + MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_NONE); + + MAX7219_set_digit(display, 7, MAX7219_EXTFONT_H); + MAX7219_set_digit(display, 6, MAX7219_EXTFONT_E); + MAX7219_set_digit(display, 5, MAX7219_EXTFONT_L); + MAX7219_set_digit(display, 4, MAX7219_EXTFONT_L); + MAX7219_set_digit(display, 3, MAX7219_EXTFONT_O); + MAX7219_set_digit(display, 2, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 1, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 0, MAX7219_EXTFONT_BLANK); + + Delay_Ms(500); + + MAX7219_shutdown(display, true); + + Delay_Ms(500); + + MAX7219_shutdown(display, false); + + Delay_Ms(500); + + MAX7219_shutdown(display, true); + + Delay_Ms(500); + + MAX7219_shutdown(display, false); + + Delay_Ms(500); + + MAX7219_shutdown(display, true); + + Delay_Ms(500); + + MAX7219_shutdown(display, false); + #endif + + #ifdef DEMO_FIGURES_OF_EIGHT + //Figures of 8 (HP style) + MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_NONE); + + MAX7219_set_digit(display, 7, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 6, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 5, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 4, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 3, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 2, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 1, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 0, MAX7219_EXTFONT_BLANK); + + for (size_t figureeightcycles = 0; figureeightcycles < 8; figureeightcycles++) + { + MAX7219_set_digit(display, 7, MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 6, MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 5, MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 4, MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 3, MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 2, MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 1, MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 0, MAX7219_SEGMENT_G); + Delay_Ms(75); + + MAX7219_set_digit(display, 7, MAX7219_SEGMENT_B); + MAX7219_set_digit(display, 6, MAX7219_SEGMENT_B); + MAX7219_set_digit(display, 5, MAX7219_SEGMENT_B); + MAX7219_set_digit(display, 4, MAX7219_SEGMENT_B); + MAX7219_set_digit(display, 3, MAX7219_SEGMENT_B); + MAX7219_set_digit(display, 2, MAX7219_SEGMENT_B); + MAX7219_set_digit(display, 1, MAX7219_SEGMENT_B); + MAX7219_set_digit(display, 0, MAX7219_SEGMENT_B); + Delay_Ms(75); + + MAX7219_set_digit(display, 7, MAX7219_SEGMENT_A); + MAX7219_set_digit(display, 6, MAX7219_SEGMENT_A); + MAX7219_set_digit(display, 5, MAX7219_SEGMENT_A); + MAX7219_set_digit(display, 4, MAX7219_SEGMENT_A); + MAX7219_set_digit(display, 3, MAX7219_SEGMENT_A); + MAX7219_set_digit(display, 2, MAX7219_SEGMENT_A); + MAX7219_set_digit(display, 1, MAX7219_SEGMENT_A); + MAX7219_set_digit(display, 0, MAX7219_SEGMENT_A); + Delay_Ms(75); + + MAX7219_set_digit(display, 7, MAX7219_SEGMENT_F); + MAX7219_set_digit(display, 6, MAX7219_SEGMENT_F); + MAX7219_set_digit(display, 5, MAX7219_SEGMENT_F); + MAX7219_set_digit(display, 4, MAX7219_SEGMENT_F); + MAX7219_set_digit(display, 3, MAX7219_SEGMENT_F); + MAX7219_set_digit(display, 2, MAX7219_SEGMENT_F); + MAX7219_set_digit(display, 1, MAX7219_SEGMENT_F); + MAX7219_set_digit(display, 0, MAX7219_SEGMENT_F); + Delay_Ms(75); + + MAX7219_set_digit(display, 7, MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 6, MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 5, MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 4, MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 3, MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 2, MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 1, MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 0, MAX7219_SEGMENT_G); + Delay_Ms(75); + + MAX7219_set_digit(display, 7, MAX7219_SEGMENT_C); + MAX7219_set_digit(display, 6, MAX7219_SEGMENT_C); + MAX7219_set_digit(display, 5, MAX7219_SEGMENT_C); + MAX7219_set_digit(display, 4, MAX7219_SEGMENT_C); + MAX7219_set_digit(display, 3, MAX7219_SEGMENT_C); + MAX7219_set_digit(display, 2, MAX7219_SEGMENT_C); + MAX7219_set_digit(display, 1, MAX7219_SEGMENT_C); + MAX7219_set_digit(display, 0, MAX7219_SEGMENT_C); + Delay_Ms(75); + + MAX7219_set_digit(display, 7, MAX7219_SEGMENT_D); + MAX7219_set_digit(display, 6, MAX7219_SEGMENT_D); + MAX7219_set_digit(display, 5, MAX7219_SEGMENT_D); + MAX7219_set_digit(display, 4, MAX7219_SEGMENT_D); + MAX7219_set_digit(display, 3, MAX7219_SEGMENT_D); + MAX7219_set_digit(display, 2, MAX7219_SEGMENT_D); + MAX7219_set_digit(display, 1, MAX7219_SEGMENT_D); + MAX7219_set_digit(display, 0, MAX7219_SEGMENT_D); + Delay_Ms(75); + + MAX7219_set_digit(display, 7, MAX7219_SEGMENT_E); + MAX7219_set_digit(display, 6, MAX7219_SEGMENT_E); + MAX7219_set_digit(display, 5, MAX7219_SEGMENT_E); + MAX7219_set_digit(display, 4, MAX7219_SEGMENT_E); + MAX7219_set_digit(display, 3, MAX7219_SEGMENT_E); + MAX7219_set_digit(display, 2, MAX7219_SEGMENT_E); + MAX7219_set_digit(display, 1, MAX7219_SEGMENT_E); + MAX7219_set_digit(display, 0, MAX7219_SEGMENT_E); + Delay_Ms(75); + } + #endif + + #ifdef DEMO_TOGETHER_TWIRLS + //Together twirls + MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_NONE); + + for (size_t togethertwirlcycles = 0; togethertwirlcycles < 8; togethertwirlcycles++) + { + MAX7219_set_digit(display, 7, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 6, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 5, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 4, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 3, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 2, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 1, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 0, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G); + Delay_Ms(250); + + MAX7219_set_digit(display, 7, MAX7219_SEGMENT_B | MAX7219_SEGMENT_C); + MAX7219_set_digit(display, 6, MAX7219_SEGMENT_B | MAX7219_SEGMENT_C); + MAX7219_set_digit(display, 5, MAX7219_SEGMENT_B | MAX7219_SEGMENT_C); + MAX7219_set_digit(display, 4, MAX7219_SEGMENT_B | MAX7219_SEGMENT_C); + MAX7219_set_digit(display, 3, MAX7219_SEGMENT_B | MAX7219_SEGMENT_C); + MAX7219_set_digit(display, 2, MAX7219_SEGMENT_B | MAX7219_SEGMENT_C); + MAX7219_set_digit(display, 1, MAX7219_SEGMENT_B | MAX7219_SEGMENT_C); + MAX7219_set_digit(display, 0, MAX7219_SEGMENT_B | MAX7219_SEGMENT_C); + Delay_Ms(250); + + MAX7219_set_digit(display, 7, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D); + MAX7219_set_digit(display, 6, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D); + MAX7219_set_digit(display, 5, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D); + MAX7219_set_digit(display, 4, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D); + MAX7219_set_digit(display, 3, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D); + MAX7219_set_digit(display, 2, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D); + MAX7219_set_digit(display, 1, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D); + MAX7219_set_digit(display, 0, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D); + Delay_Ms(250); + + MAX7219_set_digit(display, 7, MAX7219_SEGMENT_E | MAX7219_SEGMENT_F); + MAX7219_set_digit(display, 6, MAX7219_SEGMENT_E | MAX7219_SEGMENT_F); + MAX7219_set_digit(display, 5, MAX7219_SEGMENT_E | MAX7219_SEGMENT_F); + MAX7219_set_digit(display, 4, MAX7219_SEGMENT_E | MAX7219_SEGMENT_F); + MAX7219_set_digit(display, 3, MAX7219_SEGMENT_E | MAX7219_SEGMENT_F); + MAX7219_set_digit(display, 2, MAX7219_SEGMENT_E | MAX7219_SEGMENT_F); + MAX7219_set_digit(display, 1, MAX7219_SEGMENT_E | MAX7219_SEGMENT_F); + MAX7219_set_digit(display, 0, MAX7219_SEGMENT_E | MAX7219_SEGMENT_F); + Delay_Ms(250); + } + #endif + + #ifdef DEMO_COUNTER_TWIRLS + //Counter twirls + MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_NONE); + + for (size_t countertwirlcycles = 0; countertwirlcycles < 8; countertwirlcycles++) + { + MAX7219_set_digit(display, 7, MAX7219_SEGMENT_F | MAX7219_SEGMENT_C); + MAX7219_set_digit(display, 6, MAX7219_SEGMENT_F | MAX7219_SEGMENT_C); + MAX7219_set_digit(display, 5, MAX7219_SEGMENT_F | MAX7219_SEGMENT_C); + MAX7219_set_digit(display, 4, MAX7219_SEGMENT_F | MAX7219_SEGMENT_C); + MAX7219_set_digit(display, 3, MAX7219_SEGMENT_F | MAX7219_SEGMENT_C); + MAX7219_set_digit(display, 2, MAX7219_SEGMENT_F | MAX7219_SEGMENT_C); + MAX7219_set_digit(display, 1, MAX7219_SEGMENT_F | MAX7219_SEGMENT_C); + MAX7219_set_digit(display, 0, MAX7219_SEGMENT_F | MAX7219_SEGMENT_C); + Delay_Ms(250); + + MAX7219_set_digit(display, 7, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 6, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 5, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 4, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 3, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 2, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 1, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G); + MAX7219_set_digit(display, 0, MAX7219_SEGMENT_A | MAX7219_SEGMENT_G); + Delay_Ms(250); + + MAX7219_set_digit(display, 7, MAX7219_SEGMENT_B | MAX7219_SEGMENT_E); + MAX7219_set_digit(display, 6, MAX7219_SEGMENT_B | MAX7219_SEGMENT_E); + MAX7219_set_digit(display, 5, MAX7219_SEGMENT_B | MAX7219_SEGMENT_E); + MAX7219_set_digit(display, 4, MAX7219_SEGMENT_B | MAX7219_SEGMENT_E); + MAX7219_set_digit(display, 3, MAX7219_SEGMENT_B | MAX7219_SEGMENT_E); + MAX7219_set_digit(display, 2, MAX7219_SEGMENT_B | MAX7219_SEGMENT_E); + MAX7219_set_digit(display, 1, MAX7219_SEGMENT_B | MAX7219_SEGMENT_E); + MAX7219_set_digit(display, 0, MAX7219_SEGMENT_B | MAX7219_SEGMENT_E); + Delay_Ms(250); + + MAX7219_set_digit(display, 7, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D); + MAX7219_set_digit(display, 6, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D); + MAX7219_set_digit(display, 5, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D); + MAX7219_set_digit(display, 4, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D); + MAX7219_set_digit(display, 3, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D); + MAX7219_set_digit(display, 2, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D); + MAX7219_set_digit(display, 1, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D); + MAX7219_set_digit(display, 0, MAX7219_SEGMENT_G | MAX7219_SEGMENT_D); + Delay_Ms(250); + } + #endif + + #ifdef DEMO_KNIGHTRIDER_DECIMAL + //Decimal knight-rider-like bounce effect using Code B on decimals + MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_ALL); + + MAX7219_set_digit(display, 7, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT); + MAX7219_set_digit(display, 6, MAX7219_CODEB_BLANK); + MAX7219_set_digit(display, 5, MAX7219_CODEB_BLANK); + MAX7219_set_digit(display, 4, MAX7219_CODEB_BLANK); + MAX7219_set_digit(display, 3, MAX7219_CODEB_BLANK); + MAX7219_set_digit(display, 2, MAX7219_CODEB_BLANK); + MAX7219_set_digit(display, 1, MAX7219_CODEB_BLANK); + MAX7219_set_digit(display, 0, MAX7219_CODEB_BLANK); + + Delay_Ms(125); + + for (size_t ridercycles = 0; ridercycles < 4; ridercycles++) + { + size_t position = 7; + + while (position > 0) + { + //If the next position is within bounds, place a DP there + if (position - 1 >= 0) MAX7219_set_digit(display, position - 1, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT); + + //Clear current position + MAX7219_set_digit(display, position, MAX7219_CODEB_BLANK); + + //Move onto the next position + position--; + + Delay_Ms(125); + } + + while (position < 7) + { + //If the next position is within bounds, place a DP there + if (position + 1 <= 7) MAX7219_set_digit(display, position + 1, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT); + + //Clear current position + MAX7219_set_digit(display, position, MAX7219_CODEB_BLANK); + + //Move onto the next position + position++; + + Delay_Ms(125); + } + } + #endif + + #ifdef DEMO_KNIGHTRIDER_DASH + //Decimal knight-rider-like bounce effect using Code B on dashes + MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_ALL); + + MAX7219_set_digit(display, 7, MAX7219_CODEB_DASH); + MAX7219_set_digit(display, 6, MAX7219_CODEB_BLANK); + MAX7219_set_digit(display, 5, MAX7219_CODEB_BLANK); + MAX7219_set_digit(display, 4, MAX7219_CODEB_BLANK); + MAX7219_set_digit(display, 3, MAX7219_CODEB_BLANK); + MAX7219_set_digit(display, 2, MAX7219_CODEB_BLANK); + MAX7219_set_digit(display, 1, MAX7219_CODEB_BLANK); + MAX7219_set_digit(display, 0, MAX7219_CODEB_BLANK); + + Delay_Ms(125); + + for (size_t ridercycles = 0; ridercycles < 4; ridercycles++) + { + size_t position = 7; + + while (position > 0) + { + //If the next position is within bounds, place a DP there + if (position - 1 >= 0) MAX7219_set_digit(display, position - 1, MAX7219_CODEB_DASH); + + //Clear current position + MAX7219_set_digit(display, position, MAX7219_CODEB_BLANK); + + //Move onto the next position + position--; + + Delay_Ms(125); + } + + while (position < 7) + { + //If the next position is within bounds, place a DP there + if (position + 1 <= 7) MAX7219_set_digit(display, position + 1, MAX7219_CODEB_DASH); + + //Clear current position + MAX7219_set_digit(display, position, MAX7219_CODEB_BLANK); + + //Move onto the next position + position++; + + Delay_Ms(125); + } + } + #endif + + #ifdef DEMO_INTENSITY_FADE_DECIMALS + //All decimals intensity fade effect + MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_ALL); + + MAX7219_set_digit(display, 7, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT); + MAX7219_set_digit(display, 6, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT); + MAX7219_set_digit(display, 5, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT); + MAX7219_set_digit(display, 4, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT); + MAX7219_set_digit(display, 3, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT); + MAX7219_set_digit(display, 2, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT); + MAX7219_set_digit(display, 1, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT); + MAX7219_set_digit(display, 0, MAX7219_CODEB_BLANK | MAX7219_CODEB_ADD_DECPOINT); + + MAX7219_set_brightness(display, MAX7219_BRIGHTNESS_MAX); + + Delay_Ms(34); //~30FPS + + for (size_t intensitycycles = 0; intensitycycles < 4; intensitycycles++) + { + size_t intensity = 0x0E; + + while (intensity > 0x00) + { + MAX7219_set_brightness(display, intensity); + + intensity--; + + Delay_Ms(34); //~30FPS + } + + while (intensity < 0x0F) + { + MAX7219_set_brightness(display, intensity); + + //Move onto the next position + intensity++; + + Delay_Ms(34); //~30FPS + } + } + #endif + + #ifdef DEMO_INTENSITY_FADE_DIGITS + //Numeric intensity fade effect + MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_ALL); + + MAX7219_set_digit(display, 7, MAX7219_CODEB_0); + MAX7219_set_digit(display, 6, MAX7219_CODEB_1); + MAX7219_set_digit(display, 5, MAX7219_CODEB_2); + MAX7219_set_digit(display, 4, MAX7219_CODEB_3); + MAX7219_set_digit(display, 3, MAX7219_CODEB_4); + MAX7219_set_digit(display, 2, MAX7219_CODEB_5); + MAX7219_set_digit(display, 1, MAX7219_CODEB_6); + MAX7219_set_digit(display, 0, MAX7219_CODEB_7); + + MAX7219_set_brightness(display, MAX7219_BRIGHTNESS_MAX); + + Delay_Ms(34); //~30FPS + + for (size_t intensitycycles = 0; intensitycycles < 4; intensitycycles++) + { + size_t intensity = 0x0E; + + while (intensity > 0x00) + { + MAX7219_set_brightness(display, intensity); + + intensity--; + + Delay_Ms(34); //~30FPS + } + + while (intensity < 0x0F) + { + MAX7219_set_brightness(display, intensity); + + //Move onto the next position + intensity++; + + Delay_Ms(34); //~30FPS + } + } + #endif + + #ifdef DEMO_LED_CHASE + //Spinny LED chase effect using raw segment control + MAX7219_reset(display); + + MAX7219_set_decode_mode(display, MAX7219_DECODE_MODE_NONE); + + MAX7219_set_digit(display, 7, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 6, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 5, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 4, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 3, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 2, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 1, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 0, MAX7219_EXTFONT_BLANK); + + for (size_t chasecycles = 0; chasecycles < 8; chasecycles++) + { + MAX7219_set_digit(display, 7, MAX7219_SEGMENT_A); + Delay_Ms(34); //~30FPS + + MAX7219_set_digit(display, 7, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 6, MAX7219_SEGMENT_A); + Delay_Ms(34); //~30FPS + + MAX7219_set_digit(display, 6, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 5, MAX7219_SEGMENT_A); + Delay_Ms(34); //~30FPS + + MAX7219_set_digit(display, 5, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 4, MAX7219_SEGMENT_A); + Delay_Ms(34); //~30FPS + + MAX7219_set_digit(display, 4, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 3, MAX7219_SEGMENT_A); + Delay_Ms(34); //~30FPS + + MAX7219_set_digit(display, 3, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 2, MAX7219_SEGMENT_A); + Delay_Ms(34); //~30FPS + + MAX7219_set_digit(display, 2, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 1, MAX7219_SEGMENT_A); + Delay_Ms(34); //~30FPS + + MAX7219_set_digit(display, 1, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 0, MAX7219_SEGMENT_A); + Delay_Ms(34); //~30FPS + + MAX7219_set_digit(display, 0, MAX7219_SEGMENT_B); + Delay_Ms(34); //~30FPS + + MAX7219_set_digit(display, 0, MAX7219_SEGMENT_C); + Delay_Ms(34); //~30FPS + + MAX7219_set_digit(display, 0, MAX7219_SEGMENT_D); + Delay_Ms(34); //~30FPS + + MAX7219_set_digit(display, 0, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 1, MAX7219_SEGMENT_D); + Delay_Ms(34); //~30FPS + + MAX7219_set_digit(display, 1, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 2, MAX7219_SEGMENT_D); + Delay_Ms(34); //~30FPS + + MAX7219_set_digit(display, 2, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 3, MAX7219_SEGMENT_D); + Delay_Ms(34); //~30FPS + + MAX7219_set_digit(display, 3, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 4, MAX7219_SEGMENT_D); + Delay_Ms(34); //~30FPS + + MAX7219_set_digit(display, 4, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 5, MAX7219_SEGMENT_D); + Delay_Ms(34); //~30FPS + + MAX7219_set_digit(display, 5, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 6, MAX7219_SEGMENT_D); + Delay_Ms(34); //~30FPS + + MAX7219_set_digit(display, 6, MAX7219_EXTFONT_BLANK); + MAX7219_set_digit(display, 7, MAX7219_SEGMENT_D); + Delay_Ms(34); //~30FPS + + MAX7219_set_digit(display, 7, MAX7219_SEGMENT_E); + Delay_Ms(34); //~30FPS + + MAX7219_set_digit(display, 7, MAX7219_SEGMENT_F); + Delay_Ms(34); //~30FPS + } + #endif + } +} diff --git a/examples/spi_oled/font_8x8.h b/examples/spi_oled/font_8x8.h new file mode 100644 index 0000000000000000000000000000000000000000..d1593abd9e2a56e536b50f3afe2e67fcc89f2cf2 --- /dev/null +++ b/examples/spi_oled/font_8x8.h @@ -0,0 +1,2569 @@ +/**********************************************/ +/* */ +/* Font file generated by cpi2fnt */ +/* */ +/**********************************************/ + +const static unsigned char fontdata[] = { + + /* 0 0x00 '^@' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 1 0x01 '^A' */ + 0x7e, /* 01111110 */ + 0x81, /* 10000001 */ + 0xa5, /* 10100101 */ + 0x81, /* 10000001 */ + 0xbd, /* 10111101 */ + 0x99, /* 10011001 */ + 0x81, /* 10000001 */ + 0x7e, /* 01111110 */ + + /* 2 0x02 '^B' */ + 0x7e, /* 01111110 */ + 0xff, /* 11111111 */ + 0xdb, /* 11011011 */ + 0xff, /* 11111111 */ + 0xc3, /* 11000011 */ + 0xe7, /* 11100111 */ + 0xff, /* 11111111 */ + 0x7e, /* 01111110 */ + + /* 3 0x03 '^C' */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + + /* 4 0x04 '^D' */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + + /* 5 0x05 '^E' */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + + /* 6 0x06 '^F' */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + + /* 7 0x07 '^G' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 8 0x08 '^H' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xe7, /* 11100111 */ + 0xc3, /* 11000011 */ + 0xc3, /* 11000011 */ + 0xe7, /* 11100111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 9 0x09 '^I' */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x42, /* 01000010 */ + 0x42, /* 01000010 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 10 0x0a '^J' */ + 0xff, /* 11111111 */ + 0xc3, /* 11000011 */ + 0x99, /* 10011001 */ + 0xbd, /* 10111101 */ + 0xbd, /* 10111101 */ + 0x99, /* 10011001 */ + 0xc3, /* 11000011 */ + 0xff, /* 11111111 */ + + /* 11 0x0b '^K' */ + 0x0f, /* 00001111 */ + 0x07, /* 00000111 */ + 0x0f, /* 00001111 */ + 0x7d, /* 01111101 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + + /* 12 0x0c '^L' */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + + /* 13 0x0d '^M' */ + 0x3f, /* 00111111 */ + 0x33, /* 00110011 */ + 0x3f, /* 00111111 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x70, /* 01110000 */ + 0xf0, /* 11110000 */ + 0xe0, /* 11100000 */ + + /* 14 0x0e '^N' */ + 0x7f, /* 01111111 */ + 0x63, /* 01100011 */ + 0x7f, /* 01111111 */ + 0x63, /* 01100011 */ + 0x63, /* 01100011 */ + 0x67, /* 01100111 */ + 0xe6, /* 11100110 */ + 0xc0, /* 11000000 */ + + /* 15 0x0f '^O' */ + 0x18, /* 00011000 */ + 0xdb, /* 11011011 */ + 0x3c, /* 00111100 */ + 0xe7, /* 11100111 */ + 0xe7, /* 11100111 */ + 0x3c, /* 00111100 */ + 0xdb, /* 11011011 */ + 0x18, /* 00011000 */ + + /* 16 0x10 '^P' */ + 0x80, /* 10000000 */ + 0xe0, /* 11100000 */ + 0xf8, /* 11111000 */ + 0xfe, /* 11111110 */ + 0xf8, /* 11111000 */ + 0xe0, /* 11100000 */ + 0x80, /* 10000000 */ + 0x00, /* 00000000 */ + + /* 17 0x11 '^Q' */ + 0x02, /* 00000010 */ + 0x0e, /* 00001110 */ + 0x3e, /* 00111110 */ + 0xfe, /* 11111110 */ + 0x3e, /* 00111110 */ + 0x0e, /* 00001110 */ + 0x02, /* 00000010 */ + 0x00, /* 00000000 */ + + /* 18 0x12 '^R' */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + + /* 19 0x13 '^S' */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + + /* 20 0x14 '^T' */ + 0x7f, /* 01111111 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7b, /* 01111011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x00, /* 00000000 */ + + /* 21 0x15 '^U' */ + 0x3e, /* 00111110 */ + 0x61, /* 01100001 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x86, /* 10000110 */ + 0x7c, /* 01111100 */ + + /* 22 0x16 '^V' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 23 0x17 '^W' */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + + /* 24 0x18 '^X' */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 25 0x19 '^Y' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 26 0x1a '^Z' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0xfe, /* 11111110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 27 0x1b '^[' */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xfe, /* 11111110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 28 0x1c '^\' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 29 0x1d '^]' */ + 0x00, /* 00000000 */ + 0x24, /* 00100100 */ + 0x66, /* 01100110 */ + 0xff, /* 11111111 */ + 0x66, /* 01100110 */ + 0x24, /* 00100100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 30 0x1e '^^' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 31 0x1f '^_' */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 32 0x20 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 33 0x21 '!' */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 34 0x22 '"' */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x24, /* 00100100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 35 0x23 '#' */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + + /* 36 0x24 '$' */ + 0x18, /* 00011000 */ + 0x3e, /* 00111110 */ + 0x60, /* 01100000 */ + 0x3c, /* 00111100 */ + 0x06, /* 00000110 */ + 0x7c, /* 01111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 37 0x25 '%' */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xcc, /* 11001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x66, /* 01100110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 38 0x26 '&' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 39 0x27 ''' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 40 0x28 '(' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + + /* 41 0x29 ')' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + + /* 42 0x2a '*' */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0xff, /* 11111111 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 43 0x2b '+' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 44 0x2c ',' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + + /* 45 0x2d '-' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 46 0x2e '.' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 47 0x2f '/' */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0x80, /* 10000000 */ + 0x00, /* 00000000 */ + + /* 48 0x30 '0' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* 49 0x31 '1' */ + 0x18, /* 00011000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 50 0x32 '2' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x1c, /* 00011100 */ + 0x30, /* 00110000 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 51 0x33 '3' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x3c, /* 00111100 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 52 0x34 '4' */ + 0x1c, /* 00011100 */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xfe, /* 11111110 */ + 0x0c, /* 00001100 */ + 0x1e, /* 00011110 */ + 0x00, /* 00000000 */ + + /* 53 0x35 '5' */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfc, /* 11111100 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 54 0x36 '6' */ + 0x38, /* 00111000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0xfc, /* 11111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 55 0x37 '7' */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + + /* 56 0x38 '8' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 57 0x39 '9' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* 58 0x3a ':' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 59 0x3b ';' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + + /* 60 0x3c '<' */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + + /* 61 0x3d '=' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 62 0x3e '>' */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + + /* 63 0x3f '?' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 64 0x40 '@' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xde, /* 11011110 */ + 0xde, /* 11011110 */ + 0xde, /* 11011110 */ + 0xc0, /* 11000000 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* 65 0x41 'A' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 66 0x42 'B' */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + + /* 67 0x43 'C' */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 68 0x44 'D' */ + 0xf8, /* 11111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + + /* 69 0x45 'E' */ + 0xfe, /* 11111110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x62, /* 01100010 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 70 0x46 'F' */ + 0xfe, /* 11111110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* 71 0x47 'G' */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xce, /* 11001110 */ + 0x66, /* 01100110 */ + 0x3a, /* 00111010 */ + 0x00, /* 00000000 */ + + /* 72 0x48 'H' */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 73 0x49 'I' */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 74 0x4a 'J' */ + 0x1e, /* 00011110 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* 75 0x4b 'K' */ + 0xe6, /* 11100110 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + + /* 76 0x4c 'L' */ + 0xf0, /* 11110000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 77 0x4d 'M' */ + 0xc6, /* 11000110 */ + 0xee, /* 11101110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 78 0x4e 'N' */ + 0xc6, /* 11000110 */ + 0xe6, /* 11100110 */ + 0xf6, /* 11110110 */ + 0xde, /* 11011110 */ + 0xce, /* 11001110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 79 0x4f 'O' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 80 0x50 'P' */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* 81 0x51 'Q' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xce, /* 11001110 */ + 0x7c, /* 01111100 */ + 0x0e, /* 00001110 */ + + /* 82 0x52 'R' */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + + /* 83 0x53 'S' */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 84 0x54 'T' */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x5a, /* 01011010 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 85 0x55 'U' */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 86 0x56 'V' */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* 87 0x57 'W' */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + + /* 88 0x58 'X' */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 89 0x59 'Y' */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 90 0x5a 'Z' */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x8c, /* 10001100 */ + 0x18, /* 00011000 */ + 0x32, /* 00110010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 91 0x5b '[' */ + 0x3c, /* 00111100 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 92 0x5c '\' */ + 0xc0, /* 11000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x02, /* 00000010 */ + 0x00, /* 00000000 */ + + /* 93 0x5d ']' */ + 0x3c, /* 00111100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 94 0x5e '^' */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 95 0x5f '_' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + + /* 96 0x60 '`' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 97 0x61 'a' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 98 0x62 'b' */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x7c, /* 01111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + + /* 99 0x63 'c' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 100 0x64 'd' */ + 0x1c, /* 00011100 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 101 0x65 'e' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 102 0x66 'f' */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x60, /* 01100000 */ + 0xf8, /* 11111000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* 103 0x67 'g' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0xf8, /* 11111000 */ + + /* 104 0x68 'h' */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x6c, /* 01101100 */ + 0x76, /* 01110110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + + /* 105 0x69 'i' */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 106 0x6a 'j' */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + + /* 107 0x6b 'k' */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + + /* 108 0x6c 'l' */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 109 0x6d 'm' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xec, /* 11101100 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0x00, /* 00000000 */ + + /* 110 0x6e 'n' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + + /* 111 0x6f 'o' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 112 0x70 'p' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + + /* 113 0x71 'q' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0x1e, /* 00011110 */ + + /* 114 0x72 'r' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x76, /* 01110110 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* 115 0x73 's' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x06, /* 00000110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + + /* 116 0x74 't' */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0xfc, /* 11111100 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x36, /* 00110110 */ + 0x1c, /* 00011100 */ + 0x00, /* 00000000 */ + + /* 117 0x75 'u' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 118 0x76 'v' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* 119 0x77 'w' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + + /* 120 0x78 'x' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 121 0x79 'y' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0xfc, /* 11111100 */ + + /* 122 0x7a 'z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x4c, /* 01001100 */ + 0x18, /* 00011000 */ + 0x32, /* 00110010 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 123 0x7b '{' */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x0e, /* 00001110 */ + 0x00, /* 00000000 */ + + /* 124 0x7c '|' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 125 0x7d '}' */ + 0x70, /* 01110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + + /* 126 0x7e '~' */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 127 0x7f '' */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 128 0x80 '€' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0x78, /* 01111000 */ + + /* 129 0x81 '' */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 130 0x82 '‚' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 131 0x83 'ƒ' */ + 0x7c, /* 01111100 */ + 0x82, /* 10000010 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 132 0x84 '„' */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 133 0x85 '…' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 134 0x86 '†' */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 135 0x87 '‡' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x7e, /* 01111110 */ + 0x0c, /* 00001100 */ + 0x38, /* 00111000 */ + + /* 136 0x88 'ˆ' */ + 0x7c, /* 01111100 */ + 0x82, /* 10000010 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 137 0x89 '‰' */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 138 0x8a 'Š' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 139 0x8b '‹' */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 140 0x8c 'Œ' */ + 0x7c, /* 01111100 */ + 0x82, /* 10000010 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 141 0x8d '' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 142 0x8e 'Ž' */ + 0xc6, /* 11000110 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 143 0x8f '' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 144 0x90 '' */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xf8, /* 11111000 */ + 0xc0, /* 11000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 145 0x91 '‘' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0xd8, /* 11011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 146 0x92 '’' */ + 0x3e, /* 00111110 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xfe, /* 11111110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xce, /* 11001110 */ + 0x00, /* 00000000 */ + + /* 147 0x93 '“' */ + 0x7c, /* 01111100 */ + 0x82, /* 10000010 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 148 0x94 '”' */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 149 0x95 '•' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 150 0x96 '–' */ + 0x78, /* 01111000 */ + 0x84, /* 10000100 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 151 0x97 '—' */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 152 0x98 '˜' */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0xfc, /* 11111100 */ + + /* 153 0x99 '™' */ + 0xc6, /* 11000110 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* 154 0x9a 'š' */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 155 0x9b '›' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 156 0x9c 'œ' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x64, /* 01100100 */ + 0xf0, /* 11110000 */ + 0x60, /* 01100000 */ + 0x66, /* 01100110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + + /* 157 0x9d '' */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 158 0x9e 'ž' */ + 0xf8, /* 11111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xfa, /* 11111010 */ + 0xc6, /* 11000110 */ + 0xcf, /* 11001111 */ + 0xc6, /* 11000110 */ + 0xc7, /* 11000111 */ + + /* 159 0x9f 'Ÿ' */ + 0x0e, /* 00001110 */ + 0x1b, /* 00011011 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + + /* 160 0xa0 ' ' */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 161 0xa1 '¡' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 162 0xa2 '¢' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 163 0xa3 '£' */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 164 0xa4 '¤' */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + + /* 165 0xa5 '¥' */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0xe6, /* 11100110 */ + 0xf6, /* 11110110 */ + 0xde, /* 11011110 */ + 0xce, /* 11001110 */ + 0x00, /* 00000000 */ + + /* 166 0xa6 '¦' */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x3e, /* 00111110 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 167 0xa7 '§' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 168 0xa8 '¨' */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x63, /* 01100011 */ + 0x3e, /* 00111110 */ + 0x00, /* 00000000 */ + + /* 169 0xa9 '©' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 170 0xaa 'ª' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 171 0xab '«' */ + 0x63, /* 01100011 */ + 0xe6, /* 11100110 */ + 0x6c, /* 01101100 */ + 0x7e, /* 01111110 */ + 0x33, /* 00110011 */ + 0x66, /* 01100110 */ + 0xcc, /* 11001100 */ + 0x0f, /* 00001111 */ + + /* 172 0xac '¬' */ + 0x63, /* 01100011 */ + 0xe6, /* 11100110 */ + 0x6c, /* 01101100 */ + 0x7a, /* 01111010 */ + 0x36, /* 00110110 */ + 0x6a, /* 01101010 */ + 0xdf, /* 11011111 */ + 0x06, /* 00000110 */ + + /* 173 0xad '' */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 174 0xae '®' */ + 0x00, /* 00000000 */ + 0x33, /* 00110011 */ + 0x66, /* 01100110 */ + 0xcc, /* 11001100 */ + 0x66, /* 01100110 */ + 0x33, /* 00110011 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 175 0xaf '¯' */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0x66, /* 01100110 */ + 0x33, /* 00110011 */ + 0x66, /* 01100110 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 176 0xb0 '°' */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + + /* 177 0xb1 '±' */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + + /* 178 0xb2 '²' */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + + /* 179 0xb3 '³' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 180 0xb4 '´' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 181 0xb5 'µ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 182 0xb6 '¶' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 183 0xb7 '·' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 184 0xb8 '¸' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 185 0xb9 '¹' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x06, /* 00000110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 186 0xba 'º' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 187 0xbb '»' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x06, /* 00000110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 188 0xbc '¼' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x06, /* 00000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 189 0xbd '½' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 190 0xbe '¾' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 191 0xbf '¿' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 192 0xc0 'À' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 193 0xc1 'Á' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 194 0xc2 'Â' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 195 0xc3 'Ã' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 196 0xc4 'Ä' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 197 0xc5 'Å' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 198 0xc6 'Æ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 199 0xc7 'Ç' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 200 0xc8 'È' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x30, /* 00110000 */ + 0x3f, /* 00111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 201 0xc9 'É' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x30, /* 00110000 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 202 0xca 'Ê' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf7, /* 11110111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 203 0xcb 'Ë' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xf7, /* 11110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 204 0xcc 'Ì' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x30, /* 00110000 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 205 0xcd 'Í' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 206 0xce 'Î' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf7, /* 11110111 */ + 0x00, /* 00000000 */ + 0xf7, /* 11110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 207 0xcf 'Ï' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 208 0xd0 'Ð' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 209 0xd1 'Ñ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 210 0xd2 'Ò' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 211 0xd3 'Ó' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x3f, /* 00111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 212 0xd4 'Ô' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 213 0xd5 'Õ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 214 0xd6 'Ö' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 215 0xd7 '×' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xff, /* 11111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 216 0xd8 'Ø' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 217 0xd9 'Ù' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 218 0xda 'Ú' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 219 0xdb 'Û' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 220 0xdc 'Ü' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 221 0xdd 'Ý' */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + + /* 222 0xde 'Þ' */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + + /* 223 0xdf 'ß' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 224 0xe0 'à' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0xc8, /* 11001000 */ + 0xdc, /* 11011100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 225 0xe1 'á' */ + 0x78, /* 01111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xd8, /* 11011000 */ + 0xcc, /* 11001100 */ + 0xc6, /* 11000110 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + + /* 226 0xe2 'â' */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + + /* 227 0xe3 'ã' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + + /* 228 0xe4 'ä' */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 229 0xe5 'å' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + + /* 230 0xe6 'æ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0xc0, /* 11000000 */ + + /* 231 0xe7 'ç' */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 232 0xe8 'è' */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + + /* 233 0xe9 'é' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* 234 0xea 'ê' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xee, /* 11101110 */ + 0x00, /* 00000000 */ + + /* 235 0xeb 'ë' */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x3e, /* 00111110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 236 0xec 'ì' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 237 0xed 'í' */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x7e, /* 01111110 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7e, /* 01111110 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + + /* 238 0xee 'î' */ + 0x1e, /* 00011110 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x7e, /* 01111110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x1e, /* 00011110 */ + 0x00, /* 00000000 */ + + /* 239 0xef 'ï' */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 240 0xf0 'ð' */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 241 0xf1 'ñ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 242 0xf2 'ò' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 243 0xf3 'ó' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 244 0xf4 'ô' */ + 0x0e, /* 00001110 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 245 0xf5 'õ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + + /* 246 0xf6 'ö' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 247 0xf7 '÷' */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 248 0xf8 'ø' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 249 0xf9 'ù' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 250 0xfa 'ú' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 251 0xfb 'û' */ + 0x0f, /* 00001111 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0xec, /* 11101100 */ + 0x6c, /* 01101100 */ + 0x3c, /* 00111100 */ + 0x1c, /* 00011100 */ + + /* 252 0xfc 'ü' */ + 0x6c, /* 01101100 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 253 0xfd 'ý' */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 254 0xfe 'þ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 255 0xff 'ÿ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + +}; diff --git a/examples/spi_oled/funconfig.h b/examples/spi_oled/funconfig.h new file mode 100644 index 0000000000000000000000000000000000000000..998cf76fede1479f162c72ef34d3a4d747430093 --- /dev/null +++ b/examples/spi_oled/funconfig.h @@ -0,0 +1,7 @@ +#ifndef _FUNCONFIG_H +#define _FUNCONFIG_H + +#define CH32V003 1 + +#endif + diff --git a/examples/spi_oled/ssd1306.h b/examples/spi_oled/ssd1306.h new file mode 100644 index 0000000000000000000000000000000000000000..ea985d8d8a0108004beb495107b3c95570eb5b13 --- /dev/null +++ b/examples/spi_oled/ssd1306.h @@ -0,0 +1,663 @@ +/* + * Single-File-Header for using SPI OLED + * 05-05-2023 E. Brombaugh + */ + +#ifndef _SSD1306_H +#define _SSD1306_H + +#include <stdint.h> +#include <string.h> +#include "font_8x8.h" + +// comfortable packet size for this OLED +#define SSD1306_PSZ 32 + +// characteristics of each type +#if !defined (SSD1306_64X32) && !defined (SSD1306_128X32) && !defined (SSD1306_128X64) + #error "Please define the SSD1306_WXH resolution used in your application" +#endif + +#ifdef SSD1306_64X32 +#define SSD1306_W 64 +#define SSD1306_H 32 +#define SSD1306_FULLUSE +#define SSD1306_OFFSET 32 +#endif + +#ifdef SSD1306_128X32 +#define SSD1306_W 128 +#define SSD1306_H 32 +#define SSD1306_OFFSET 0 +#endif + +#ifdef SSD1306_128X64 +#define SSD1306_W 128 +#define SSD1306_H 64 +#define SSD1306_FULLUSE +#define SSD1306_OFFSET 0 +#endif + +/* + * send OLED command byte + */ +uint8_t ssd1306_cmd(uint8_t cmd) +{ + ssd1306_pkt_send(&cmd, 1, 1); + return 0; +} + +/* + * send OLED data packet (up to 32 bytes) + */ +uint8_t ssd1306_data(uint8_t *data, uint8_t sz) +{ + ssd1306_pkt_send(data, sz, 0); + return 0; +} + +#define SSD1306_SETCONTRAST 0x81 +#define SSD1306_SEGREMAP 0xA0 +#define SSD1306_DISPLAYALLON_RESUME 0xA4 +#define SSD1306_DISPLAYALLON 0xA5 +#define SSD1306_NORMALDISPLAY 0xA6 +#define SSD1306_INVERTDISPLAY 0xA7 +#define SSD1306_DISPLAYOFF 0xAE +#define SSD1306_DISPLAYON 0xAF +#define SSD1306_SETDISPLAYOFFSET 0xD3 +#define SSD1306_SETCOMPINS 0xDA +#define SSD1306_SETVCOMDETECT 0xDB +#define SSD1306_SETDISPLAYCLOCKDIV 0xD5 +#define SSD1306_SETPRECHARGE 0xD9 +#define SSD1306_SETMULTIPLEX 0xA8 +#define SSD1306_SETLOWCOLUMN 0x00 +#define SSD1306_SETHIGHCOLUMN 0x10 +#define SSD1306_SETSTARTLINE 0x40 +#define SSD1306_MEMORYMODE 0x20 +#define SSD1306_COLUMNADDR 0x21 +#define SSD1306_PAGEADDR 0x22 +#define SSD1306_COMSCANINC 0xC0 +#define SSD1306_COMSCANDEC 0xC8 +#define SSD1306_CHARGEPUMP 0x8D +#define SSD1306_EXTERNALVCC 0x1 +#define SSD1306_SWITCHCAPVCC 0x2 +#define SSD1306_TERMINATE_CMDS 0xFF + +/* choose VCC mode */ +#define SSD1306_EXTERNALVCC 0x1 +#define SSD1306_SWITCHCAPVCC 0x2 +//#define vccstate SSD1306_EXTERNALVCC +#define vccstate SSD1306_SWITCHCAPVCC + +// OLED initialization commands for 128x32 +const uint8_t ssd1306_init_array[] = +{ + SSD1306_DISPLAYOFF, // 0xAE + SSD1306_SETDISPLAYCLOCKDIV, // 0xD5 + 0x80, // the suggested ratio 0x80 + SSD1306_SETMULTIPLEX, // 0xA8 +#ifdef SSD1306_64X32 + 0x1F, // for 64-wide displays +#else + 0x3F, // for 128-wide displays +#endif + SSD1306_SETDISPLAYOFFSET, // 0xD3 + 0x00, // no offset + SSD1306_SETSTARTLINE | 0x0, // 0x40 | line + SSD1306_CHARGEPUMP, // 0x8D + 0x14, // enable? + SSD1306_MEMORYMODE, // 0x20 + 0x00, // 0x0 act like ks0108 + SSD1306_SEGREMAP | 0x1, // 0xA0 | bit + SSD1306_COMSCANDEC, + SSD1306_SETCOMPINS, // 0xDA + 0x12, // + SSD1306_SETCONTRAST, // 0x81 + 0x8F, + SSD1306_SETPRECHARGE, // 0xd9 + 0xF1, + SSD1306_SETVCOMDETECT, // 0xDB + 0x40, + SSD1306_DISPLAYALLON_RESUME, // 0xA4 + SSD1306_NORMALDISPLAY, // 0xA6 + SSD1306_DISPLAYON, // 0xAF --turn on oled panel + SSD1306_TERMINATE_CMDS // 0xFF --fake command to mark end +}; + +// the display buffer +uint8_t ssd1306_buffer[SSD1306_W*SSD1306_H/8]; + +/* + * set the buffer to a color + */ +void ssd1306_setbuf(uint8_t color) +{ + memset(ssd1306_buffer, color ? 0xFF : 0x00, sizeof(ssd1306_buffer)); +} + +#ifndef SSD1306_FULLUSE +/* + * expansion array for OLED with every other row unused + */ +const uint8_t expand[16] = +{ + 0x00,0x02,0x08,0x0a, + 0x20,0x22,0x28,0x2a, + 0x80,0x82,0x88,0x8a, + 0xa0,0xa2,0xa8,0xaa, +}; +#endif + +/* + * Send the frame buffer + */ +void ssd1306_refresh(void) +{ + uint16_t i; + + ssd1306_cmd(SSD1306_COLUMNADDR); + ssd1306_cmd(SSD1306_OFFSET); // Column start address (0 = reset) + ssd1306_cmd(SSD1306_OFFSET+SSD1306_W-1); // Column end address (127 = reset) + + ssd1306_cmd(SSD1306_PAGEADDR); + ssd1306_cmd(0); // Page start address (0 = reset) + ssd1306_cmd(7); // Page end address + +#ifdef SSD1306_FULLUSE + /* for fully used rows just plow thru everything */ + for(i=0;i<sizeof(ssd1306_buffer);i+=SSD1306_PSZ) + { + /* send PSZ block of data */ + ssd1306_data(&ssd1306_buffer[i], SSD1306_PSZ); + } +#else + /* for displays with odd rows unused expand bytes */ + uint8_t tbuf[SSD1306_PSZ], j, k; + for(i=0;i<sizeof(ssd1306_buffer);i+=128) + { + /* low nybble */ + for(j=0;j<128;j+=SSD1306_PSZ) + { + for(k=0;k<SSD1306_PSZ;k++) + tbuf[k] = expand[ssd1306_buffer[i+j+k]&0xf]; + + /* send PSZ block of data */ + ssd1306_data(tbuf, SSD1306_PSZ); + } + + /* high nybble */ + for(j=0;j<128;j+=SSD1306_PSZ) + { + for(k=0;k<SSD1306_PSZ;k++) + tbuf[k] = expand[(ssd1306_buffer[i+j+k]>>4)&0xf]; + + /* send PSZ block of data */ + ssd1306_data(tbuf, SSD1306_PSZ); + } + } +#endif +} + +/* + * plot a pixel in the buffer + */ +void ssd1306_drawPixel(uint8_t x, uint8_t y, uint8_t color) +{ + uint16_t addr; + + /* clip */ + if(x >= SSD1306_W) + return; + if(y >= SSD1306_H) + return; + + /* compute buffer address */ + addr = x + SSD1306_W*(y/8); + + /* set/clear bit in buffer */ + if(color) + ssd1306_buffer[addr] |= (1<<(y&7)); + else + ssd1306_buffer[addr] &= ~(1<<(y&7)); +} + +/* + * plot a pixel in the buffer + */ +void ssd1306_xorPixel(uint8_t x, uint8_t y) +{ + uint16_t addr; + + /* clip */ + if(x >= SSD1306_W) + return; + if(y >= SSD1306_H) + return; + + /* compute buffer address */ + addr = x + SSD1306_W*(y/8); + + ssd1306_buffer[addr] ^= (1<<(y&7)); +} + +/* + * draw a an image from an array, directly into to the display buffer + * the color modes allow for overwriting and even layering (sprites!) + */ +void ssd1306_drawImage(uint8_t x, uint8_t y, const unsigned char* input, uint8_t width, uint8_t height, uint8_t color_mode) { + uint8_t x_absolute; + uint8_t y_absolute; + uint8_t pixel; + uint8_t bytes_to_draw = width / 8; + uint16_t buffer_addr; + + for (uint8_t line = 0; line < height; line++) { + y_absolute = y + line; + if (y_absolute >= SSD1306_H) { + break; + } + + // SSD1306 is in vertical mode, yet we want to draw horizontally, which necessitates assembling the output bytes from the input data + // bitmask for current pixel in vertical (output) byte + uint8_t v_mask = 1 << (y_absolute & 7); + + for (uint8_t byte = 0; byte < bytes_to_draw; byte++) { + uint8_t input_byte = input[byte + line * bytes_to_draw]; + + for (pixel = 0; pixel < 8; pixel++) { + x_absolute = x + 8 * (bytes_to_draw - byte) + pixel; + if (x_absolute >= SSD1306_W) { + break; + } + // looking at the horizontal display, we're drawing bytes bottom to top, not left to right, hence y / 8 + buffer_addr = x_absolute + SSD1306_W * (y_absolute / 8); + // state of current pixel + uint8_t input_pixel = input_byte & (1 << pixel); + + switch (color_mode) { + case 0: + // write pixels as they are + ssd1306_buffer[buffer_addr] = (ssd1306_buffer[buffer_addr] & ~v_mask) | (input_pixel ? v_mask : 0); + break; + case 1: + // write pixels after inversion + ssd1306_buffer[buffer_addr] = (ssd1306_buffer[buffer_addr] & ~v_mask) | (!input_pixel ? v_mask : 0); + break; + case 2: + // 0 clears pixel + ssd1306_buffer[buffer_addr] &= input_pixel ? 0xFF : ~v_mask; + break; + case 3: + // 1 sets pixel + ssd1306_buffer[buffer_addr] |= input_pixel ? v_mask : 0; + break; + case 4: + // 0 sets pixel + ssd1306_buffer[buffer_addr] |= !input_pixel ? v_mask : 0; + break; + case 5: + // 1 clears pixel + ssd1306_buffer[buffer_addr] &= input_pixel ? ~v_mask : 0xFF; + break; + } + } + #if SSD1306_LOG_IMAGE == 1 + printf("%02x ", input_byte); + #endif + } + #if SSD1306_LOG_IMAGE == 1 + printf("\n\r"); + #endif + } +} + +/* + * fast vert line + */ +void ssd1306_drawFastVLine(uint8_t x, uint8_t y, uint8_t h, uint8_t color) +{ + // clipping + if((x >= SSD1306_W) || (y >= SSD1306_H)) return; + if((y+h-1) >= SSD1306_H) h = SSD1306_H-y; + while(h--) + { + ssd1306_drawPixel(x, y++, color); + } +} + +/* + * fast horiz line + */ +void ssd1306_drawFastHLine(uint8_t x, uint8_t y, uint8_t w, uint8_t color) +{ + // clipping + if((x >= SSD1306_W) || (y >= SSD1306_H)) return; + if((x+w-1) >= SSD1306_W) w = SSD1306_W-x; + + while (w--) + { + ssd1306_drawPixel(x++, y, color); + } +} + +/* + * abs() helper function for line drawing + */ +int16_t gfx_abs(int16_t x) +{ + return (x<0) ? -x : x; +} + +/* + * swap() helper function for line drawing + */ +void gfx_swap(uint16_t *z0, uint16_t *z1) +{ + uint16_t temp = *z0; + *z0 = *z1; + *z1 = temp; +} + +/* + * Bresenham line draw routine swiped from Wikipedia + */ +void ssd1306_drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t color) +{ + int16_t steep; + int16_t deltax, deltay, error, ystep, x, y; + + /* flip sense 45deg to keep error calc in range */ + steep = (gfx_abs(y1 - y0) > gfx_abs(x1 - x0)); + + if(steep) + { + gfx_swap(&x0, &y0); + gfx_swap(&x1, &y1); + } + + /* run low->high */ + if(x0 > x1) + { + gfx_swap(&x0, &x1); + gfx_swap(&y0, &y1); + } + + /* set up loop initial conditions */ + deltax = x1 - x0; + deltay = gfx_abs(y1 - y0); + error = deltax/2; + y = y0; + if(y0 < y1) + ystep = 1; + else + ystep = -1; + + /* loop x */ + for(x=x0;x<=x1;x++) + { + /* plot point */ + if(steep) + /* flip point & plot */ + ssd1306_drawPixel(y, x, color); + else + /* just plot */ + ssd1306_drawPixel(x, y, color); + + /* update error */ + error = error - deltay; + + /* update y */ + if(error < 0) + { + y = y + ystep; + error = error + deltax; + } + } +} + +/* + * draws a circle + */ +void ssd1306_drawCircle(int16_t x, int16_t y, int16_t radius, int8_t color) +{ + /* Bresenham algorithm */ + int16_t x_pos = -radius; + int16_t y_pos = 0; + int16_t err = 2 - 2 * radius; + int16_t e2; + + do { + ssd1306_drawPixel(x - x_pos, y + y_pos, color); + ssd1306_drawPixel(x + x_pos, y + y_pos, color); + ssd1306_drawPixel(x + x_pos, y - y_pos, color); + ssd1306_drawPixel(x - x_pos, y - y_pos, color); + e2 = err; + if (e2 <= y_pos) { + err += ++y_pos * 2 + 1; + if(-x_pos == y_pos && e2 <= x_pos) { + e2 = 0; + } + } + if (e2 > x_pos) { + err += ++x_pos * 2 + 1; + } + } while (x_pos <= 0); +} + +/* + * draws a filled circle + */ +void ssd1306_fillCircle(int16_t x, int16_t y, int16_t radius, int8_t color) +{ + /* Bresenham algorithm */ + int16_t x_pos = -radius; + int16_t y_pos = 0; + int16_t err = 2 - 2 * radius; + int16_t e2; + + do { + ssd1306_drawPixel(x - x_pos, y + y_pos, color); + ssd1306_drawPixel(x + x_pos, y + y_pos, color); + ssd1306_drawPixel(x + x_pos, y - y_pos, color); + ssd1306_drawPixel(x - x_pos, y - y_pos, color); + ssd1306_drawFastHLine(x + x_pos, y + y_pos, 2 * (-x_pos) + 1, color); + ssd1306_drawFastHLine(x + x_pos, y - y_pos, 2 * (-x_pos) + 1, color); + e2 = err; + if (e2 <= y_pos) { + err += ++y_pos * 2 + 1; + if(-x_pos == y_pos && e2 <= x_pos) { + e2 = 0; + } + } + if(e2 > x_pos) { + err += ++x_pos * 2 + 1; + } + } while(x_pos <= 0); +} + +/* + * draw a rectangle + */ +void ssd1306_drawRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color) +{ + ssd1306_drawFastVLine(x, y, h, color); + ssd1306_drawFastVLine(x+w-1, y, h, color); + ssd1306_drawFastHLine(x, y, w, color); + ssd1306_drawFastHLine(x, y+h-1, w, color); +} + +/* + * fill a rectangle + */ +void ssd1306_fillRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color) +{ + uint8_t m, n=y, iw = w; + + /* scan vertical */ + while(h--) + { + m=x; + w=iw; + /* scan horizontal */ + while(w--) + { + /* invert pixels */ + ssd1306_drawPixel(m++, n, color); + } + n++; + } +} + +/* + * invert a rectangle in the buffer + */ +void ssd1306_xorrect(uint8_t x, uint8_t y, uint8_t w, uint8_t h) +{ + uint8_t m, n=y, iw = w; + + /* scan vertical */ + while(h--) + { + m=x; + w=iw; + /* scan horizontal */ + while(w--) + { + /* invert pixels */ + ssd1306_xorPixel(m++, n); + } + n++; + } +} + +/* + * Draw character to the display buffer + */ +void ssd1306_drawchar(uint8_t x, uint8_t y, uint8_t chr, uint8_t color) +{ + uint16_t i, j, col; + uint8_t d; + + for(i=0;i<8;i++) + { + d = fontdata[(chr<<3)+i]; + for(j=0;j<8;j++) + { + if(d&0x80) + col = color; + else + col = (~color)&1; + + ssd1306_drawPixel(x+j, y+i, col); + + // next bit + d <<= 1; + } + } +} + +/* + * draw a string to the display + */ +void ssd1306_drawstr(uint8_t x, uint8_t y, char *str, uint8_t color) +{ + uint8_t c; + + while((c=*str++)) + { + ssd1306_drawchar(x, y, c, color); + x += 8; + if(x>120) + break; + } +} + +/* + * enum for font size + */ +typedef enum { + fontsize_8x8 = 1, + fontsize_16x16 = 2, + fontsize_32x32 = 4, + fontsize_64x64 = 8, +} font_size_t; + +/* + * Draw character to the display buffer, scaled to size + */ +void ssd1306_drawchar_sz(uint8_t x, uint8_t y, uint8_t chr, uint8_t color, font_size_t font_size) +{ + uint16_t i, j, col; + uint8_t d; + + // Determine the font scale factor based on the font_size parameter + uint8_t font_scale = (uint8_t)font_size; + + // Loop through each row of the font data + for (i = 0; i < 8; i++) + { + // Retrieve the font data for the current row + d = fontdata[(chr << 3) + i]; + + // Loop through each column of the font data + for (j = 0; j < 8; j++) + { + // Determine the color to draw based on the current bit in the font data + if (d & 0x80) + col = color; + else + col = (~color) & 1; + + // Draw the pixel at the original size and scaled size using nested for-loops + for (uint8_t k = 0; k < font_scale; k++) { + for (uint8_t l = 0; l < font_scale; l++) { + ssd1306_drawPixel(x + (j * font_scale) + k, y + (i * font_scale) + l, col); + } + } + + // Move to the next bit in the font data + d <<= 1; + } + } +} + +/* + * draw a string to the display buffer, scaled to size + */ +void ssd1306_drawstr_sz(uint8_t x, uint8_t y, char *str, uint8_t color, font_size_t font_size) +{ + uint8_t c; + + while((c=*str++)) + { + ssd1306_drawchar_sz(x, y, c, color, font_size); + x += 8 * font_size; + if(x>128 - 8 * font_size) + break; + } +} + +/* + * initialize I2C and OLED + */ +uint8_t ssd1306_init(void) +{ + // pulse reset + ssd1306_rst(); + + // initialize OLED + uint8_t *cmd_list = (uint8_t *)ssd1306_init_array; + while(*cmd_list != SSD1306_TERMINATE_CMDS) + { + if(ssd1306_cmd(*cmd_list++)) + return 1; + } + + // clear display + ssd1306_setbuf(0); + ssd1306_refresh(); + + return 0; +} + +#endif diff --git a/examples/standby_autowake/README.md b/examples/standby_autowake/README.md index 205a20afd4985de0e516aefff821d97d109e04d1..2fe3d123f79270038312627ab679fc4e3442c57e 100644 --- a/examples/standby_autowake/README.md +++ b/examples/standby_autowake/README.md @@ -1,11 +1,12 @@ # the deepest slumber +**WARNING: You MUST hard-reboot the CH32V003 to allow it to go into deep sleep. You cannot go from flashing to deep sleep without a hard power cycle.** + This example serves to show how to put the CH32V003 into its lowest power state (standby) and have it wake periodically. Power consumption should be around 10uA. -The MCU only toggles the LED and prints a message, then it goes to sleep. -The LED staying on demonstrates that GPIO keeps its state even when the rest of the mcu is in a coma. +Refer to the standby_btn example for GPIO settings. Based on the groundwork of Marek M. diff --git a/examples/standby_autowake/standby_autowake.c b/examples/standby_autowake/standby_autowake.c index 4cfaaf34c1248cd27cac5e82a3cef6d78d754a5e..9e41489a4b595d10fec04f28871180dc49fffa92 100644 --- a/examples/standby_autowake/standby_autowake.c +++ b/examples/standby_autowake/standby_autowake.c @@ -3,30 +3,56 @@ #include "ch32v003fun.h" #include <stdio.h> -/* somehow this ISR won't get called?? -void AWU_IRQHandler( void ) __attribute__((interrupt)); -void AWU_IRQHandler( void ) { - GPIOD->OUTDR ^= (1 << 4); -} -*/ int main() { SystemInit(); - Delay_Ms(100); - printf("\r\n\r\nlow power example\r\n\r\n"); + // This delay gives us some time to reprogram the device. + // Otherwise if the device enters standby mode we can't + // program it any more. + Delay_Ms(5000); - RCC->APB2PCENR |= RCC_APB2Periph_GPIOD; - // GPIO D4 Push-Pull - GPIOD->CFGLR &= ~(0xf<<(4*4)); - GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*4); - GPIOD->OUTDR |= (1 << 4); + printf("\r\n\r\nlow power example\r\n\r\n"); - // give the user time to open the terminal connection - //Delay_Ms(5000); - printf("5000ms wait over\r\n"); + // Set all GPIOs to input pull up + RCC->APB2PCENR |= RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD; + // GPIOA: Set to output + GPIOA->CFGLR = (GPIO_CNF_IN_PUPD<<(4*2)) | + (GPIO_CNF_IN_PUPD<<(4*1)); + GPIOA->BSHR = GPIO_BSHR_BS2 | GPIO_BSHR_BR1; + GPIOC->CFGLR = (GPIO_CNF_IN_PUPD<<(4*7)) | + (GPIO_CNF_IN_PUPD<<(4*6)) | + (GPIO_CNF_IN_PUPD<<(4*5)) | + (GPIO_CNF_IN_PUPD<<(4*4)) | + (GPIO_CNF_IN_PUPD<<(4*3)) | + (GPIO_CNF_IN_PUPD<<(4*2)) | + (GPIO_CNF_IN_PUPD<<(4*1)) | + (GPIO_CNF_IN_PUPD<<(4*0)); + GPIOC->BSHR = GPIO_BSHR_BS7 | + GPIO_BSHR_BS6 | + GPIO_BSHR_BS5 | + GPIO_BSHR_BS4 | + GPIO_BSHR_BS3 | + GPIO_BSHR_BS2 | + GPIO_BSHR_BS1 | + GPIO_BSHR_BS0; + GPIOD->CFGLR = (GPIO_CNF_IN_PUPD<<(4*7)) | + (GPIO_CNF_IN_PUPD<<(4*6)) | + (GPIO_CNF_IN_PUPD<<(4*5)) | + (GPIO_CNF_IN_PUPD<<(4*4)) | + (GPIO_CNF_IN_PUPD<<(4*3)) | + (GPIO_CNF_IN_PUPD<<(4*2)) | + (GPIO_CNF_IN_PUPD<<(4*0)); + GPIOD->BSHR = GPIO_BSHR_BS7 | + GPIO_BSHR_BS6 | + GPIO_BSHR_BS5 | + GPIO_BSHR_BS4 | + GPIO_BSHR_BS3 | + GPIO_BSHR_BS2 | + GPIO_BSHR_BS0; + // enable power interface module clock RCC->APB1PCENR |= RCC_APB1Periph_PWR; @@ -62,6 +88,5 @@ int main() // restore clock to full speed SystemInit(); printf("\r\nawake, %u\r\n", counter++); - GPIOD->OUTDR ^= (1 << 4); } } diff --git a/examples/standby_btn/README.md b/examples/standby_btn/README.md index 794e8b4ccc30b278904c12658a303c200170b380..4b6421c99cef584df8d4a0f91fde4b1418dbe453 100644 --- a/examples/standby_btn/README.md +++ b/examples/standby_btn/README.md @@ -1,15 +1,30 @@ # the deepest slumber +**WARNING: You MUST hard-reboot the CH32V003 to allow it to go into deep sleep. You cannot go from flashing to deep sleep without a hard power cycle.** + This example serves to show how to put the CH32V003 into its lowest power state (standby) and have it wake with a button press. -Power consumption should be around 10uA. +Power consumption should be around 9uA. + +To enter 10uA standby mode you must perform these steps: + +1. GPIOs other than the wake up pin can be set to either input or output mode (see notes). +2. Set GPIO(s) for wake up to input mode with appropriate pull up/down. +3. Enable AFIO clock and set AFIO_EXTICR to the wakeup channel. +4. Configure EXTI event. +5. Set PWR_CTLR_PDDS bit in PWR_CTLR (Setting PWREN in RCC_APB1PCENR is not required hum?) +6. Set SLEEPDEEP bit in PFIC_SCTLR +7. Call __WFE() to enter standby mode. -The MCU only toggles the LED and prints a message, then it goes back to sleep. -The LED staying on demonstrates that GPIO keeps its state even when the rest of the mcu is in a coma. +Note: +* GPIOs in output mode will retain state during standby. +* GPIO if set to input mode must have internal or external pulling resistor. Floating input pin will cause 100uA standby current. +* Once CH32V003 enters standby mode, it won't respond to any SWDIO command, therefor cannot be reprogrammed. User must provide a way to have the processor stay awake for reprogramming, e.g. some delay at startup. +* Debug circuitry will consume power. If minichlink terminal is active (including immediately after flashing), standby current will stay around 1.2mA until power cycle. Based on the groundwork of Marek M. ## circuit -Connect LED to PD4 (with resistor), connect button to GND and PD2. +Connect button to GND and PD2. There is no debouncing but it should suffice for waking the chip. diff --git a/examples/standby_btn/standby_btn.c b/examples/standby_btn/standby_btn.c index 33f55a89cb8c29e7ae7991cb01c0fac41ed183cb..be6c99b7f109db9e04af80dea2d94618a5aac4d8 100644 --- a/examples/standby_btn/standby_btn.c +++ b/examples/standby_btn/standby_btn.c @@ -3,40 +3,60 @@ #include "ch32v003fun.h" #include <stdio.h> -void EXTI7_0_IRQHandler( void ) __attribute__((interrupt)); -void EXTI7_0_IRQHandler( void ) { - //GPIOD->OUTDR ^= (1 << 4); -} - - int main() { SystemInit(); - Delay_Ms(100); + + // This delay gives us some time to reprogram the device. + // Otherwise if the device enters standby mode we can't + // program it any more. + Delay_Ms(5000); printf("\n\nlow power example\n\n"); + RCC->APB2PCENR |= RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD; + // GPIOA: Set to output + GPIOA->CFGLR = ((GPIO_CNF_OUT_PP | GPIO_Speed_2MHz)<<(4*2)) | + ((GPIO_CNF_OUT_PP | GPIO_Speed_2MHz)<<(4*1)); + GPIOA->BSHR = GPIO_BSHR_BS2 | GPIO_BSHR_BR1; + // GPIOC: Set to input with mixed pull-up / pull-down + GPIOC->CFGLR = (GPIO_CNF_IN_PUPD<<(4*7)) | + (GPIO_CNF_IN_PUPD<<(4*6)) | + (GPIO_CNF_IN_PUPD<<(4*5)) | + (GPIO_CNF_IN_PUPD<<(4*4)) | + (GPIO_CNF_IN_PUPD<<(4*3)) | + (GPIO_CNF_IN_PUPD<<(4*2)) | + (GPIO_CNF_IN_PUPD<<(4*1)) | + (GPIO_CNF_IN_PUPD<<(4*0)); + GPIOC->BSHR = GPIO_BSHR_BS7 | + GPIO_BSHR_BR6 | + GPIO_BSHR_BS5 | + GPIO_BSHR_BR4 | + GPIO_BSHR_BS3 | + GPIO_BSHR_BR2 | + GPIO_BSHR_BS1 | + GPIO_BSHR_BR0; + // GPIOD: D2 set to input pull-up + GPIOD->CFGLR = (GPIO_CNF_IN_PUPD<<(4*7)) | + (GPIO_CNF_IN_PUPD<<(4*6)) | + (GPIO_CNF_IN_PUPD<<(4*5)) | + (GPIO_CNF_IN_PUPD<<(4*4)) | + (GPIO_CNF_IN_PUPD<<(4*3)) | + (GPIO_CNF_IN_PUPD<<(4*2)) | + (GPIO_CNF_IN_PUPD<<(4*0)); + GPIOD->BSHR = GPIO_BSHR_BR7 | + GPIO_BSHR_BS6 | + GPIO_BSHR_BR5 | + GPIO_BSHR_BS4 | + GPIO_BSHR_BR3 | + GPIO_BSHR_BS2 | + GPIO_BSHR_BR0; - RCC->APB2PCENR |= RCC_APB2Periph_GPIOD; - // GPIO D4 Push-Pull - GPIOD->CFGLR &= ~(0xf<<(4*4)); - GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*4); - GPIOD->OUTDR |= (1 << 4); - - // give the user time to open the terminal connection - //Delay_Ms(5000); - //printf("5000ms wait over\r\n"); - - // enable alternate IO function module clock + // AFIO is needed for EXTI RCC->APB2PCENR |= RCC_AFIOEN; - // configure button on PD2 as input, pullup - GPIOD->CFGLR &= ~(0xf<<(2*4)); - GPIOD->CFGLR |= (GPIO_CNF_IN_PUPD)<<(2*4); - GPIOD->BSHR = (1 << 2); - // assign pin 2 interrupt from portD (0b11) to EXTI channel 2 - AFIO->EXTICR |= (uint32_t)(0b11 << (2 * 2)); + AFIO->EXTICR |= (uint32_t)(0b11 << (2*2)); // enable line2 interrupt event EXTI->EVENR |= EXTI_Line2; @@ -56,6 +76,6 @@ int main() // restore clock to full speed SystemInit(); printf("\nawake, %u\n", counter++); - GPIOD->OUTDR ^= (1 << 4); + Delay_Ms(5000); // wake and reflash can happen here } } diff --git a/extralibs/ch32v003_SPI.h b/extralibs/ch32v003_SPI.h index 0da599a3e1d0ee764de40930ce69002f07e94820..69a7e6a0edbb914651a4c4369da60b18b7242305 100644 --- a/extralibs/ch32v003_SPI.h +++ b/extralibs/ch32v003_SPI.h @@ -75,7 +75,7 @@ static inline void SPI_write_16(uint16_t data); // send a command and get a response from the SPI device // you'll use this for most devices static inline uint8_t SPI_transfer_8(uint8_t data); -static inline uint8_t SPI_transfer_16(uint16_t data); +static inline uint16_t SPI_transfer_16(uint16_t data); // SPI peripheral power enable / disable (default off, init() automatically enables) // send SPI peripheral to sleep @@ -238,15 +238,15 @@ static inline void SPI_init() { } static inline void SPI_begin_8() { - SPI1->CTLR1 |= SPI_DataSize_8b; // DFF 16bit data-length enable, writable only when SPE is 0 - SPI1->CTLR1 |= CTLR1_SPE_Set; + SPI1->CTLR1 &= ~(SPI_CTLR1_DFF); // DFF 16bit data-length enable, writable only when SPE is 0 + SPI1->CTLR1 |= SPI_CTLR1_SPE; } static inline void SPI_begin_16() { - SPI1->CTLR1 |= SPI_DataSize_16b; // DFF 16bit data-length enable, writable only when SPE is 0 - SPI1->CTLR1 |= CTLR1_SPE_Set; + SPI1->CTLR1 |= SPI_CTLR1_DFF; // DFF 16bit data-length enable, writable only when SPE is 0 + SPI1->CTLR1 |= SPI_CTLR1_SPE; } static inline void SPI_end() { - SPI1->CTLR1 &= CTLR1_SPE_Reset; + SPI1->CTLR1 &= ~(SPI_CTLR1_SPE); } #if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) @@ -290,7 +290,7 @@ static inline uint8_t SPI_transfer_8(uint8_t data) { #endif return SPI_read_8(); } -static inline uint8_t SPI_transfer_16(uint16_t data) { +static inline uint16_t SPI_transfer_16(uint16_t data) { #if defined(CH32V003_SPI_NSS_SOFTWARE_PC3) || defined(CH32V003_SPI_NSS_SOFTWARE_PC4) SPI_NSS_software_high(); #endif @@ -333,7 +333,13 @@ static inline uint8_t SPI_is_RX_empty() { static inline void SPI_wait_RX_available() { while(!(SPI1->STATR & SPI_STATR_RXNE)) {} } - +static inline void SPI_wait_not_busy() { + while((SPI1->STATR & SPI_STATR_BSY) != 0) {} +} +static inline void SPI_wait_transmit_finished() { + SPI_wait_TX_complete(); + SPI_wait_not_busy(); +} //######## implementation block diff --git a/extralibs/ch32v003_touch.h b/extralibs/ch32v003_touch.h new file mode 100644 index 0000000000000000000000000000000000000000..88756afe48cb5464275262f1f06a84c613af72ef --- /dev/null +++ b/extralibs/ch32v003_touch.h @@ -0,0 +1,201 @@ +#ifndef _CH32V003_TOUCH_H +#define _CH32V003_TOUCH_H + +/** ADC-based Capactive Touch Control. + + see cap_touch_adc.c for an example. + + // Enable GPIOD, C and ADC + RCC->APB2PCENR |= RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOC | RCC_APB2Periph_ADC1; + InitTouchADC(); + + + // Then do this any time you want to read some touches. + sum[0] += ReadTouchPin( GPIOA, 2, 0, iterations ); + sum[1] += ReadTouchPin( GPIOA, 1, 1, iterations ); + sum[2] += ReadTouchPin( GPIOC, 4, 2, iterations ); + sum[3] += ReadTouchPin( GPIOD, 2, 3, iterations ); + sum[4] += ReadTouchPin( GPIOD, 3, 4, iterations ); + sum[5] += ReadTouchPin( GPIOD, 5, 5, iterations ); + sum[6] += ReadTouchPin( GPIOD, 6, 6, iterations ); + sum[7] += ReadTouchPin( GPIOD, 4, 7, iterations ); +*/ + + + +#define TOUCH_ADC_SAMPLE_TIME 2 // Tricky: Don't change this without a lot of experimentation. + +// Can either be 0 or 1. +// If 0: Measurement low and rises high. So more pressed is smaller number. +// If 1: Higher number = harder press. Good to pair with TOUCH_FLAT. +// If you are doing more prox, use mode 0, otherwise, use mode 1. +#define TOUCH_SLOPE 1 + +// If you set this to 1, it will glitch the line, so it will only read +// anything reasonable if the capacitance can overcome that initial spike. +// Typically, it seems if you use this you probbly don't need to do +// any pre-use calibration. +#define TOUCH_FLAT 0 + +static void InitTouchADC( ); +void InitTouchADC( ) +{ + // ADCCLK = 24 MHz => RCC_ADCPRE = 0: divide sys clock by 2 + RCC->CFGR0 &= ~(0x1F<<11); + + // Set up single conversion on chl 2 + ADC1->RSQR1 = 0; + ADC1->RSQR2 = 0; + + // turn on ADC and set rule group to sw trig + ADC1->CTLR2 |= ADC_ADON | ADC_EXTSEL; + + // Reset calibration + ADC1->CTLR2 |= ADC_RSTCAL; + while(ADC1->CTLR2 & ADC_RSTCAL); + + // Calibrate + ADC1->CTLR2 |= ADC_CAL; + while(ADC1->CTLR2 & ADC_CAL); +} + +// Run from RAM to get even more stable timing. +// This function call takes about 8.1uS to execute. +static uint32_t ReadTouchPin( GPIO_TypeDef * io, int portpin, int adcno, int iterations ) __attribute__((noinline, section(".srodata"))); +uint32_t ReadTouchPin( GPIO_TypeDef * io, int portpin, int adcno, int iterations ) +{ + uint32_t ret = 0; + + ADC1->RSQR3 = adcno; + ADC1->SAMPTR2 = TOUCH_ADC_SAMPLE_TIME<<(3*adcno); + + uint32_t CFGBASE = io->CFGLR & (~(0xf<<(4*portpin))); + uint32_t CFGFLOAT = ((GPIO_CFGLR_IN_PUPD)<<(4*portpin)) | CFGBASE; + uint32_t CFGDRIVE = (GPIO_CFGLR_OUT_2Mhz_PP)<<(4*portpin) | CFGBASE; + + // If we run multiple times with slightly different wait times, we can + // reduce the impact of the ADC's DNL. + + +#if TOUCH_FLAT == 1 +#define RELEASEIO io->BSHR = 1<<(portpin+16*TOUCH_SLOPE); io->CFGLR = CFGFLOAT; +#else +#define RELEASEIO io->CFGLR = CFGFLOAT; io->BSHR = 1<<(portpin+16*TOUCH_SLOPE); +#endif + +#define INNER_LOOP( n ) \ + { \ + /* Only lock IRQ for a very narrow window. */ \ + __disable_irq(); \ + \ + /* Tricky - we start the ADC BEFORE we transition the pin. By doing \ + this We are catching it onthe slope much more effectively. */ \ + ADC1->CTLR2 = ADC_SWSTART | ADC_ADON | ADC_EXTSEL; \ + \ + ADD_N_NOPS( n ) \ + \ + RELEASEIO \ + \ + /* Sampling actually starts here, somewhere, so we can let other \ + interrupts run */ \ + __enable_irq(); \ + while(!(ADC1->STATR & ADC_EOC)); \ + io->CFGLR = CFGDRIVE; \ + io->BSHR = 1<<(portpin+(16*(1-TOUCH_SLOPE))); \ + ret += ADC1->RDATAR; \ + } + + int i; + for( i = 0; i < iterations; i++ ) + { + // Wait a variable amount of time based on loop iteration, in order + // to get a variety of RC points and minimize DNL. + + INNER_LOOP( 0 ); + INNER_LOOP( 2 ); + INNER_LOOP( 4 ); + } + + return ret; +} + +// Run from RAM to get even more stable timing. +// This function call takes about 8.1uS to execute. +static uint32_t ReadTouchPinSafe( GPIO_TypeDef * io, int portpin, int adcno, int iterations ) __attribute__((noinline, section(".srodata"))); +uint32_t ReadTouchPinSafe( GPIO_TypeDef * io, int portpin, int adcno, int iterations ) +{ + uint32_t ret = 0; + + ADC1->RSQR3 = adcno; + ADC1->SAMPTR2 = TOUCH_ADC_SAMPLE_TIME<<(3*adcno); + + // If we run multiple times with slightly different wait times, we can + // reduce the impact of the ADC's DNL. + +#define INNER_LOOP_SAFE( n ) \ + { \ + /* Only lock IRQ for a very narrow window. */ \ + __disable_irq(); \ + \ + \ + /* Tricky - we start the ADC BEFORE we transition the pin. By doing \ + this We are catching it onthe slope much more effectively. */ \ + ADC1->CTLR2 = ADC_SWSTART | ADC_ADON | ADC_EXTSEL; \ + \ + ADD_N_NOPS( n ) \ + \ + io->CFGLR = ((GPIO_CFGLR_IN_PUPD)<<(4*portpin)) | (io->CFGLR & (~(0xf<<(4*portpin)))); \ + io->BSHR = 1<<(portpin+16*TOUCH_SLOPE); \ + \ + /* Sampling actually starts here, somewhere, so we can let other \ + interrupts run */ \ + __enable_irq(); \ + while(!(ADC1->STATR & ADC_EOC)); \ + __disable_irq(); \ + io->CFGLR = (GPIO_CFGLR_OUT_2Mhz_PP)<<(4*portpin) | (io->CFGLR & (~(0xf<<(4*portpin)))); \ + __enable_irq(); \ + io->BSHR = 1<<(portpin+(16*(1-TOUCH_SLOPE))); \ + ret += ADC1->RDATAR; \ + } + + int i; + for( i = 0; i < iterations; i++ ) + { + // Wait a variable amount of time based on loop iteration, in order + // to get a variety of RC points and minimize DNL. + + INNER_LOOP_SAFE( 0 ); + INNER_LOOP_SAFE( 2 ); + INNER_LOOP_SAFE( 4 ); + } + + return ret; +} + + +#endif + +/* + * MIT License + * + * Copyright (c) 2023 Valve Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + diff --git a/minichlink/99-minichlink.rules b/minichlink/99-minichlink.rules index 8cca6989adad49aceff2f035b04e8024dc76446e..9fa431775dad75e8aeb8b9155987136e879090cd 100644 --- a/minichlink/99-minichlink.rules +++ b/minichlink/99-minichlink.rules @@ -1,4 +1,6 @@ SUBSYSTEM=="usb", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="8010", GROUP="plugdev", MODE="0660" SUBSYSTEM=="usb", ATTRS{idVendor}=="303a", ATTRS{idProduct}=="4004", GROUP="plugdev", MODE="0660" KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="303a", ATTRS{idProduct}=="4004", GROUP="plugdev", MODE="0660" - +# rv003usb bootloader +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="b003", GROUP="plugdev", MODE="0660" +#KERNEL=="hiddev*", SUBSYSTEM=="usbmisc", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="b003", GROUP="plugdev", MODE="0660" diff --git a/minichlink/minichlink.c b/minichlink/minichlink.c index 3c610a48db6f2952b8e646e79fabc6eb790d0d89..dc897e69cc1d02c2def7bdc97f7a09ee740f0dfd 100644 --- a/minichlink/minichlink.c +++ b/minichlink/minichlink.c @@ -114,7 +114,7 @@ int main( int argc, char ** argv ) if( i < argc ) hints.serial_port = argv[i]; } - else if( strncmp( v, "-c", 2 ) == 0 ) + else if( strncmp( v, "-C", 2 ) == 0 ) { i++; if( i < argc ) @@ -649,6 +649,7 @@ help: fprintf( stderr, " -t Disable 3.3V\n" ); fprintf( stderr, " -f Disable 5V\n" ); fprintf( stderr, " -c [serial port for Ardulink, try /dev/ttyACM0 or COM11 etc]\n" ); + fprintf( stderr, " -C [specified programmer, eg. b003boot, ardulink, esp32s2chfun]\n" ); fprintf( stderr, " -u Clear all code flash - by power off (also can unbrick)\n" ); fprintf( stderr, " -E Erase chip\n" ); fprintf( stderr, " -b Reboot out of Halt\n" ); diff --git a/platformio.ini b/platformio.ini index b20948c308573b4c6a792953fa544b65c6b559c0..9f25b5b0b0706c3094c8314c6e701d73b4bf5cab 100644 --- a/platformio.ini +++ b/platformio.ini @@ -26,32 +26,125 @@ extra_libs_srcs = +<extralibs> ; 2. Add build_src_filter with fun base files + example folder (+ extra libraries if used) for source files ; 3. Add additional build flags as needed (see uartdemo) ; 4. Switch to new environment in VSCode bottom taskbar (https://docs.platformio.org/en/latest/integration/ide/vscode.html#project-tasks) +[env:adc_dma_opamp] +build_src_filter = ${fun_base.build_src_filter} +<examples/adc_dma_opamp> + +[env:adc_fixed_fs] +build_src_filter = ${fun_base.build_src_filter} +<examples/adc_fixed_fs> + +[env:adc_polled] +build_src_filter = ${fun_base.build_src_filter} +<examples/adc_polled> + [env:blink] build_src_filter = ${fun_base.build_src_filter} +<examples/blink> [env:bootload] build_src_filter = ${fun_base.build_src_filter} +<examples/bootload> +[env:cap_touch_adc] +build_src_filter = ${fun_base.build_src_filter} +<examples/cap_touch_adc> + +[env:cap_touch_exti] +build_src_filter = ${fun_base.build_src_filter} +<examples/cap_touch_exti> + +[env:cpp_virtual_methods] +build_src_filter = ${fun_base.build_src_filter} +<examples/cpp_virtual_methods> + [env:debugprintfdemo] build_src_filter = ${fun_base.build_src_filter} +<examples/debugprintfdemo> +[env:direct_gpio] +build_src_filter = ${fun_base.build_src_filter} +<examples/direct_gpio> + +[env:dma_gpio] +build_src_filter = ${fun_base.build_src_filter} +<examples/dma_gpio> + [env:external_crystal] build_src_filter = ${fun_base.build_src_filter} +<examples/external_crystal> +[env:exti_pin_change_isr] +build_src_filter = ${fun_base.build_src_filter} +<examples/exti_pin_change_isr> + +[env:flashtest] +build_src_filter = ${fun_base.build_src_filter} +<examples/flashtest> + [env:GPIO] build_src_filter = ${fun_base.build_src_filter} ${fun_base.extra_libs_srcs} +<examples/GPIO> +[env:i2c_oled] +build_src_filter = ${fun_base.build_src_filter} +<examples/i2c_oled> + +[env:i2c_slave] +build_src_filter = ${fun_base.build_src_filter} +<examples/i2c_slave> + +[env:input_capture] +build_src_filter = ${fun_base.build_src_filter} +<examples/input_capture> + +[env:iwdg] +build_src_filter = ${fun_base.build_src_filter} +<examples/iwdg> + +[env:hsitrim] +build_src_filter = ${fun_base.build_src_filter} +<examples/hsitrim> + +[env:MCOtest] +build_src_filter = ${fun_base.build_src_filter} +<examples/MCOtest> + [env:optionbytes] build_src_filter = ${fun_base.build_src_filter} +<examples/optionbytes> +[env:optiondata] +build_src_filter = ${fun_base.build_src_filter} +<examples/optiondata> + [env:run_from_ram] build_src_filter = ${fun_base.build_src_filter} +<examples/run_from_ram> +[env:self_modify_code] +build_src_filter = ${fun_base.build_src_filter} +<examples/self_modify_code> + +[env:spi_24L01_rx] +build_src_filter = ${fun_base.build_src_filter} +<examples/spi_24L01_rx> + +[env:spi_24L01_tx] +build_src_filter = ${fun_base.build_src_filter} +<examples/spi_24L01_tx> + +[env:spi_dac] +build_src_filter = ${fun_base.build_src_filter} +<examples/spi_dac> + +[env:spi_oled] +build_src_filter = ${fun_base.build_src_filter} ${fun_base.extra_libs_srcs} +<examples/spi_oled> + +[env:standby_autowake] +build_src_filter = ${fun_base.build_src_filter} +<examples/standby_autowake> + +[env:standby_btn] +build_src_filter = ${fun_base.build_src_filter} +<examples/standby_btn> + +[env:struct_direct_gpio] +build_src_filter = ${fun_base.build_src_filter} +<examples/struct_direct_gpio> + +[env:struct_gpio] +build_src_filter = ${fun_base.build_src_filter} +<examples/struct_gpio> + +[env:sysclk_config] +build_src_filter = ${fun_base.build_src_filter} +<examples/sysclk_config> + +[env:systick_irq] +build_src_filter = ${fun_base.build_src_filter} +<examples/systick_irq> + [env:template] build_src_filter = ${fun_base.build_src_filter} +<examples/template> -[env:self_modify_code] -build_src_filter = ${fun_base.build_src_filter} +<examples/self_modify_code> +[env:tim1_pwm] +build_src_filter = ${fun_base.build_src_filter} +<examples/tim1_pwm> + +[env:tim2_encoder] +build_src_filter = ${fun_base.build_src_filter} +<examples/tim2_encoder> + +[env:tim2_pwm] +build_src_filter = ${fun_base.build_src_filter} +<examples/tim2_pwm> + +[env:tim2_pwm_remap] +build_src_filter = ${fun_base.build_src_filter} +<examples/tim2_pwm_remap> [env:uartdemo] build_flags = ${fun_base.build_flags} -DSTDOUT_UART