Skip to content

Commit 77afa23

Browse files
committed
feat(usart): add configuration change event
also add `txEnable` and `rxEnable` properties
1 parent 97b60b9 commit 77afa23

File tree

2 files changed

+95
-3
lines changed

2 files changed

+95
-3
lines changed

src/peripherals/usart.spec.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,24 @@ describe('USART', () => {
5353
expect(usart.baudRate).toEqual(2400);
5454
});
5555

56+
it('should call onConfigurationChange when the baudRate changes', () => {
57+
const cpu = new CPU(new Uint16Array(1024));
58+
const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ);
59+
const onConfigurationChange = jest.fn();
60+
usart.onConfigurationChange = onConfigurationChange;
61+
62+
cpu.writeData(UBRR0H, 0);
63+
expect(onConfigurationChange).toHaveBeenCalled();
64+
65+
onConfigurationChange.mockClear();
66+
cpu.writeData(UBRR0L, 5);
67+
expect(onConfigurationChange).toHaveBeenCalled();
68+
69+
onConfigurationChange.mockClear();
70+
cpu.writeData(UCSR0A, U2X0);
71+
expect(onConfigurationChange).toHaveBeenCalled();
72+
});
73+
5674
describe('bitsPerChar', () => {
5775
it('should return 5-bits per byte when UCSZ = 0', () => {
5876
const cpu = new CPU(new Uint16Array(1024));
@@ -89,6 +107,24 @@ describe('USART', () => {
89107
cpu.writeData(UCSR0B, UCSZ2);
90108
expect(usart.bitsPerChar).toEqual(9);
91109
});
110+
111+
it('should call onConfigurationChange when bitsPerChar change', () => {
112+
const cpu = new CPU(new Uint16Array(1024));
113+
const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ);
114+
const onConfigurationChange = jest.fn();
115+
usart.onConfigurationChange = onConfigurationChange;
116+
117+
cpu.writeData(UCSR0C, UCSZ0 | UCSZ1);
118+
expect(onConfigurationChange).toHaveBeenCalled();
119+
120+
onConfigurationChange.mockClear();
121+
cpu.writeData(UCSR0B, UCSZ2);
122+
expect(onConfigurationChange).toHaveBeenCalled();
123+
124+
onConfigurationChange.mockClear();
125+
cpu.writeData(UCSR0B, UCSZ2);
126+
expect(onConfigurationChange).not.toHaveBeenCalled();
127+
});
92128
});
93129

94130
describe('stopBits', () => {
@@ -145,6 +181,26 @@ describe('USART', () => {
145181
expect(usart.onByteTransmit).toHaveBeenCalledWith(0x61);
146182
});
147183

184+
describe('txEnable/rxEnable', () => {
185+
it('txEnable should equal true when the transitter is enabled', () => {
186+
const cpu = new CPU(new Uint16Array(1024));
187+
const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ);
188+
usart.onByteTransmit = jest.fn();
189+
expect(usart.txEnable).toEqual(false);
190+
cpu.writeData(UCSR0B, TXEN);
191+
expect(usart.txEnable).toEqual(true);
192+
});
193+
194+
it('rxEnable should equal true when the transitter is enabled', () => {
195+
const cpu = new CPU(new Uint16Array(1024));
196+
const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ);
197+
usart.onByteTransmit = jest.fn();
198+
expect(usart.rxEnable).toEqual(false);
199+
cpu.writeData(UCSR0B, RXEN);
200+
expect(usart.rxEnable).toEqual(true);
201+
});
202+
});
203+
148204
describe('tick()', () => {
149205
it('should trigger data register empty interrupt if UDRE is set', () => {
150206
const cpu = new CPU(new Uint16Array(1024));

src/peripherals/usart.ts

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Part of AVR8js
44
* Reference: http://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf
55
*
6-
* Copyright (C) 2019, 2020, Uri Shaked
6+
* Copyright (C) 2019, 2020, 2021 Uri Shaked
77
*/
88

99
import { AVRInterruptConfig, CPU } from '../cpu/cpu';
@@ -36,6 +36,7 @@ export const usart0Config: USARTConfig = {
3636

3737
export type USARTTransmitCallback = (value: u8) => void;
3838
export type USARTLineTransmitCallback = (value: string) => void;
39+
export type USARTConfigurationChangeCallback = () => void;
3940

4041
/* eslint-disable @typescript-eslint/no-unused-vars */
4142
// Register bits:
@@ -47,6 +48,7 @@ const UCSRA_DOR = 0x8; // Data OverRun
4748
const UCSRA_UPE = 0x4; // USART Parity Error
4849
const UCSRA_U2X = 0x2; // Double the USART Transmission Speed
4950
const UCSRA_MPCM = 0x1; // Multi-processor Communication Mode
51+
const UCSRA_CFG_MASK = UCSRA_U2X;
5052
const UCSRB_RXCIE = 0x80; // RX Complete Interrupt Enable
5153
const UCSRB_TXCIE = 0x40; // TX Complete Interrupt Enable
5254
const UCSRB_UDRIE = 0x20; // USART Data Register Empty Interrupt Enable
@@ -55,6 +57,7 @@ const UCSRB_TXEN = 0x8; // Transmitter Enable
5557
const UCSRB_UCSZ2 = 0x4; // Character Size 2
5658
const UCSRB_RXB8 = 0x2; // Receive Data Bit 8
5759
const UCSRB_TXB8 = 0x1; // Transmit Data Bit 8
60+
const UCSRB_CFG_MASK = UCSRB_UCSZ2 | UCSRB_RXEN | UCSRB_TXEN;
5861
const UCSRC_UMSEL1 = 0x80; // USART Mode Select 1
5962
const UCSRC_UMSEL0 = 0x40; // USART Mode Select 0
6063
const UCSRC_UPM1 = 0x20; // Parity Mode 1
@@ -76,6 +79,7 @@ export class AVRUSART {
7679
public onByteTransmit: USARTTransmitCallback | null = null;
7780
public onLineTransmit: USARTLineTransmitCallback | null = null;
7881
public onRxComplete: (() => void) | null = null;
82+
public onConfigurationChange: USARTConfigurationChangeCallback | null = null;
7983

8084
private rxBusyValue = false;
8185
private rxByte = 0;
@@ -107,9 +111,12 @@ export class AVRUSART {
107111

108112
constructor(private cpu: CPU, private config: USARTConfig, private freqHz: number) {
109113
this.reset();
110-
this.cpu.writeHooks[config.UCSRA] = (value) => {
114+
this.cpu.writeHooks[config.UCSRA] = (value, oldValue) => {
111115
cpu.data[config.UCSRA] = value & (UCSRA_MPCM | UCSRA_U2X);
112116
cpu.clearInterruptByFlag(this.TXC, value);
117+
if ((value & UCSRA_CFG_MASK) !== (oldValue & UCSRA_CFG_MASK)) {
118+
this.onConfigurationChange?.();
119+
}
113120
return true;
114121
};
115122
this.cpu.writeHooks[config.UCSRB] = (value, oldValue) => {
@@ -123,6 +130,16 @@ export class AVRUSART {
123130
// Enabling the transmission - mark UDR as empty
124131
cpu.setInterruptFlag(this.UDRE);
125132
}
133+
cpu.data[config.UCSRB] = value;
134+
if ((value & UCSRB_CFG_MASK) !== (oldValue & UCSRB_CFG_MASK)) {
135+
this.onConfigurationChange?.();
136+
}
137+
return true;
138+
};
139+
this.cpu.writeHooks[config.UCSRC] = (value) => {
140+
cpu.data[config.UCSRC] = value;
141+
this.onConfigurationChange?.();
142+
return true;
126143
};
127144
this.cpu.readHooks[config.UDR] = () => {
128145
const mask = rxMasks[this.bitsPerChar] ?? 0xff;
@@ -151,6 +168,16 @@ export class AVRUSART {
151168
this.cpu.clearInterrupt(this.TXC);
152169
this.cpu.clearInterrupt(this.UDRE);
153170
};
171+
this.cpu.writeHooks[config.UBRRH] = (value) => {
172+
this.cpu.data[config.UBRRH] = value;
173+
this.onConfigurationChange?.();
174+
return true;
175+
};
176+
this.cpu.writeHooks[config.UBRRL] = (value) => {
177+
this.cpu.data[config.UBRRL] = value;
178+
this.onConfigurationChange?.();
179+
return true;
180+
};
154181
}
155182

156183
reset() {
@@ -187,13 +214,22 @@ export class AVRUSART {
187214
}
188215

189216
private get UBRR() {
190-
return (this.cpu.data[this.config.UBRRH] << 8) | this.cpu.data[this.config.UBRRL];
217+
const { UBRRH, UBRRL } = this.config;
218+
return (this.cpu.data[UBRRH] << 8) | this.cpu.data[UBRRL];
191219
}
192220

193221
private get multiplier() {
194222
return this.cpu.data[this.config.UCSRA] & UCSRA_U2X ? 8 : 16;
195223
}
196224

225+
get rxEnable() {
226+
return !!(this.cpu.data[this.config.UCSRB] & UCSRB_RXEN);
227+
}
228+
229+
get txEnable() {
230+
return !!(this.cpu.data[this.config.UCSRB] & UCSRB_TXEN);
231+
}
232+
197233
get baudRate() {
198234
return Math.floor(this.freqHz / (this.multiplier * (1 + this.UBRR)));
199235
}

0 commit comments

Comments
 (0)