Skip to content

Commit 1c4fe54

Browse files
ArcaneNibbledlech
authored andcommitted
pbio/drv/sound/sound_ev3: Implement beep-only driver
1 parent 0856963 commit 1c4fe54

File tree

1 file changed

+114
-1
lines changed

1 file changed

+114
-1
lines changed

lib/pbio/drv/sound/sound_ev3.c

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,131 @@
55

66
#if PBDRV_CONFIG_SOUND_EV3
77

8+
#include <math.h>
89
#include <stdint.h>
910

10-
void pbdrv_sound_stop() {
11+
#include <pbdrv/gpio.h>
12+
13+
#include <tiam1808/ehrpwm.h>
14+
#include <tiam1808/hw/soc_AM1808.h>
15+
#include <tiam1808/hw/hw_syscfg0_AM1808.h>
16+
#include <tiam1808/hw/hw_types.h>
17+
#include <tiam1808/psc.h>
18+
19+
#include "../drv/gpio/gpio_ev3.h"
20+
21+
// This module covers the frequency range from 64 Hz to 10 kHz.
22+
// It uses a maximum duty cycle of 1/16, and it controls volume
23+
// by shortening the duty cycle. The hardware has a resolution
24+
// of 16 bits for the period counter, and we want 8 bits for the volume,
25+
// so we use the following timebase division factors to make this work:
26+
//
27+
// 64 Hz - 900 Hz => /40
28+
// 900 Hz - 8 kHz => /4
29+
// 8 kHz - 10 kHz => /1
1130

31+
// Audio amplifier enable
32+
static const pbdrv_gpio_t pin_sound_en = PBDRV_GPIO_EV3_PIN(13, 3, 0, 6, 15);
33+
// Audio output pin
34+
#define SYSCFG_PINMUX3_PINMUX3_7_4_GPIO0_0 0
35+
static const pbdrv_gpio_t pin_audio = PBDRV_GPIO_EV3_PIN(3, 7, 4, 0, 0);
36+
37+
void pbdrv_sound_stop() {
38+
// Force the output low
39+
EHRPWMAQContSWForceOnB(SOC_EHRPWM_0_REGS, EHRPWM_AQCSFRC_CSFB_LOW, EHRPWM_AQSFRC_RLDCSF_IMMEDIATE);
40+
// Clean up counter
41+
HWREGH(SOC_EHRPWM_0_REGS + EHRPWM_TBCTL) |= EHRPWM_TBCTL_CTRMODE_STOPFREEZE;
42+
EHRPWMWriteTBCount(SOC_EHRPWM_0_REGS, 0);
1243
}
1344

1445
void pbdrv_beep_start(uint32_t frequency, uint16_t sample_attenuator) {
46+
// Clamp the frequency into the supported range
47+
if (frequency < 64) {
48+
frequency = 64;
49+
}
50+
if (frequency > 10000) {
51+
frequency = 10000;
52+
}
53+
54+
// Clamp the volume into the supported range
55+
if (sample_attenuator > INT16_MAX) {
56+
sample_attenuator = INT16_MAX;
57+
}
58+
// Extract bits[14:7] for our 8-bit volume resolution
59+
sample_attenuator >>= 7;
1560

61+
// Configure the timebase depending on which bucket the frequency is in.
62+
// Don't use EHRPWMTimebaseClkConfig because its calculation algorithm
63+
// isn't very good and is also tricky to fix.
64+
uint32_t timebase_div;
65+
if (frequency < 900) {
66+
timebase_div = 40;
67+
HWREGH(SOC_EHRPWM_0_REGS + EHRPWM_TBCTL) = (HWREGH(SOC_EHRPWM_0_REGS + EHRPWM_TBCTL) &
68+
~(EHRPWM_TBCTL_CLKDIV | EHRPWM_TBCTL_HSPCLKDIV)) |
69+
(EHRPWM_TBCTL_CLKDIV_DIVBY4 << EHRPWM_TBCTL_CLKDIV_SHIFT) |
70+
(EHRPWM_TBCTL_HSPCLKDIV_DIVBY10 << EHRPWM_TBCTL_HSPCLKDIV_SHIFT);
71+
} else if (frequency < 8000) {
72+
timebase_div = 4;
73+
HWREGH(SOC_EHRPWM_0_REGS + EHRPWM_TBCTL) = (HWREGH(SOC_EHRPWM_0_REGS + EHRPWM_TBCTL) &
74+
~(EHRPWM_TBCTL_CLKDIV | EHRPWM_TBCTL_HSPCLKDIV)) |
75+
(EHRPWM_TBCTL_CLKDIV_DIVBY4 << EHRPWM_TBCTL_CLKDIV_SHIFT) |
76+
(EHRPWM_TBCTL_HSPCLKDIV_DIVBY1 << EHRPWM_TBCTL_HSPCLKDIV_SHIFT);
77+
} else {
78+
timebase_div = 1;
79+
HWREGH(SOC_EHRPWM_0_REGS + EHRPWM_TBCTL) = (HWREGH(SOC_EHRPWM_0_REGS + EHRPWM_TBCTL) &
80+
~(EHRPWM_TBCTL_CLKDIV | EHRPWM_TBCTL_HSPCLKDIV)) |
81+
(EHRPWM_TBCTL_CLKDIV_DIVBY1 << EHRPWM_TBCTL_CLKDIV_SHIFT) |
82+
(EHRPWM_TBCTL_HSPCLKDIV_DIVBY1 << EHRPWM_TBCTL_HSPCLKDIV_SHIFT);
83+
}
84+
85+
// The way that this code controls volume by adjusting the duty cycle
86+
// is not linear across the frequency range. For a basic beep driver,
87+
// this empirical formula compensates well enough.
88+
uint32_t freq_adj = powf(2, log10f(frequency) - 3) * 256;
89+
90+
uint32_t pwm_period = (SOC_EHRPWM_0_MODULE_FREQ / timebase_div + frequency / 2) / frequency;
91+
uint32_t pwm_duty_cycle = (pwm_period * sample_attenuator / 256) / 16 * freq_adj / 256;
92+
93+
// Program PWM to generate a square wave of this frequency + duty cycle
94+
EHRPWMLoadCMPB(SOC_EHRPWM_0_REGS, pwm_duty_cycle, true, 0, true);
95+
EHRPWMPWMOpPeriodSet(SOC_EHRPWM_0_REGS, pwm_period, EHRPWM_COUNT_UP, true);
96+
97+
// Stop forcing output low
98+
EHRPWMAQContSWForceOnB(SOC_EHRPWM_0_REGS, 0, EHRPWM_AQSFRC_RLDCSF_IMMEDIATE);
1699
}
17100

18101
void pbdrv_sound_init() {
102+
// Turn on EPWM
103+
PSCModuleControl(SOC_PSC_1_REGS, HW_PSC_EHRPWM, PSC_POWERDOMAIN_ALWAYS_ON, PSC_MDCTL_NEXT_ENABLE);
104+
105+
// The stop function performs various initializations
106+
pbdrv_sound_stop();
107+
108+
// Program EPWM to generate the desired wave shape
109+
// Pulse goes high @ t=0
110+
// Pulse goes low @ t=CMPB
111+
EHRPWMConfigureAQActionOnB(
112+
SOC_EHRPWM_0_REGS,
113+
EHRPWM_AQCTLB_ZRO_EPWMXBHIGH,
114+
EHRPWM_AQCTLB_PRD_DONOTHING,
115+
EHRPWM_AQCTLB_CAU_DONOTHING,
116+
EHRPWM_AQCTLB_CAD_DONOTHING,
117+
EHRPWM_AQCTLB_CBU_EPWMXBLOW,
118+
EHRPWM_AQCTLB_CBD_DONOTHING,
119+
EHRPWM_AQSFRC_ACTSFB_DONOTHING
120+
);
121+
// Disable unused features
122+
EHRPWMDBOutput(SOC_EHRPWM_0_REGS, EHRPWM_DBCTL_OUT_MODE_BYPASS);
123+
EHRPWMChopperDisable(SOC_EHRPWM_0_REGS);
124+
EHRPWMTZTripEventDisable(SOC_EHRPWM_0_REGS, false);
125+
EHRPWMTZTripEventDisable(SOC_EHRPWM_0_REGS, true);
19126

127+
// Configure IO pin mode
128+
pbdrv_gpio_alt(&pin_audio, SYSCFG_PINMUX3_PINMUX3_7_4_EPWM0B);
129+
// Turn speaker amplifier on
130+
// We turn the amplifier on and leave it turned on, because otherwise
131+
// it will generate a popping sound whenever it is enabled.
132+
pbdrv_gpio_out_high(&pin_sound_en);
20133
}
21134

22135
#endif // PBDRV_CONFIG_SOUND_EV3

0 commit comments

Comments
 (0)