aboutsummaryrefslogtreecommitdiff
path: root/src/usart.ts
diff options
context:
space:
mode:
authorUri Shaked2019-12-01 15:18:25 +0200
committerUri Shaked2019-12-01 15:18:25 +0200
commit2fd9e6d4c040d4a54456b153f4523cab05adbf02 (patch)
treeaf194f254e818c1922efe6e135a071879821b32a /src/usart.ts
parentchore: release 0.3.3 (diff)
downloadavr8js-2fd9e6d4c040d4a54456b153f4523cab05adbf02.tar.gz
avr8js-2fd9e6d4c040d4a54456b153f4523cab05adbf02.tar.bz2
avr8js-2fd9e6d4c040d4a54456b153f4523cab05adbf02.zip
feat: initial implementation of USART
#6
Diffstat (limited to 'src/usart.ts')
-rw-r--r--src/usart.ts119
1 files changed, 119 insertions, 0 deletions
diff --git a/src/usart.ts b/src/usart.ts
new file mode 100644
index 0000000..276207e
--- /dev/null
+++ b/src/usart.ts
@@ -0,0 +1,119 @@
+import { CPU } from './cpu';
+import { avrInterrupt } from './interrupt';
+import { u8 } from './types';
+
+export interface USARTConfig {
+ rxCompleteInterrupt: u8;
+ dataRegisterEmptyInterrupt: u8;
+ txCompleteInterrupt: u8;
+
+ UCSRA: u8;
+ UCSRB: u8;
+ UCSRC: u8;
+ UBRRL: u8;
+ UBRRH: u8;
+ UDR: u8;
+}
+
+export const usart0Config: USARTConfig = {
+ rxCompleteInterrupt: 0x24,
+ dataRegisterEmptyInterrupt: 0x26,
+ txCompleteInterrupt: 0x28,
+ UCSRA: 0xc0,
+ UCSRB: 0xc1,
+ UCSRC: 0xc2,
+ UBRRL: 0xc4,
+ UBRRH: 0xc5,
+ UDR: 0xc6
+};
+
+export type USARTTransmitCallback = (value: u8) => void;
+
+// Register bits
+const UCSRA_RXC = 0x80; // USART Receive Complete
+const UCSRA_TXC = 0x40; // USART Transmit Complete
+const UCSRA_UDRE = 0x20; // USART Data Register Empty
+const UCSRA_FE = 0x10; // Frame Error
+const UCSRA_DOR = 0x8; // Data OverRun
+const UCSRA_UPE = 0x4; // USART Parity Error
+const UCSRA_U2X = 0x2; // Double the USART Transmission Speed
+const UCSRA_MPCM = 0x1; // Multi-processor Communication Mode
+const UCSRB_RXCIE = 0x80; // RX Complete Interrupt Enable
+const UCSRB_TXCIE = 0x40; // TX Complete Interrupt Enable
+const UCSRB_UDRIE = 0x20; // USART Data Register Empty Interrupt Enable
+const UCSRB_RXEN = 0x10; // Receiver Enable
+const UCSRB_TXEN = 0x8; // Transmitter Enable
+const UCSRB_UCSZ2 = 0x4; // Character Size 2
+const UCSRB_RXB8 = 0x2; // Receive Data Bit 8
+const UCSRB_TXB8 = 0x1; // Transmit Data Bit 8
+const UCSRC_UMSEL1 = 0x80; // USART Mode Select 1
+const UCSRC_UMSEL0 = 0x40; // USART Mode Select 0
+const UCSRC_UPM1 = 0x20; // Parity Mode 1
+const UCSRC_UPM0 = 0x10; // Parity Mode 0
+const UCSRC_USBS = 0x8; // Stop Bit Select
+const UCSRC_UCSZ1 = 0x4; // Character Size 1
+const UCSRC_UCSZ0 = 0x2; // Character Size 0
+const UCSRC_UCPOL = 0x1; // Clock Polarity
+
+export class AVRUSART {
+ public onByteTransmit: USARTTransmitCallback | null = null;
+
+ constructor(private cpu: CPU, private config: USARTConfig, private freqMHz: number) {
+ this.cpu.writeHooks[config.UCSRA] = (value, oldValue) => {
+ this.cpu.data[config.UCSRA] = value | UCSRA_UDRE | UCSRA_TXC;
+ return true;
+ };
+ this.cpu.writeHooks[config.UCSRB] = (value, oldValue) => {
+ if (value & UCSRB_TXEN && !(oldValue & UCSRB_TXEN)) {
+ // Enabling the transmission - mark UDR as empty
+ this.cpu.data[config.UCSRA] |= UCSRA_UDRE;
+ }
+ };
+ this.cpu.writeHooks[config.UDR] = (value) => {
+ if (this.onByteTransmit) {
+ this.onByteTransmit(value);
+ }
+ this.cpu.data[config.UCSRA] |= UCSRA_UDRE | UCSRA_TXC;
+ };
+ }
+
+ tick() {
+ if (this.cpu.interruptsEnabled) {
+ const ucsra = this.cpu.data[this.config.UCSRA];
+ const ucsrb = this.cpu.data[this.config.UCSRB];
+ if (ucsra & UCSRA_UDRE && ucsrb & UCSRB_UDRIE) {
+ avrInterrupt(this.cpu, this.config.dataRegisterEmptyInterrupt);
+ this.cpu.data[this.config.UCSRA] &= ~UCSRA_UDRE;
+ }
+ if (ucsrb & UCSRA_TXC && ucsrb & UCSRB_TXCIE) {
+ avrInterrupt(this.cpu, this.config.txCompleteInterrupt);
+ this.cpu.data[this.config.UCSRA] &= ~UCSRA_TXC;
+ }
+ }
+ }
+
+ get baudRate() {
+ const UBRR = (this.cpu.data[this.config.UBRRH] << 8) | this.cpu.data[this.config.UBRRL];
+ const multiplier = this.cpu.data[this.config.UCSRA] & UCSRA_U2X ? 8 : 16;
+ return Math.floor(this.freqMHz / (multiplier * (1 + UBRR)));
+ }
+
+ get bitsPerChar() {
+ const ucsz =
+ ((this.cpu.data[this.config.UCSRA] & (UCSRC_UCSZ1 | UCSRC_UCSZ0)) >> 1) |
+ (this.cpu.data[this.config.UCSRB] & UCSRB_UCSZ2);
+ switch (ucsz) {
+ case 0:
+ return 5;
+ case 1:
+ return 6;
+ case 2:
+ return 7;
+ case 3:
+ return 8;
+ default: // 4..6 are reserved
+ case 7:
+ return 9;
+ }
+ }
+}