Created
December 27, 2023 23:17
-
-
Save inajob/eea0d77adb8807662e5c3bd0a80da697 to your computer and use it in GitHub Desktop.
pianoboard.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 2023-12-28 inajob | |
* Copyright (c) 2023 inajob | |
This software is released under the MIT License. | |
http://opensource.org/licenses/mit-license.php | |
This is based on https://github.com/cnlohr/ch32v003fun/tree/master/examples/i2c_oled | |
*/ | |
// what type of OLED - uncomment just one | |
//#define SSD1306_64X32 | |
//#define SSD1306_128X32 | |
#define SSD1306_128X64 | |
#include "ch32v003fun.h" | |
#include "ch32v003_GPIO_branchless.h" | |
#include <stdio.h> | |
#include "ssd1306_i2c.h" | |
#include "ssd1306.h" | |
#include "bomb.h" | |
#define TIM2_DEFAULT 0xff | |
#define SYSTICK_SR_CNTIF (1<<0) | |
#define SYSTICK_CTLR_STE (1<<0) | |
#define SYSTICK_CTLR_STIE (1<<1) | |
#define SYSTICK_CTLR_STCLK (1<<2) | |
#define SYSTICK_CTLR_STRE (1<<3) | |
#define SYSTICK_CTLR_SWIE (1<<31) | |
volatile uint32_t systick_cnt; | |
volatile uint32_t add = 0; | |
volatile uint32_t osc = 0; // 0 -> 255*8 | |
volatile uint32_t delta = 0; | |
volatile uint32_t osc2 = 0; // 0 -> 255*8 | |
volatile uint32_t delta2 = 0; | |
volatile uint32_t osc3 = 0; // 0 -> 255*8 | |
volatile uint32_t delta3 = 0; | |
uint32_t tones[] = {81, 92, 97, 109, 122, 130, 146}; | |
/* | |
* Start up the SysTick IRQ | |
*/ | |
void systick_init(void) | |
{ | |
/* disable default SysTick behavior */ | |
SysTick->CTLR = 0; | |
/* enable the SysTick IRQ */ | |
NVIC_EnableIRQ(SysTicK_IRQn); | |
/* Set the tick interval to 1ms for normal op */ | |
SysTick->CMP = (FUNCONF_SYSTEM_CORE_CLOCK/1000)-1; | |
/* Start at zero */ | |
SysTick->CNT = 0; | |
systick_cnt = 0; | |
/* Enable SysTick counter, IRQ, HCLK/1 */ | |
SysTick->CTLR = SYSTICK_CTLR_STE | SYSTICK_CTLR_STIE | | |
SYSTICK_CTLR_STCLK; | |
} | |
/* | |
* SysTick ISR just counts ticks | |
* note - the __attribute__((interrupt)) syntax is crucial! | |
*/ | |
void SysTick_Handler(void) __attribute__((interrupt)); | |
void SysTick_Handler(void) | |
{ | |
// move the compare further ahead in time. | |
// as a warning, if more than this length of time | |
// passes before triggering, you may miss your | |
// interrupt. | |
SysTick->CMP += (FUNCONF_SYSTEM_CORE_CLOCK/(11000)); | |
/* clear IRQ */ | |
SysTick->SR = 0; | |
/* update counter */ | |
systick_cnt+=add; | |
osc += delta; | |
osc2 += delta2; | |
osc3 += delta3; | |
t2pwm_setpw(0, systick_cnt%255); // CH1 | |
//t2pwm_setpw(1, (((osc>>3)&255 + (osc2>>3)&255) + (osc3>>3)&255) >> 1); // CH2 180° out-of-phase | |
t2pwm_setpw(1, ((((osc>>3&255))<128?0:255) + (((osc2>>3&255))<128?0:255) + (((osc3>>3)&255)<128?0:255)) >> 1); // CH2 180° out-of-phase | |
} | |
/* | |
* initialize TIM2 for PWM | |
*/ | |
void t2pwm_init( void ) | |
{ | |
// Enable GPIOD and TIM2 | |
RCC->APB2PCENR |= RCC_APB2Periph_GPIOD; | |
RCC->APB1PCENR |= RCC_APB1Periph_TIM2; | |
// PD4 is T2CH1, 10MHz Output alt func, push-pull | |
// GPIOD->CFGLR &= ~(0xf<<(4*4)); | |
// GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*4); | |
// PD3 is T2CH2, 10MHz Output alt func, push-pull | |
GPIOD->CFGLR &= ~(0xf<<(4*3)); | |
GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*3); | |
// Reset TIM2 to init all regs | |
RCC->APB1PRSTR |= RCC_APB1Periph_TIM2; | |
RCC->APB1PRSTR &= ~RCC_APB1Periph_TIM2; | |
// SMCFGR: default clk input is CK_INT | |
// set TIM2 clock prescaler divider | |
TIM2->PSC = 0x0000; | |
// set PWM total cycle width | |
TIM2->ATRLR = 255; | |
// for channel 1 and 2, let CCxS stay 00 (output), set OCxM to 110 (PWM I) | |
// enabling preload causes the new pulse width in compare capture register only to come into effect when UG bit in SWEVGR is set (= initiate update) (auto-clears) | |
TIM2->CHCTLR1 |= TIM_OC1M_2 | TIM_OC1M_1 | TIM_OC1PE | TIM_OC2M_2 | TIM_OC2M_1 | TIM_OC2PE; | |
// CTLR1: default is up, events generated, edge align | |
// enable auto-reload of preload | |
TIM2->CTLR1 |= TIM_ARPE; | |
// Enable Channel outputs, set default state (based on TIM2_DEFAULT) | |
TIM2->CCER |= TIM_CC1E | (TIM_CC1P & TIM2_DEFAULT); | |
TIM2->CCER |= TIM_CC2E | (TIM_CC2P & TIM2_DEFAULT); | |
// initialize counter | |
TIM2->SWEVGR |= TIM_UG; | |
// Enable TIM2 | |
TIM2->CTLR1 |= TIM_CEN; | |
} | |
/* | |
* set timer channel PW | |
*/ | |
void t2pwm_setpw(uint8_t chl, uint16_t width) | |
{ | |
switch(chl&3) | |
{ | |
case 0: TIM2->CH1CVR = width; break; | |
case 1: TIM2->CH2CVR = width; break; | |
} | |
//TIM2->SWEVGR |= TIM_UG; // load new value in compare capture register | |
} | |
// function prototype (declaration), definition in "ch32v003fun.c" | |
int mini_snprintf(char* buffer, unsigned int buffer_len, const char *fmt, ...); | |
int main() | |
{ | |
// 48MHz internal clock | |
SystemInit(); | |
systick_init(); | |
t2pwm_init(); | |
Delay_Ms( 100 ); | |
printf("\r\r\n\ni2c_oled example\n\r"); | |
// init i2c and oled | |
Delay_Ms( 100 ); // give OLED some more time | |
printf("initializing i2c oled..."); | |
if(!ssd1306_i2c_init()) | |
{ | |
ssd1306_init(); | |
printf("done.\n\r"); | |
// Setup GPIO | |
GPIO_port_enable(GPIO_port_C); | |
GPIO_port_enable(GPIO_port_D); | |
// Input Pull Up | |
GPIO_pinMode(GPIOv_from_PORT_PIN(GPIO_port_C, 0), GPIO_pinMode_I_pullUp, GPIO_Speed_In); | |
GPIO_pinMode(GPIOv_from_PORT_PIN(GPIO_port_D, 0), GPIO_pinMode_I_pullUp, GPIO_Speed_In); | |
GPIO_pinMode(GPIOv_from_PORT_PIN(GPIO_port_D, 2), GPIO_pinMode_I_pullUp, GPIO_Speed_In); | |
GPIO_pinMode(GPIOv_from_PORT_PIN(GPIO_port_D, 4), GPIO_pinMode_I_pullUp, GPIO_Speed_In); | |
// Output | |
GPIO_pinMode(GPIOv_from_PORT_PIN(GPIO_port_C, 3), GPIO_pinMode_O_pushPull, GPIO_Speed_10MHz); | |
GPIO_pinMode(GPIOv_from_PORT_PIN(GPIO_port_C, 4), GPIO_pinMode_O_pushPull, GPIO_Speed_10MHz); | |
GPIO_pinMode(GPIOv_from_PORT_PIN(GPIO_port_C, 5), GPIO_pinMode_O_pushPull, GPIO_Speed_10MHz); | |
GPIO_pinMode(GPIOv_from_PORT_PIN(GPIO_port_C, 6), GPIO_pinMode_O_pushPull, GPIO_Speed_10MHz); | |
GPIO_pinMode(GPIOv_from_PORT_PIN(GPIO_port_C, 7), GPIO_pinMode_O_pushPull, GPIO_Speed_10MHz); | |
GPIO_digitalWrite(GPIOv_from_PORT_PIN(GPIO_port_C, 3), high); | |
GPIO_digitalWrite(GPIOv_from_PORT_PIN(GPIO_port_C, 4), high); | |
GPIO_digitalWrite(GPIOv_from_PORT_PIN(GPIO_port_C, 5), high); | |
GPIO_digitalWrite(GPIOv_from_PORT_PIN(GPIO_port_C, 6), high); | |
GPIO_digitalWrite(GPIOv_from_PORT_PIN(GPIO_port_C, 7), high); | |
char buf[128]; | |
char buf2[128]; | |
int buttons[20]; | |
int count = 0; | |
while(1) | |
{ | |
GPIO_digitalWrite(GPIOv_from_PORT_PIN(GPIO_port_C, 3), low); | |
buttons[0] = !GPIO_digitalRead(GPIOv_from_PORT_PIN(GPIO_port_D, 2)); | |
buttons[1] = !GPIO_digitalRead(GPIOv_from_PORT_PIN(GPIO_port_D, 4)); | |
buttons[2] = !GPIO_digitalRead(GPIOv_from_PORT_PIN(GPIO_port_D, 0)); | |
buttons[3] = !GPIO_digitalRead(GPIOv_from_PORT_PIN(GPIO_port_C, 0)); | |
GPIO_digitalWrite(GPIOv_from_PORT_PIN(GPIO_port_C, 3), high); | |
GPIO_digitalWrite(GPIOv_from_PORT_PIN(GPIO_port_C, 4), low); | |
Delay_Ms(10); | |
buttons[4] = !GPIO_digitalRead(GPIOv_from_PORT_PIN(GPIO_port_D, 2)); | |
buttons[5] = !GPIO_digitalRead(GPIOv_from_PORT_PIN(GPIO_port_D, 4)); | |
buttons[7] = !GPIO_digitalRead(GPIOv_from_PORT_PIN(GPIO_port_C, 0)); //?! | |
buttons[6] = !GPIO_digitalRead(GPIOv_from_PORT_PIN(GPIO_port_D, 0)); //?! | |
GPIO_digitalWrite(GPIOv_from_PORT_PIN(GPIO_port_C, 4), high); | |
GPIO_digitalWrite(GPIOv_from_PORT_PIN(GPIO_port_C, 5), low); | |
Delay_Ms(10); | |
buttons[8] = !GPIO_digitalRead(GPIOv_from_PORT_PIN(GPIO_port_D, 2)); | |
buttons[9] = !GPIO_digitalRead(GPIOv_from_PORT_PIN(GPIO_port_D, 4)); | |
buttons[10] = !GPIO_digitalRead(GPIOv_from_PORT_PIN(GPIO_port_D, 0)); | |
buttons[11] = !GPIO_digitalRead(GPIOv_from_PORT_PIN(GPIO_port_C, 0)); | |
GPIO_digitalWrite(GPIOv_from_PORT_PIN(GPIO_port_C, 5), high); | |
GPIO_digitalWrite(GPIOv_from_PORT_PIN(GPIO_port_C, 6), low); | |
Delay_Ms(10); | |
buttons[12] = !GPIO_digitalRead(GPIOv_from_PORT_PIN(GPIO_port_D, 2)); | |
buttons[13] = !GPIO_digitalRead(GPIOv_from_PORT_PIN(GPIO_port_D, 4)); | |
buttons[14] = !GPIO_digitalRead(GPIOv_from_PORT_PIN(GPIO_port_D, 0)); | |
buttons[15] = !GPIO_digitalRead(GPIOv_from_PORT_PIN(GPIO_port_C, 0)); | |
GPIO_digitalWrite(GPIOv_from_PORT_PIN(GPIO_port_C, 6), high); | |
GPIO_digitalWrite(GPIOv_from_PORT_PIN(GPIO_port_C, 7), low); | |
Delay_Ms(10); | |
buttons[16] = !GPIO_digitalRead(GPIOv_from_PORT_PIN(GPIO_port_D, 2)); | |
buttons[17] = !GPIO_digitalRead(GPIOv_from_PORT_PIN(GPIO_port_D, 4)); | |
buttons[18] = !GPIO_digitalRead(GPIOv_from_PORT_PIN(GPIO_port_D, 0)); | |
buttons[19] = !GPIO_digitalRead(GPIOv_from_PORT_PIN(GPIO_port_C, 0)); | |
GPIO_digitalWrite(GPIOv_from_PORT_PIN(GPIO_port_C, 7), high); | |
uint32_t pdelta[64]; | |
int index = 0; | |
pdelta[0] = pdelta[1] = pdelta[2] = 0; | |
if(buttons[4]){pdelta[index++] = tones[2];} // do | |
if(buttons[6]){pdelta[index++] = tones[3];} | |
if(buttons[8]){pdelta[index++] = tones[4];} | |
if(buttons[9]){pdelta[index++] = tones[5];} | |
if(buttons[11]){pdelta[index++] = tones[6];} | |
if(buttons[13]){pdelta[index++] = tones[0] << 1;} | |
if(buttons[15]){pdelta[index++] = tones[1] << 1;} | |
if(buttons[16]){pdelta[index++] = tones[2] << 1;} | |
if(buttons[17]){pdelta[index++] = tones[3] << 1;} | |
delta = pdelta[0]; | |
delta2 = pdelta[1]; | |
delta3 = pdelta[2]; | |
// clear buffer | |
ssd1306_setbuf(0); | |
mini_snprintf(buf,128, "counter: %d", count); | |
ssd1306_drawstr(0,0, buf, 1); | |
for(int i = 0; i < 20; i ++){ | |
ssd1306_drawstr(i*5,8, buttons[i]?"#":"-", 1); | |
} | |
ssd1306_drawstr(0,16, "PianoBoard", 1); | |
ssd1306_drawstr(0,24, "CH32V003", 1); | |
ssd1306_refresh(); | |
Delay_Ms(10); | |
count ++; | |
} | |
} | |
else | |
printf("failed.\n\r"); | |
printf("Stuck here forever...\n\r"); | |
while(1); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment