-
Notifications
You must be signed in to change notification settings - Fork 204
Add support for CH32X035 architecture #324
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 18 commits
fe17896
98e9cc7
1c46911
3d6c5ea
ff7b782
ba78571
8c1a6e7
9ecdd09
aa967f1
04b3263
78ebf73
f7cf290
a034090
468b0ec
3e45eab
a621383
4d4f43a
d7a80c7
bf55f56
a0d1678
05570d5
0678911
5cda424
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,160 @@ | ||
| /* | ||
| * MozziGuts_impl_CH32.hpp | ||
| * | ||
| * Implementation for WCH CH32X035 (RISC-V) using Standard Peripheral Library (SPL) | ||
| * | ||
| */ | ||
|
|
||
| // Suppress "narrowing conversion" error for CH32 (RISC-V GCC is strict) | ||
| // This allows legacy Mozzi tables (declared as int8_t but containing uint8_t values) to compile. | ||
| #pragma GCC diagnostic ignored "-Wnarrowing" | ||
|
|
||
| #if !(IS_CH32()) | ||
| # error "Wrong implementation included for this platform" | ||
| #endif | ||
|
|
||
| #if !defined(CH32X035) | ||
| # error "This CH32 chip is not supported by Mozzi yet. Only CH32X035 is currently supported." | ||
| #endif | ||
|
|
||
| #include <Arduino.h> | ||
|
|
||
| // Forward declare the ISR to ensure C linkage | ||
| extern "C" void TIM1_UP_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); | ||
|
|
||
| namespace MozziPrivate { | ||
|
|
||
| #define MOZZI_AUDIO_PIN_1 PA6 // High Byte (Main) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would define these in the "config_checks_CH32" file, is order for both them to be user configurable (if they are not, it should be clearly said), and to be coherent with what is done for the other platforms.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I apologize for the prolonged absence and for not responding sooner. Have you already purchased the hardware (the CH32X035 development board)? |
||
| #define MOZZI_AUDIO_PIN_2 PA7 // Low Byte (Fine tune) for 2PIN_PWM | ||
|
|
||
| static void startAudio() { | ||
| // --- 1. Audio Interrupt Timer Setup (TIM1) --- | ||
| TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; | ||
| NVIC_InitTypeDef NVIC_InitStructure; | ||
|
|
||
| RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); | ||
|
|
||
| uint32_t period = (SystemCoreClock / MOZZI_AUDIO_RATE) - 1; | ||
|
|
||
| TIM_DeInit(TIM1); | ||
| TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); | ||
| TIM_TimeBaseStructure.TIM_Prescaler = 0; | ||
| TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; | ||
| TIM_TimeBaseStructure.TIM_Period = period; | ||
| TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; | ||
| TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; | ||
| TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); | ||
|
|
||
| TIM_ClearFlag(TIM1, TIM_FLAG_Update); | ||
| TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE); | ||
|
|
||
| NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn; | ||
| NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; | ||
| NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; | ||
| NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; | ||
| NVIC_Init(&NVIC_InitStructure); | ||
|
|
||
| TIM_Cmd(TIM1, ENABLE); | ||
| TIM_CtrlPWMOutputs(TIM1, ENABLE); | ||
|
|
||
| // --- 2. PWM Output Setup (TIM3) --- | ||
| GPIO_InitTypeDef GPIO_InitStructure; | ||
| TIM_OCInitTypeDef TIM_OCInitStructure; | ||
|
|
||
| // Enable Clocks | ||
| RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); | ||
| RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); | ||
|
|
||
| // Configure TIM3 Time Base | ||
| TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); | ||
| TIM_TimeBaseStructure.TIM_Period = 255; // 8-bit resolution | ||
| TIM_TimeBaseStructure.TIM_Prescaler = 0; | ||
| TIM_TimeBaseStructure.TIM_ClockDivision = 0; | ||
| TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; | ||
| TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); | ||
|
|
||
| // Common PWM Configuration | ||
| TIM_OCStructInit(&TIM_OCInitStructure); | ||
| TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; | ||
| TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; | ||
| TIM_OCInitStructure.TIM_Pulse = 128; | ||
| TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; | ||
|
|
||
| // --- Setup Pin 1 (PA6 / CH1) --- | ||
| GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; | ||
| GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; | ||
| GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; | ||
| GPIO_Init(GPIOA, &GPIO_InitStructure); | ||
|
|
||
| TIM_OC1Init(TIM3, &TIM_OCInitStructure); | ||
| TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); | ||
|
|
||
| #if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM) | ||
| // --- Setup Pin 2 (PA7 / CH2) for Hi-Fi --- | ||
| GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; | ||
| GPIO_Init(GPIOA, &GPIO_InitStructure); // Init PA7 as AF_PP too | ||
|
|
||
| TIM_OC2Init(TIM3, &TIM_OCInitStructure); // Init CH2 | ||
| TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); | ||
| #endif | ||
|
|
||
| TIM_ARRPreloadConfig(TIM3, ENABLE); | ||
|
|
||
| // Enable TIM3 | ||
| TIM_Cmd(TIM3, ENABLE); | ||
| TIM_CtrlPWMOutputs(TIM3, ENABLE); | ||
| } | ||
|
|
||
| void stopMozzi() { | ||
| TIM_Cmd(TIM1, DISABLE); | ||
| TIM_Cmd(TIM3, DISABLE); | ||
| } | ||
|
|
||
| void MozziRandPrivate::autoSeed() {} | ||
|
|
||
| #if MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_STANDARD) | ||
| void setupMozziADC(int8_t speed) {} | ||
| void setupFastAnalogRead(int8_t speed) {} | ||
| void adcStartConversion(uint8_t channel) {} | ||
| void startSecondADCReadOnCurrentChannel() {} | ||
| uint8_t adcPinToChannelNum(uint8_t pin) { return pin; } | ||
| #endif | ||
|
|
||
| // --- Audio Output Functions --- | ||
|
|
||
| #if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM) | ||
| inline void audioOutput(const AudioOutput f) { | ||
| long out = (long)f.l() + MOZZI_AUDIO_BIAS; | ||
| if (out < 0) out = 0; | ||
| out = out >> (MOZZI_AUDIO_BITS - 8); | ||
| if (out > 255) out = 255; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Idem
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again. You are right. |
||
| TIM_SetCompare1(TIM3, (uint16_t)out); | ||
| } | ||
|
|
||
| #elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM) | ||
| inline void audioOutput(const AudioOutput f) { | ||
| // 16-bit output split into two 8-bit channels | ||
| long out = (long)f.l() + MOZZI_AUDIO_BIAS; | ||
|
|
||
| // Clamp to 16-bit unsigned range just in case | ||
| if (out < 0) out = 0; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would think this is un-necessary: having a check for every sample will incur some drawbacks on the CPU. Other platforms don't do it so users are usually used to take care of overflows.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are right |
||
| if (out > 65535) out = 65535; | ||
|
|
||
| // High Byte -> CH1 (PA6) | ||
| TIM_SetCompare1(TIM3, (uint16_t)(out >> 8)); | ||
|
|
||
| // Low Byte -> CH2 (PA7) | ||
| TIM_SetCompare2(TIM3, (uint16_t)(out & 0xFF)); | ||
| } | ||
| #endif | ||
|
|
||
| } // namespace MozziPrivate | ||
|
|
||
| extern "C" void TIM1_UP_IRQHandler(void) { | ||
| if (TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET) { | ||
| TIM_ClearITPendingBit(TIM1, TIM_IT_Update); | ||
| #if (BYPASS_MOZZI_OUTPUT_BUFFER != true) | ||
| MozziPrivate::defaultAudioOutput(); | ||
| #endif | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add your name/pseudo here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There’s no need to include my pseudonym in the code or anywhere else. I just want to give back to the community that has given me so much (and while I’m at it, I’d like to say thank you for the Mozzi library and FixMath as well).
Asynchronous analog readings are definitely something that could improve this a lot, and I should implement them.
Right now, the port for this architecture works as it is, and I think it’s good enough for an initial release, but I’m definitely keeping this in mind.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of course that's up to you, but most of Mozzi's files display such information, which also shows that this is a collaborative effort and helps to track back when things were done and who might be the expert. A new port is always a good thing :).
As per the ADC, I fully agree that this should go in without waiting for async.