Skip to content
Snippets Groups Projects
Commit 828dac72 authored by cnlohr's avatar cnlohr
Browse files

Merge branch 'master' of https://github.com/cnlohr/ch32v003fun

parents 88803f74 93b972d5
No related branches found
No related tags found
No related merge requests found
all : flash
TARGET:=i2c_slave
include ../../ch32v003fun/ch32v003fun.mk
flash : cv_flash
clean : cv_clean
# I2C peripheral in slave mode
This library and example show how to use the I2C peripheral in slave mode.
The library uses a one-byte address, allowing for up to 256 registers to be defined.
The first byte written to the device within a transaction determines the offset for following reads and writes, emulating a simple EEPROM.
The example will turn on a LED connected to PD0 when the LSB of register 0 is set to 1 and off when it's set to 0.
#define SYSTEM_CORE_CLOCK 48000000
#include "ch32v003fun.h"
#include "i2c_slave.h"
#include <stdio.h>
// The I2C slave library uses a one byte address so you can extend the size of this array up to 256 registers
// note that the register set is modified by interrupts, to prevent the compiler from accidently optimizing stuff
// away make sure to declare the register array volatile
volatile uint8_t i2c_registers[32] = {0x00};
int main() {
SystemInit48HSI();
SetupDebugPrintf();
SetupI2CSlave(0x9, i2c_registers, sizeof(i2c_registers));
// Enable GPIOD and set pin 0 to output
RCC->APB2PCENR |= RCC_APB2Periph_GPIOD;
GPIOD->CFGLR &= ~(0xf<<(4*0));
GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*0);
while (1) {
if (i2c_registers[0] & 1) { // Turn on LED (PD0) if bit 1 of register 0 is set
GPIOD-> BSHR |= 1 << 16;
} else {
GPIOD-> BSHR |= 1;
}
}
}
/*
* Single-File-Header for using the I2C peripheral in slave mode
*
* MIT License
*
* Copyright (c) 2023 Renze Nicolai
*
* 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.
*/
#ifndef __I2C_SLAVE_H
#define __I2C_SLAVE_H
#include <stdint.h>
#define APB_CLOCK SYSTEM_CORE_CLOCK
struct _i2c_slave_state {
uint8_t first_write;
uint8_t offset;
uint8_t position;
volatile uint8_t* volatile registers;
uint8_t size;
} i2c_slave_state;
void SetupI2CSlave(uint8_t address, volatile uint8_t* registers, uint8_t size) {
i2c_slave_state.first_write = 1;
i2c_slave_state.offset = 0;
i2c_slave_state.position = 0;
i2c_slave_state.registers = registers;
i2c_slave_state.size = size;
// Enable GPIOC and I2C
RCC->APB2PCENR |= RCC_APB2Periph_GPIOC;
RCC->APB1PCENR |= RCC_APB1Periph_I2C1;
// PC1 is SDA, 10MHz Output, alt func, open-drain
GPIOC->CFGLR &= ~(0xf<<(4*1));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF)<<(4*1);
// PC2 is SCL, 10MHz Output, alt func, open-drain
GPIOC->CFGLR &= ~(0xf<<(4*2));
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF)<<(4*2);
// Reset I2C1 to init all regs
RCC->APB1PRSTR |= RCC_APB1Periph_I2C1;
RCC->APB1PRSTR &= ~RCC_APB1Periph_I2C1;
I2C1->CTLR1 |= I2C_CTLR1_SWRST;
I2C1->CTLR1 &= ~I2C_CTLR1_SWRST;
// Set module clock frequency
uint32_t prerate = 2000000; // I2C Logic clock rate, must be higher than the bus clock rate
I2C1->CTLR2 |= (APB_CLOCK/prerate) & I2C_CTLR2_FREQ;
// Enable interrupts
I2C1->CTLR2 |= I2C_CTLR2_ITBUFEN;
I2C1->CTLR2 |= I2C_CTLR2_ITEVTEN; // Event interrupt
I2C1->CTLR2 |= I2C_CTLR2_ITERREN; // Error interrupt
NVIC_EnableIRQ(I2C1_EV_IRQn); // Event interrupt
NVIC_SetPriority(I2C1_EV_IRQn, 2 << 4);
NVIC_EnableIRQ(I2C1_ER_IRQn); // Error interrupt
// Set clock configuration
uint32_t clockrate = 1000000; // I2C Bus clock rate, must be lower than the logic clock rate
I2C1->CKCFGR = ((APB_CLOCK/(3*clockrate))&I2C_CKCFGR_CCR) | I2C_CKCFGR_FS; // Fast mode 33% duty cycle
//I2C1->CKCFGR = ((APB_CLOCK/(25*clockrate))&I2C_CKCFGR_CCR) | I2C_CKCFGR_DUTY | I2C_CKCFGR_FS; // Fast mode 36% duty cycle
//I2C1->CKCFGR = (APB_CLOCK/(2*clockrate))&I2C_CKCFGR_CCR; // Standard mode good to 100kHz
// Set I2C address
I2C1->OADDR1 = address << 1;
// Enable I2C
I2C1->CTLR1 |= I2C_CTLR1_PE;
// Acknowledge the first address match event when it happens
I2C1->CTLR1 |= I2C_CTLR1_ACK;
}
void I2C1_EV_IRQHandler(void) __attribute__((interrupt));
void I2C1_EV_IRQHandler(void) {
uint16_t STAR1, STAR2 __attribute__((unused));
STAR1 = I2C1->STAR1;
STAR2 = I2C1->STAR2;
I2C1->CTLR1 |= I2C_CTLR1_ACK;
if (STAR1 & I2C_STAR1_ADDR) { // Start event
i2c_slave_state.first_write = 1; // Next write will be the offset
i2c_slave_state.position = i2c_slave_state.offset; // Reset position
}
if (STAR1 & I2C_STAR1_RXNE) { // Write event
if (i2c_slave_state.first_write) { // First byte written, set the offset
i2c_slave_state.offset = I2C1->DATAR;
i2c_slave_state.position = i2c_slave_state.offset;
i2c_slave_state.first_write = 0;
} else { // Normal register write
if (i2c_slave_state.position < i2c_slave_state.size) {
i2c_slave_state.registers[i2c_slave_state.position] = I2C1->DATAR;
i2c_slave_state.position++;
}
}
}
if (STAR1 & I2C_STAR1_TXE) { // Read event
if (i2c_slave_state.position < i2c_slave_state.size) {
I2C1->DATAR = i2c_slave_state.registers[i2c_slave_state.position];
i2c_slave_state.position++;
} else {
I2C1->DATAR = 0x00;
}
}
}
void I2C1_ER_IRQHandler(void) __attribute__((interrupt));
void I2C1_ER_IRQHandler(void) {
uint16_t STAR1 = I2C1->STAR1;
if (STAR1 & I2C_STAR1_BERR) { // Bus error
I2C1->STAR1 &= ~(I2C_STAR1_BERR); // Clear error
}
if (STAR1 & I2C_STAR1_ARLO) { // Arbitration lost error
I2C1->STAR1 &= ~(I2C_STAR1_ARLO); // Clear error
}
if (STAR1 & I2C_STAR1_AF) { // Acknowledge failure
I2C1->STAR1 &= ~(I2C_STAR1_AF); // Clear error
}
}
#endif
file tim1_pwm.elf
target extended-remote localhost:3333
all : flash
TARGET:=tim2_pwm
CFLAGS+=-DSTDOUT_UART
include ../../ch32v003fun/ch32v003fun.mk
flash : cv_flash
clean : cv_clean
# Demonstration of PWM using Timer 2
This example shows how to set up Timer 2 (the General Purpose Timer) to generate Pulse-Width Modulation (PWM) on two output pins.
PWM is frequently used to do variable brightness on LEDs or for digital-to-analog conversion when combined with a suitable lowpass filter.
## Use
Connect GPIO pins PD04 and PD3 to LEDs (with appropriate current limiting) and observe that they fade at the same speed, but phase-rotatd by 180°.
#!/bin/bash
# before running this you should start OOCD server
#../../../MRS_Toolchain_Linux_x64_V1.70/OpenOCD/bin/openocd -f ../../../MRS_Toolchain_Linux_x64_V1.70/OpenOCD/bin/wch-riscv.cfg
../../../MRS_Toolchain_Linux_x64_V1.70/RISC-V\ Embedded\ GCC/bin/riscv-none-embed-gdb
/*
* Example for using Advanced Control Timer (TIM2) for PWM generation
* 03-28-2023 E. Brombaugh
* 05-29-2023 recallmenot adapted from Timer1 to Timer2
*/
/*
Timer 2 pin mappings by AFIO->PCFR1
00 (default)
D4 T2CH1ETR
D3 T2CH2
C0 T2CH3
D7 T2CH4
01
C5 T2CH1ETR_
C2 T2CH2_
D2 T2CH3_
C1 T2CH4_
10
C1 T2CH1ETR_
D3 T2CH2
C0 T2CH3
D7 T2CH4
11
C1 T2CH1ETR_
C7 T2CH2_
D6 T2CH3_
D5 T2CH4_
*/
// Could be defined here, or in the processor defines.
#define SYSTEM_CORE_CLOCK 48000000
#define APB_CLOCK SYSTEM_CORE_CLOCK
#include "ch32v003fun.h"
#include <stdio.h>
/*
* 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 CH1 output, positive pol
TIM2->CCER |= TIM_CC1E | TIM_CC1P;
// Enable CH2 output, positive pol
TIM2->CCER |= TIM_CC2E | TIM_CC2P;
// 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;
case 2: TIM2->CH3CVR = width; break;
case 3: TIM2->CH4CVR = width; break;
}
TIM2->SWEVGR |= TIM_UG; // load new value in compare capture register
}
/*
* entry
*/
int main()
{
uint32_t count = 0;
SystemInit48HSI();
// start serial @ default 115200bps
SetupUART( UART_BRR );
Delay_Ms( 100 );
printf("\r\r\n\ntim2_pwm example\n\r");
// init TIM2 for PWM
printf("initializing tim2...");
t2pwm_init();
printf("done.\n\r");
printf("looping...\n\r");
while(1)
{
t2pwm_setpw(0, count); // Chl 1
t2pwm_setpw(1, (count + 128)&255); // Chl 2 180° out-of-phase
count++;
count &= 255;
Delay_Ms( 5 );
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment