-
Hello guys,
I'm working on some sort of 8bit to 12bit encoder/decoder (not sure how to name it). I want to have an 8bit BCD input displayed on 3 7-segment displays. For this I want to use an D27C210 EPROM (because it has more than enough outputs).
Using the code below i split a number into its digits and than concatenate them to get the desired combination.
for example: address 1 -> 0x1.....address 100 -> 0x100...address 255 -> 0x255.
The questions are, how to arrange the data into one file, or table. Can i just use fopen() to create a file and write to it? What should i write? HEX or DEC value? The output file should be compatible with the TL866II Plus programmer (I don't have it yet, but it's on the way)
I know that there are other ways to achieve this but I still want to know how to generate a HEX file for an EPROM.
Thanks a lot.
#include<stdlib.h>
#include<stdio.h>
int finalResult();
int split(int length);
int digits[3]; //Array to store each digit.
int main() {
//Test routine
for (int i = 0; i < 256; i++) {
split(i);
printf("Address: %d\tValue HEX: 0x%X \tValue DEC: %d\n", i, finalResult(), finalResult());
}
return 0;
}
//Split the number into 3 digits and store them in an array.
int split(int input) {
int pos = 0;
while (input > 0) {
int digit = input % 10;
digits[pos] = digit; pos++;
input /= 10;
}
return pos;
}
//Concatenate the digits each ito it's right position.
int finalResult() {
int res = ((digits[2] << 8) | digits[1] << 4) | digits[0];
return res;
}
Console output:
Address: 0 Value HEX: 0x0 Value DEC: 0
Address: 1 Value HEX: 0x1 Value DEC: 1
Address: 2 Value HEX: 0x2 Value DEC: 2
Address: 3 Value HEX: 0x3 Value DEC: 3
Address: 4 Value HEX: 0x4 Value DEC: 4
Address: 5 Value HEX: 0x5 Value DEC: 5
Address: 6 Value HEX: 0x6 Value DEC: 6
Address: 7 Value HEX: 0x7 Value DEC: 7
Address: 8 Value HEX: 0x8 Value DEC: 8
Address: 9 Value HEX: 0x9 Value DEC: 9
Address: 10 Value HEX: 0x10 Value DEC: 16
Address: 11 Value HEX: 0x11 Value DEC: 17
Address: 12 Value HEX: 0x12 Value DEC: 18
Address: 13 Value HEX: 0x13 Value DEC: 19
Address: 14 Value HEX: 0x14 Value DEC: 20
Address: 15 Value HEX: 0x15 Value DEC: 21
Address: 16 Value HEX: 0x16 Value DEC: 22
Address: 17 Value HEX: 0x17 Value DEC: 23
Address: 18 Value HEX: 0x18 Value DEC: 24
Address: 19 Value HEX: 0x19 Value DEC: 25
Address: 20 Value HEX: 0x20 Value DEC: 32
Address: 21 Value HEX: 0x21 Value DEC: 33
Address: 22 Value HEX: 0x22 Value DEC: 34
Address: 23 Value HEX: 0x23 Value DEC: 35
Address: 24 Value HEX: 0x24 Value DEC: 36
Address: 25 Value HEX: 0x25 Value DEC: 37
Address: 26 Value HEX: 0x26 Value DEC: 38
Address: 27 Value HEX: 0x27 Value DEC: 39
Address: 28 Value HEX: 0x28 Value DEC: 40
Address: 29 Value HEX: 0x29 Value DEC: 41
Address: 30 Value HEX: 0x30 Value DEC: 48
Address: 31 Value HEX: 0x31 Value DEC: 49
Address: 32 Value HEX: 0x32 Value DEC: 50
Address: 33 Value HEX: 0x33 Value DEC: 51
Address: 34 Value HEX: 0x34 Value DEC: 52
Address: 35 Value HEX: 0x35 Value DEC: 53
Address: 36 Value HEX: 0x36 Value DEC: 54
Address: 37 Value HEX: 0x37 Value DEC: 55
Address: 38 Value HEX: 0x38 Value DEC: 56
Address: 39 Value HEX: 0x39 Value DEC: 57
Address: 40 Value HEX: 0x40 Value DEC: 64
Address: 41 Value HEX: 0x41 Value DEC: 65
Address: 42 Value HEX: 0x42 Value DEC: 66
Address: 43 Value HEX: 0x43 Value DEC: 67
Address: 44 Value HEX: 0x44 Value DEC: 68
Address: 45 Value HEX: 0x45 Value DEC: 69
Address: 46 Value HEX: 0x46 Value DEC: 70
Address: 47 Value HEX: 0x47 Value DEC: 71
Address: 48 Value HEX: 0x48 Value DEC: 72
Address: 49 Value HEX: 0x49 Value DEC: 73
Address: 50 Value HEX: 0x50 Value DEC: 80
Address: 51 Value HEX: 0x51 Value DEC: 81
Address: 52 Value HEX: 0x52 Value DEC: 82
Address: 53 Value HEX: 0x53 Value DEC: 83
Address: 54 Value HEX: 0x54 Value DEC: 84
Address: 55 Value HEX: 0x55 Value DEC: 85
Address: 56 Value HEX: 0x56 Value DEC: 86
Address: 57 Value HEX: 0x57 Value DEC: 87
Address: 58 Value HEX: 0x58 Value DEC: 88
Address: 59 Value HEX: 0x59 Value DEC: 89
Address: 60 Value HEX: 0x60 Value DEC: 96
Address: 61 Value HEX: 0x61 Value DEC: 97
Address: 62 Value HEX: 0x62 Value DEC: 98
Address: 63 Value HEX: 0x63 Value DEC: 99
Address: 64 Value HEX: 0x64 Value DEC: 100
Address: 65 Value HEX: 0x65 Value DEC: 101
Address: 66 Value HEX: 0x66 Value DEC: 102
Address: 67 Value HEX: 0x67 Value DEC: 103
Address: 68 Value HEX: 0x68 Value DEC: 104
Address: 69 Value HEX: 0x69 Value DEC: 105
Address: 70 Value HEX: 0x70 Value DEC: 112
Address: 71 Value HEX: 0x71 Value DEC: 113
Address: 72 Value HEX: 0x72 Value DEC: 114
Address: 73 Value HEX: 0x73 Value DEC: 115
Address: 74 Value HEX: 0x74 Value DEC: 116
Address: 75 Value HEX: 0x75 Value DEC: 117
Address: 76 Value HEX: 0x76 Value DEC: 118
Address: 77 Value HEX: 0x77 Value DEC: 119
Address: 78 Value HEX: 0x78 Value DEC: 120
Address: 79 Value HEX: 0x79 Value DEC: 121
Address: 80 Value HEX: 0x80 Value DEC: 128
Address: 81 Value HEX: 0x81 Value DEC: 129
Address: 82 Value HEX: 0x82 Value DEC: 130
Address: 83 Value HEX: 0x83 Value DEC: 131
Address: 84 Value HEX: 0x84 Value DEC: 132
Address: 85 Value HEX: 0x85 Value DEC: 133
Address: 86 Value HEX: 0x86 Value DEC: 134
Address: 87 Value HEX: 0x87 Value DEC: 135
Address: 88 Value HEX: 0x88 Value DEC: 136
Address: 89 Value HEX: 0x89 Value DEC: 137
Address: 90 Value HEX: 0x90 Value DEC: 144
Address: 91 Value HEX: 0x91 Value DEC: 145
Address: 92 Value HEX: 0x92 Value DEC: 146
Address: 93 Value HEX: 0x93 Value DEC: 147
Address: 94 Value HEX: 0x94 Value DEC: 148
Address: 95 Value HEX: 0x95 Value DEC: 149
Address: 96 Value HEX: 0x96 Value DEC: 150
Address: 97 Value HEX: 0x97 Value DEC: 151
Address: 98 Value HEX: 0x98 Value DEC: 152
Address: 99 Value HEX: 0x99 Value DEC: 153
Address: 100 Value HEX: 0x100 Value DEC: 256
Address: 101 Value HEX: 0x101 Value DEC: 257
Address: 102 Value HEX: 0x102 Value DEC: 258
Address: 103 Value HEX: 0x103 Value DEC: 259
Address: 104 Value HEX: 0x104 Value DEC: 260
Address: 105 Value HEX: 0x105 Value DEC: 261
Address: 106 Value HEX: 0x106 Value DEC: 262
Address: 107 Value HEX: 0x107 Value DEC: 263
Address: 108 Value HEX: 0x108 Value DEC: 264
Address: 109 Value HEX: 0x109 Value DEC: 265
Address: 110 Value HEX: 0x110 Value DEC: 272
Address: 111 Value HEX: 0x111 Value DEC: 273
Address: 112 Value HEX: 0x112 Value DEC: 274
Address: 113 Value HEX: 0x113 Value DEC: 275
Address: 114 Value HEX: 0x114 Value DEC: 276
Address: 115 Value HEX: 0x115 Value DEC: 277
Address: 116 Value HEX: 0x116 Value DEC: 278
Address: 117 Value HEX: 0x117 Value DEC: 279
Address: 118 Value HEX: 0x118 Value DEC: 280
Address: 119 Value HEX: 0x119 Value DEC: 281
Address: 120 Value HEX: 0x120 Value DEC: 288
Address: 121 Value HEX: 0x121 Value DEC: 289
Address: 122 Value HEX: 0x122 Value DEC: 290
Address: 123 Value HEX: 0x123 Value DEC: 291
Address: 124 Value HEX: 0x124 Value DEC: 292
Address: 125 Value HEX: 0x125 Value DEC: 293
Address: 126 Value HEX: 0x126 Value DEC: 294
Address: 127 Value HEX: 0x127 Value DEC: 295
Address: 128 Value HEX: 0x128 Value DEC: 296
Address: 129 Value HEX: 0x129 Value DEC: 297
Address: 130 Value HEX: 0x130 Value DEC: 304
Address: 131 Value HEX: 0x131 Value DEC: 305
Address: 132 Value HEX: 0x132 Value DEC: 306
Address: 133 Value HEX: 0x133 Value DEC: 307
Address: 134 Value HEX: 0x134 Value DEC: 308
Address: 135 Value HEX: 0x135 Value DEC: 309
Address: 136 Value HEX: 0x136 Value DEC: 310
Address: 137 Value HEX: 0x137 Value DEC: 311
Address: 138 Value HEX: 0x138 Value DEC: 312
Address: 139 Value HEX: 0x139 Value DEC: 313
Address: 140 Value HEX: 0x140 Value DEC: 320
Address: 141 Value HEX: 0x141 Value DEC: 321
Address: 142 Value HEX: 0x142 Value DEC: 322
Address: 143 Value HEX: 0x143 Value DEC: 323
Address: 144 Value HEX: 0x144 Value DEC: 324
Address: 145 Value HEX: 0x145 Value DEC: 325
Address: 146 Value HEX: 0x146 Value DEC: 326
Address: 147 Value HEX: 0x147 Value DEC: 327
Address: 148 Value HEX: 0x148 Value DEC: 328
Address: 149 Value HEX: 0x149 Value DEC: 329
Address: 150 Value HEX: 0x150 Value DEC: 336
Address: 151 Value HEX: 0x151 Value DEC: 337
Address: 152 Value HEX: 0x152 Value DEC: 338
Address: 153 Value HEX: 0x153 Value DEC: 339
Address: 154 Value HEX: 0x154 Value DEC: 340
Address: 155 Value HEX: 0x155 Value DEC: 341
Address: 156 Value HEX: 0x156 Value DEC: 342
Address: 157 Value HEX: 0x157 Value DEC: 343
Address: 158 Value HEX: 0x158 Value DEC: 344
Address: 159 Value HEX: 0x159 Value DEC: 345
Address: 160 Value HEX: 0x160 Value DEC: 352
Address: 161 Value HEX: 0x161 Value DEC: 353
Address: 162 Value HEX: 0x162 Value DEC: 354
Address: 163 Value HEX: 0x163 Value DEC: 355
Address: 164 Value HEX: 0x164 Value DEC: 356
Address: 165 Value HEX: 0x165 Value DEC: 357
Address: 166 Value HEX: 0x166 Value DEC: 358
Address: 167 Value HEX: 0x167 Value DEC: 359
Address: 168 Value HEX: 0x168 Value DEC: 360
Address: 169 Value HEX: 0x169 Value DEC: 361
Address: 170 Value HEX: 0x170 Value DEC: 368
Address: 171 Value HEX: 0x171 Value DEC: 369
Address: 172 Value HEX: 0x172 Value DEC: 370
Address: 173 Value HEX: 0x173 Value DEC: 371
Address: 174 Value HEX: 0x174 Value DEC: 372
Address: 175 Value HEX: 0x175 Value DEC: 373
Address: 176 Value HEX: 0x176 Value DEC: 374
Address: 177 Value HEX: 0x177 Value DEC: 375
Address: 178 Value HEX: 0x178 Value DEC: 376
Address: 179 Value HEX: 0x179 Value DEC: 377
Address: 180 Value HEX: 0x180 Value DEC: 384
Address: 181 Value HEX: 0x181 Value DEC: 385
Address: 182 Value HEX: 0x182 Value DEC: 386
Address: 183 Value HEX: 0x183 Value DEC: 387
Address: 184 Value HEX: 0x184 Value DEC: 388
Address: 185 Value HEX: 0x185 Value DEC: 389
Address: 186 Value HEX: 0x186 Value DEC: 390
Address: 187 Value HEX: 0x187 Value DEC: 391
Address: 188 Value HEX: 0x188 Value DEC: 392
Address: 189 Value HEX: 0x189 Value DEC: 393
Address: 190 Value HEX: 0x190 Value DEC: 400
Address: 191 Value HEX: 0x191 Value DEC: 401
Address: 192 Value HEX: 0x192 Value DEC: 402
Address: 193 Value HEX: 0x193 Value DEC: 403
Address: 194 Value HEX: 0x194 Value DEC: 404
Address: 195 Value HEX: 0x195 Value DEC: 405
Address: 196 Value HEX: 0x196 Value DEC: 406
Address: 197 Value HEX: 0x197 Value DEC: 407
Address: 198 Value HEX: 0x198 Value DEC: 408
Address: 199 Value HEX: 0x199 Value DEC: 409
Address: 200 Value HEX: 0x200 Value DEC: 512
Address: 201 Value HEX: 0x201 Value DEC: 513
Address: 202 Value HEX: 0x202 Value DEC: 514
Address: 203 Value HEX: 0x203 Value DEC: 515
Address: 204 Value HEX: 0x204 Value DEC: 516
Address: 205 Value HEX: 0x205 Value DEC: 517
Address: 206 Value HEX: 0x206 Value DEC: 518
Address: 207 Value HEX: 0x207 Value DEC: 519
Address: 208 Value HEX: 0x208 Value DEC: 520
Address: 209 Value HEX: 0x209 Value DEC: 521
Address: 210 Value HEX: 0x210 Value DEC: 528
Address: 211 Value HEX: 0x211 Value DEC: 529
Address: 212 Value HEX: 0x212 Value DEC: 530
Address: 213 Value HEX: 0x213 Value DEC: 531
Address: 214 Value HEX: 0x214 Value DEC: 532
Address: 215 Value HEX: 0x215 Value DEC: 533
Address: 216 Value HEX: 0x216 Value DEC: 534
Address: 217 Value HEX: 0x217 Value DEC: 535
Address: 218 Value HEX: 0x218 Value DEC: 536
Address: 219 Value HEX: 0x219 Value DEC: 537
Address: 220 Value HEX: 0x220 Value DEC: 544
Address: 221 Value HEX: 0x221 Value DEC: 545
Address: 222 Value HEX: 0x222 Value DEC: 546
Address: 223 Value HEX: 0x223 Value DEC: 547
Address: 224 Value HEX: 0x224 Value DEC: 548
Address: 225 Value HEX: 0x225 Value DEC: 549
Address: 226 Value HEX: 0x226 Value DEC: 550
Address: 227 Value HEX: 0x227 Value DEC: 551
Address: 228 Value HEX: 0x228 Value DEC: 552
Address: 229 Value HEX: 0x229 Value DEC: 553
Address: 230 Value HEX: 0x230 Value DEC: 560
Address: 231 Value HEX: 0x231 Value DEC: 561
Address: 232 Value HEX: 0x232 Value DEC: 562
Address: 233 Value HEX: 0x233 Value DEC: 563
Address: 234 Value HEX: 0x234 Value DEC: 564
Address: 235 Value HEX: 0x235 Value DEC: 565
Address: 236 Value HEX: 0x236 Value DEC: 566
Address: 237 Value HEX: 0x237 Value DEC: 567
Address: 238 Value HEX: 0x238 Value DEC: 568
Address: 239 Value HEX: 0x239 Value DEC: 569
Address: 240 Value HEX: 0x240 Value DEC: 576
Address: 241 Value HEX: 0x241 Value DEC: 577
Address: 242 Value HEX: 0x242 Value DEC: 578
Address: 243 Value HEX: 0x243 Value DEC: 579
Address: 244 Value HEX: 0x244 Value DEC: 580
Address: 245 Value HEX: 0x245 Value DEC: 581
Address: 246 Value HEX: 0x246 Value DEC: 582
Address: 247 Value HEX: 0x247 Value DEC: 583
Address: 248 Value HEX: 0x248 Value DEC: 584
Address: 249 Value HEX: 0x249 Value DEC: 585
Address: 250 Value HEX: 0x250 Value DEC: 592
Address: 251 Value HEX: 0x251 Value DEC: 593
Address: 252 Value HEX: 0x252 Value DEC: 594
Address: 253 Value HEX: 0x253 Value DEC: 595
Address: 254 Value HEX: 0x254 Value DEC: 596
Address: 255 Value HEX: 0x255 Value DEC: 597
-
Do you want to write a binary value conversion table in EEPROM to 3x7 = 21 discrete outputs for displaying it on the led?
-
Do you want to write a binary value conversion table in EEPROM to 3x7 = 21 discrete outputs for displaying it on the led?
No, it will be 3 x 4. 3 BCD outputs to connect to CD4511 7 segment driver.
I'll use just 8 address lines. When they are all high (255) the output of the EPROM will be something like this.
D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11
0 0 1 0 0 1 0 1 0 1 0 1 -- 597 (decimal) / 0x255 (hex)
D0 - D3 connected to 1st CD4511
D4 - D7 connected to 2nd CD4511
D8 - D11 connected to 3rd CD4511
I hope it's more clear now.
I intend to play more with EPROMS and for the next project will be to store instruction for a standard 16x2 LCD. But for every project I need to generate a hex file for the EEPROM.
-
Do you want to write a binary value conversion table in EEPROM to 3x7 = 21 discrete outputs for displaying it on the led?
No, it will be 3 x 4. 3 BCD outputs to connect to CD4511 7 segment driver.
I'll use just 8 address lines. When they are all high (255) the output of the EPROM will be something like this.
D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11
0 0 1 0 0 1 0 1 0 1 0 1 -- 597 (decimal) / 0x255 (hex)
D0 - D3 connected to 1st CD4511
D4 - D7 connected to 2nd CD4511
D8 - D11 connected to 3rd CD4511
I hope it's more clear now.
I intend to play more with EPROMS and for the next project will be to store instruction for a standard 16x2 LCD. But for every project I need to generate a hex file for the EEPROM.
Do you need to display a binary number from 0 to 999 on the indicator using BCD drivers, and transcode BIN - > 3 digits of BCD in EPROM? You can do without EPROM, but if you really need it, then here are the steps, it can be made more compact, I wrote this for clarity:
int d1,d2,d3;
uint_16 res;
for (int i=0; i<=999; i++) {
d1=i / 100;
d2=(i - d1*100) / 10;
d3=i - d1*100 - d2*10;
res=(d1 << 8) or (d2 << 4) or d3;
printf("Address: %d\tValue HEX: 0x%X",i,res)
}
And just write the res values to a binary file. The programmer can flash a binary image to EPROM, it is not necessary to do it in HEX format.
-
I would suggest that the easiest way is to write your file in binary and then write binary to the PROM.
"Hex files" are not actually just hexadecimal numbers, they are one of several specific formats that use commands to put data in specific addresses, and also have check digits. Intel Hex has different commands from Motorola Hex, etc. So you could read the documentation for (e.g.) Intel Hex, decide how to use commands to output your data, generate lines of the appropriate length to the correct addresses, etc etc... Or just write in binary which the TL866 can write verbatim without any other complexity.
-
You can use standard gnu tools to create exactly the binary file you want using either assembly language or C.
Create a simple minimal linker script something like this in a file rom_link:
OUTPUT_FORMAT(ihex)
MEMORY {
rom : ORIGIN = 0, LENGTH = 2K
}
SECTIONS {
.text : {} >rom
}
Then write your ROM values in, for example, assembly language in romdata.S:
.byte 1,2,3,4,5
Then build it:
$ gcc -nostartfiles -nostdlib -T rom_link -o romdata romdata.S
$ cat romdata
:050000000102030405EC
:00000001FF
$
If you wish, you could put .org 2048 after the .byte to fill the rest of the ROM with zeroes. If you put more data there than was specified in LENGTH in the linker script then the linker will give an error.
Or, you can do it in C, say in romdata.c:
char foo[] = {1,2,3,4,5};
Build it in just the same way:
$ gcc -nostartfiles -nostdlib -T rom_link -o romdata romdata.c
$ cat romdata
:050000000102030405EC
:00000001FF
$
The output file will be the same.
-
Use a standard way. It is almost always better to use a standard, because there are tools available.
Either write the file as an Intel HEX or a Motorola HEX. And search for a library, then you dont need to reinvent the wheel.
-
Do you have a schematic for your circuit?
Just a thought here. From my understanding, the 4511 has a latching/strobe pin which keeps the 7 seg display static or loads the data. So connecting your 4511 drivers direct to a microcontroller only requires 3 GPIO pins for the latching lines and 4 for the BCD lines. You only ever need 4 lines for the BCD, so you can add as many digits as you have spare GPIO pins. The MCU contains the translation table, reads the input number and does the segment loading.
-
I like to do these sorts of things in JS. It's accessible, it's pretty powerful, and it has access to most anything you might need. You can do it right here even, just hit F12!
Example, copy this one-liner to the console* and run it:
window.location = URL.createObjectURL(new Blob(Array.from("Hello World\n"), {type: 'text/plain;charset=utf-8'}));
This creates a "blob" (a container for data), from the data passed in (the "Hello World" string, converted to an array type), of MIME type text/plain (i.e., a text file). Then makes a URL to it, then sets the window to that URL to instantly view it. With a little different code, you can download that URL instead, and voila, you can save files of arbitrary (binary) content, just put in your array of data.
This does of course need a JS environment to work, which pretty much anything useful has (JS of some sort is built into every modern OS; file access mechanisms will vary, of course). You may still be more comfortable with C or whatever, which is fine, and to be fair, it's probably easier to drop some stock HEX formatting code into C, than to port it to JS, if you need it in that format. But I wouldn't think it's much bother either way.
There's no shortage of ways to do it; BASIC isn't very trendy these days, but would've been a prime choice in prior decades (back when every PC came with a BASIC ROM!), and is pretty easy to write stuff like this. Python and other high level languages are probably choice today. Or for the Linux guru, write it all in C, you've always got a compiler there at your fingertips, no problem whatsoever. Or in bash scripts for that matter... :-DD (Or, I'm not sure you'd want to (or even be able to) do such things with Windows batch files, but PowerShell probably could, for not too much more effort than with other command scripts.)
Tim
-
You can use standard gnu tools to create exactly the binary file you want using either assembly language or C.
Create a simple minimal linker script something like this in a file rom_link:
OUTPUT_FORMAT(ihex)
MEMORY {
rom : ORIGIN = 0, LENGTH = 2K
}
SECTIONS {
.text : {} >rom
}
Then write your ROM values in, for example, assembly language in romdata.S:
.byte 1,2,3,4,5
Then build it:
$ gcc -nostartfiles -nostdlib -T rom_link -o romdata romdata.S
$ cat romdata
:050000000102030405EC
:00000001FF
$
If you wish, you could put .org 2048 after the .byte to fill the rest of the ROM with zeroes. If you put more data there than was specified in LENGTH in the linker script then the linker will give an error.
Or, you can do it in C, say in romdata.c:
char foo[] = {1,2,3,4,5};
Build it in just the same way:
$ gcc -nostartfiles -nostdlib -T rom_link -o romdata romdata.c
$ cat romdata
:050000000102030405EC
:00000001FF
$
The output file will be the same.
Looks like i can generate the file using your method.
I will modify my code to output whatever I want in an array in a c file and that should do the trick. Can't wait for my programmer to test.
@3sl4co1l - Great idea for quick and small projects. Not sure if it will work with 1Mb of data.
@Syntax Error - Yeah, I know that an arduino can do that. Might be a lot cheaper as well. I did play around with arduino but never with parallel EPROMS, so it's time to level up, or down :))
Thank you all so far.
*EDIT: Why is my romdata file different than yours? did you actualy use this line too?char foo[] = {1,2,3,4,5};
-
*EDIT: Why is my romdata file different than yours? did you actualy use this line too?char foo[] = {1,2,3,4,5};
Different how?
Yes, I tested it both ways. Identical output. I used the native x86_64 gcc and binutils on Linux, but any GNU toolchain should work the same.
-
*EDIT: Why is my romdata file different than yours? did you actualy use this line too?char foo[] = {1,2,3,4,5};
Different how?
Yes, I tested it both ways. Identical output. I used the native x86_64 gcc and binutils on Linux, but any GNU toolchain should work the same.
This is how i have it. The first 2 lines are different. I doubt that it will work like this. The EPROM (D27C210) takes 16bit word on the data lines. Might be good to change char foo to unsigned short foo.
ubuntu@ubuntu:~/C$ cat rom_link
OUTPUT_FORMAT(ihex)
MEMORY {
rom : ORIGIN = 0, LENGTH = 2K
}
SECTIONS {
.text : {} >rom
}
ubuntu@ubuntu:~/C$ cat romdata.c
char foo[] = {1,2,3,4,5};
ubuntu@ubuntu:~/C$ cat romdata
:10000000040000001000000005000000474E5500ED
:10001000020000C004000000030000000000000017
:050020000102030405CC
:00000001FF
ubuntu@ubuntu:~/C$
ubuntu@ubuntu:~/C$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/9/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:hsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 9.3.0-17ubuntu1~20.04' --with-bugurl=file:///usr/share/doc/gcc-9/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,gm2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-9 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-9-HskZEa/gcc-9-9.3.0/debian/tmp-nvptx/usr,hsa --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04)
ubuntu@ubuntu:~/C$
-
@3sl4co1l - Great idea for quick and small projects. Not sure if it will work with 1Mb of data.
That's the best part, you have GB of RAM available. From which the browser might complain after a few hundred megs. Passing around big arrays is child's play, even with all the interpreter/GC/profiling overhead.
Easily done in C too of course, whether via malloc or memory-mapping a file. Let the OS handle the heavy lifting for you. :-+
Tim
-
[The EPROM (D27C210) takes 16bit word on the data lines. Might be good to change char foo to unsigned short foo.
That's entirely up to you. Only you know what data you want in the ROM. If you use short then you'll have to make sure you get little-endian vs big-endian correct for your ROM application. If you specify the bytes explicitly (using char) then you know it's correct.
I don't know where those first two lines come from. You didn't show the command(s) you used to create romdata from romdata.c
-
You can use standard gnu tools to create exactly the binary file you want using either assembly language or C.
Create a simple minimal linker script something like this in a file rom_link:
OUTPUT_FORMAT(ihex)
MEMORY {
rom : ORIGIN = 0, LENGTH = 2K
}
SECTIONS {
.text : {} >rom
}
Then write your ROM values in, for example, assembly language in romdata.S:
.byte 1,2,3,4,5
Then build it:
$ gcc -nostartfiles -nostdlib -T rom_link -o romdata romdata.S
$ cat romdata
:050000000102030405EC
:00000001FF
$
If you wish, you could put .org 2048 after the .byte to fill the rest of the ROM with zeroes. If you put more data there than was specified in LENGTH in the linker script then the linker will give an error.
Or, you can do it in C, say in romdata.c:
char foo[] = {1,2,3,4,5};
Build it in just the same way:
$ gcc -nostartfiles -nostdlib -T rom_link -o romdata romdata.c
$ cat romdata
:050000000102030405EC
:00000001FF
$
The output file will be the same.
Looks like i can generate the file using your method.
I will modify my code to output whatever I want in an array in a c file and that should do the trick. Can't wait for my programmer to test.
@3sl4co1l - Great idea for quick and small projects. Not sure if it will work with 1Mb of data.
@Syntax Error - Yeah, I know that an arduino can do that. Might be a lot cheaper as well. I did play around with arduino but never with parallel EPROMS, so it's time to level up, or down :))
Thank you all so far.
*EDIT: Why is my romdata file different than yours? did you actualy use this line too?char foo[] = {1,2,3,4,5};
I wrote you a simple EPROM content generator. Just replace printf with write to file procedure to the eeprom.bin file.
-
So far so good. Thank you all for suggestions. I ended up using the following code. It just works. It outputs a binary file that the minipro accepts and I'm happy with it.
#include<stdlib.h>
#include<stdio.h>
unsigned short finalResult();
int split(int length);
FILE *ptr;
unsigned short buffer[65537];
unsigned int tmp = 0;
int digits[3]; //Array to store each digit.
int main() {
//fill the buffer
for (unsigned int i = 0; i < 65536; i++) {
split(i);
tmp = finalResult();
if (i > 255) {
tmp = 0;
}
buffer[i] = tmp;
}
for (unsigned int i = 0; i < 258; i++) {
tmp = buffer[i];
printf("Address: %d\tValue HEX: 0x%X \tValue DEC: %d\n", i, tmp, tmp);
}
//size_t n = sizeof(buffer)/sizeof(buffer[0]);
ptr = fopen("rom.bin","wb");
fwrite(buffer, 2, 65536, ptr);
fflush(ptr);
fclose(ptr);
return 0;
}
//Split the number into 3 digits and store them in an array.
int split(int input) {
int pos = 0;
while (input > 0) {
int digit = input % 10;
digits[pos] = digit; pos++;
input /= 10;
}
return pos;
}
//Concatenate the digits each into it's right position.
unsigned short finalResult() {
unsigned short res = ((digits[2] << 8) | digits[1] << 4) | digits[0];
return res;
}
However another problem has arrived. Before waiting for the minipro programmer to come, I made an arduino sketch for an Atmega32 in order to program the EPROM. It works fine but at the address 0 it writes 0xff instead of 0 and I have no idea why... All other addresses and data is fine. The code is messy.
Again, many thanks!
#ifndef F_CPU
#define F_CPU 8000000L
#endif
//Necessary librarys.
#include <avr/io.h>
#include <util/delay.h>
#include <stdbool.h>
//#include <"uart.h">
//Port definitions
#define PORT_ADDRESS PORTC
#define DATA_PORT_LOW PORTA
#define DATA_PORT_HIGH PORTB
#define VPP_EN PD6
#define OE PD5
#define CE PD4
#define PGM PD3
//Variables
uint8_t FIRST_ADDRESS = 0x00;
uint8_t LAST_ADDRESS = 0xff;
uint16_t data= 0;
uint16_t buffer[3];
void enterProgramMode();
void exitProgramMode();
void writeData(uint8_t address, uint16_t data);
uint16_t readData(uint8_t address);
unsigned int split(uint16_t input);
uint16_t finalResult();
void setup() {
Serial.begin(115200);
//Configure pins
DDRC = 0xff; //All pins as output
DDRA = 0xff;
DDRB |= 1 << PB3 | 1 << PB2 | 1 << PB1 | 1<< PB0; //Only the first 4bits are used
DDRD |= 1 << VPP_EN | 1 << OE | 1 << CE | 1 << PGM; //Set PD6 to PD3 as output
PORT_ADDRESS = 0;
DATA_PORT_LOW = 0;
PORTD = 0;
_delay_ms(1000);
Serial.print("Enter programming mode\n");
enterProgramMode();
_delay_ms(500);
for (uint16_t i = FIRST_ADDRESS; i < LAST_ADDRESS + 1; i++) {
split(i);
data = finalResult();
writeData(i, data);
Serial.print(i);
Serial.print(" ");
Serial.print(data);
Serial.print(" ");
Serial.print(DATA_PORT_LOW);
Serial.println();
_delay_ms(10);
}
_delay_ms(500);
exitProgramMode();
//Usless code starts here....
enterReadMode();
for (uint16_t i = FIRST_ADDRESS; i < LAST_ADDRESS + 1; i++) {
data = readData(i);
Serial.print(i);
Serial.print(" ");
Serial.print(data);
Serial.print(" ");
Serial.print(PINB, BIN);
Serial.print(" ");
Serial.print(PINA, BIN);
Serial.println();
_delay_ms(10);
}
Serial.println("DONE!");
//return 0;
}
void enterProgramMode() {
PORTD |= 1 << VPP_EN | 1 << CE;
PORTD &= ~(1 << OE);
DATA_PORT_LOW = 0;
DATA_PORT_HIGH = 0;
}
//Usless, dosen't work for now.
void enterReadMode() {
DDRA &= 0;
PORTA = 0xff;
DDRB &= ~ (1 << PORTB3 | 1 << PORTB2 | 1 << PORTB1 | 1 << PORTB0);
PORTB |= (1 << PORTB3 | 1 << PORTB2 | 1 << PORTB1 | 1 << PORTB0);
PORTD = 0;
PORTD |= 1 << PORTD5 | 1 << PORTD4;
}
void exitProgramMode() {
PORTD &= ~(1 << VPP_EN | 1 << CE);
PORTD |= 1 << OE;
}
void writeData(uint8_t address, uint16_t data) {
uint8_t dataL, dataH;
dataH = data >> 8;
dataL = data & 0xff;
PORT_ADDRESS = address; //Set the address
_delay_us(100);
DATA_PORT_LOW = dataL; //Write lower 8bit
DATA_PORT_HIGH = dataH; //Write upper 4bit (or whats left)
PORTD |= (1 << PGM); //Toggle PGM pin
_delay_us(105);
PORTD &= ~ (1 << PGM);
_delay_ms(50);
}
//usless function. Dosen't work.
uint16_t readData(uint8_t address) {
uint8_t dataL;
uint8_t dataH;
uint16_t data;
PORTC = address;
_delay_ms(20);
dataL = PINA;
dataH = PINB;
data = (dataH << 8) | (dataL & 0xff);
//data |= dataL & 0xff;
return data;
}
unsigned int split(uint16_t input) {
unsigned int pos = 0;
while (input > 0) {
int digit = input % 10;
buffer[pos] = digit; pos++;
input /= 10;
}
return pos;
}
uint16_t finalResult() {
uint16_t res = ((buffer[2] << 8) | buffer[1] << 4) | buffer[0];
return res;
}
void loop() {
// put your main code here, to run repeatedly:
}
-
Does the EPROM datasheet guarantee that a 105us write cycle will suffice?
Often they follow a procedure to partially write, verify, and continue writing until verified.
Tim
-
This looks good. I have not tried to do 16 bits yet, but if you write your output to a file in binary, you should be fine.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd;
int n;
if ((fd = open ( "output.bin", O_CREAT|O_WRONLY|O_TRUNC, 00666)) == -1) {
printf("Cant open output file\n");
return 0;
}
//-T-e-s-t- Production routine
for (int i = 0; i < 256; i++) {
split(i);
printf("Address: %d\tValue HEX: 0x%X \tValue DEC: %d\n", i, finalResult(),n = finalResult());
write(fd, &n, 2);
}
close(fd);
return 0;
}
when you write the ROM, use a command *like*
./minipro -p 2764@DIP28 -s -w /files/programming/c/lcdmessage/output.bin
minipro does not mention that they support files in binary format.
If you want, I have a way of achiving your goal with less chips, if you are interested, please inquire.
Rue
-
Does the EPROM datasheet guarantee that a 105us write cycle will suffice?
Often they follow a procedure to partially write, verify, and continue writing until verified.
Tim
In the datasheet, page 5 it says it's enough. I added more delays and still the same. At address 0, the Atmega writes 0xff in the EPROM or it dose't write anything at all at that address. other addresses are fine.
OE,CE, and PGM are inverted with an cd4049. Instead of 12V on the VPP, I only have around 10V because of the way i switch the voltage on the pin using an optocopler.
Datasheet: https://datasheet.octopart.com/D27C210-200V10-Intel-datasheet-139899587.pdf
hexdump:
0000000 ff ff 01 00 02 00 03 00 04 00 05 00 06 00 07 00
0000010 08 00 09 00 10 00 11 00 12 00 13 00 14 00 15 00
0000020 16 00 17 00 18 00 19 00 20 00 21 00 22 00 23 00
0000030 24 00 25 00 26 00 27 00 28 00 29 00 30 00 31 00
0000040 32 00 33 00 34 00 35 00 36 00 37 00 38 00 39 00
0000050 40 00 41 00 42 00 43 00 44 00 45 00 46 00 47 00
0000060 48 00 49 00 50 00 51 00 52 00 53 00 54 00 55 00
0000070 56 00 57 00 58 00 59 00 60 00 61 00 62 00 63 00
0000080 64 00 65 00 66 00 67 00 68 00 69 00 70 00 71 00
0000090 72 00 73 00 74 00 75 00 76 00 77 00 78 00 79 00
00000a0 80 00 81 00 82 00 83 00 84 00 85 00 86 00 87 00
00000b0 88 00 89 00 90 00 91 00 92 00 93 00 94 00 95 00
00000c0 96 00 97 00 98 00 99 00 00 01 01 01 02 01 03 01
00000d0 04 01 05 01 06 01 07 01 08 01 09 01 10 01 11 01
00000e0 12 01 13 01 14 01 15 01 16 01 17 01 18 01 19 01
00000f0 20 01 21 01 22 01 23 01 24 01 25 01 26 01 27 01
0000100 28 01 29 01 30 01 31 01 32 01 33 01 34 01 35 01
0000110 36 01 37 01 38 01 39 01 40 01 41 01 42 01 43 01
0000120 44 01 45 01 46 01 47 01 48 01 49 01 50 01 51 01
0000130 52 01 53 01 54 01 55 01 56 01 57 01 58 01 59 01
0000140 60 01 61 01 62 01 63 01 64 01 65 01 66 01 67 01
0000150 68 01 69 01 70 01 71 01 72 01 73 01 74 01 75 01
0000160 76 01 77 01 78 01 79 01 80 01 81 01 82 01 83 01
0000170 84 01 85 01 86 01 87 01 88 01 89 01 90 01 91 01
0000180 92 01 93 01 94 01 95 01 96 01 97 01 98 01 99 01
0000190 00 02 01 02 02 02 03 02 04 02 05 02 06 02 07 02
00001a0 08 02 09 02 10 02 11 02 12 02 13 02 14 02 15 02
00001b0 16 02 17 02 18 02 19 02 20 02 21 02 22 02 23 02
00001c0 24 02 25 02 26 02 27 02 28 02 29 02 30 02 31 02
00001d0 32 02 33 02 34 02 35 02 36 02 37 02 38 02 39 02
00001e0 40 02 41 02 42 02 43 02 44 02 45 02 46 02 47 02
00001f0 48 02 49 02 50 02 51 02 52 02 53 02 54 02 55 02
0000200 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
*
0020000
-
How about using two Padauk PMS152-S16 (datasheet (http://www.padauk.com.tw/upload/doc/PMS152_datasheet_v105_EN_20200609.pdf); $0.08 at LCSC.com (https://lcsc.com/product-detail/Others_PADAUK-Tech-PMS152-S16_C317590.html)) instead, programming them using the free-pdk (https://github.com/free-pdk/) toolchain and hardware programmer?
Both chips are connected to the same 8 inputs, and 5 of the 10 outputs. (The two most significant bits of the 12-bit output value are always zero.)
This leaves an extra pin for latch/update, if you want/need one. (Or, you can extend to 9 bit input and 12-bit output, by supplying only the 7 most significant input bits to the chip providing the most significant 7 output bits.)
The programs themselves are trivial, except that you'll probably want to construct the input and output bit by bit, so you can get an optimal pin layout for your use case. (In particular, the least significant bit of the input always matches the least significant bit of the output, so at most you need 127 look-up entries.)
Each chip only consumes a couple of mA even running at 16 MHz; for this use case, you can use a much lower frequency, dropping the current consumption under 0.1mA.
-
How about using two Padauk PMS152-S16 (datasheet (http://www.padauk.com.tw/upload/doc/PMS152_datasheet_v105_EN_20200609.pdf); $0.08 at LCSC.com (https://lcsc.com/product-detail/Others_PADAUK-Tech-PMS152-S16_C317590.html)) instead, programming them using the free-pdk (https://github.com/free-pdk/) toolchain and hardware programmer?
Both chips are connected to the same 8 inputs, and 5 of the 10 outputs. (The two most significant bits of the 12-bit output value are always zero.)
This leaves an extra pin for latch/update, if you want/need one. (Or, you can extend to 9 bit input and 12-bit output, by supplying only the 7 most significant input bits to the chip providing the most significant 7 output bits.)
The programs themselves are trivial, except that you'll probably want to construct the input and output bit by bit, so you can get an optimal pin layout for your use case. (In particular, the least significant bit of the input always matches the least significant bit of the output, so at most you need 127 look-up entries.)
Each chip only consumes a couple of mA even running at 16 MHz; for this use case, you can use a much lower frequency, dropping the current consumption under 0.1mA.
This chips are really cheap, they are great for series production. As I read from datasheet they are OTP, one time programming. They are no use for me as i keep making mistakes and i'll need 100s of them :-//
This isn't anything comercial, just me having too much free time now and trying to build/learn new things. Thank you anyway for the infos..
-
I got it right! :clap: :clap:
Silly me forgot to connect the EPROM VCC pin to 5V. The breadboard I'm using has the VCC/GND rails split in the middle. Only the one rail for the EEPROM was left unconneted. Thats why the read function didn't work well :clap: :clap:
But somehow it did work but not as expected. What should I do with the unused data/address pins? should they be pulled low or can they be left floating? the unused address pins are pulled low now, but the data unused data pins are left floating.
EEPROM dump after programming.
0000000 00 00 01 00 02 00 03 00 04 00 05 00 06 00 07 00
0000010 08 00 09 00 10 00 11 00 12 00 13 00 14 00 15 00
0000020 16 00 17 00 18 00 19 00 20 00 21 00 22 00 23 00
0000030 24 00 25 00 26 00 27 00 28 00 29 00 30 00 31 00
0000040 32 00 33 00 34 00 35 00 36 00 37 00 38 00 39 00
0000050 40 00 41 00 42 00 43 00 44 00 45 00 46 00 47 00
0000060 48 00 49 00 50 00 51 00 52 00 53 00 54 00 55 00
0000070 56 00 57 00 58 00 59 00 60 00 61 00 62 00 63 00
0000080 64 00 65 00 66 00 67 00 68 00 69 00 70 00 71 00
0000090 72 00 73 00 74 00 75 00 76 00 77 00 78 00 79 00
00000a0 80 00 81 00 82 00 83 00 84 00 85 00 86 00 87 00
00000b0 88 00 89 00 90 00 91 00 92 00 93 00 94 00 95 00
00000c0 96 00 97 00 98 00 99 00 00 01 01 01 02 01 03 01
00000d0 04 01 05 01 06 01 07 01 08 01 09 01 10 01 11 01
00000e0 12 01 13 01 14 01 15 01 16 01 17 01 18 01 19 01
00000f0 20 01 21 01 22 01 23 01 24 01 25 01 26 01 27 01
0000100 28 01 29 01 30 01 31 01 32 01 33 01 34 01 35 01
0000110 36 01 37 01 38 01 39 01 40 01 41 01 42 01 43 01
0000120 44 01 45 01 46 01 47 01 48 01 49 01 50 01 51 01
0000130 52 01 53 01 54 01 55 01 56 01 57 01 58 01 59 01
0000140 60 01 61 01 62 01 63 01 64 01 65 01 66 01 67 01
0000150 68 01 69 01 70 01 71 01 72 01 73 01 74 01 75 01
0000160 76 01 77 01 78 01 79 01 80 01 81 01 82 01 83 01
0000170 84 01 85 01 86 01 87 01 88 01 89 01 90 01 91 01
0000180 92 01 93 01 94 01 95 01 96 01 97 01 98 01 99 01
0000190 00 02 01 02 02 02 03 02 04 02 05 02 06 02 07 02
00001a0 08 02 09 02 10 02 11 02 12 02 13 02 14 02 15 02
00001b0 16 02 17 02 18 02 19 02 20 02 21 02 22 02 23 02
00001c0 24 02 25 02 26 02 27 02 28 02 29 02 30 02 31 02
00001d0 32 02 33 02 34 02 35 02 36 02 37 02 38 02 39 02
00001e0 40 02 41 02 42 02 43 02 44 02 45 02 46 02 47 02
00001f0 48 02 49 02 50 02 51 02 52 02 53 02 54 02 55 02
0000200 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
*
0020000
-
How about using two Padauk PMS152-S16 (datasheet (http://www.padauk.com.tw/upload/doc/PMS152_datasheet_v105_EN_20200609.pdf); $0.08 at LCSC.com (https://lcsc.com/product-detail/Others_PADAUK-Tech-PMS152-S16_C317590.html)) instead, programming them using the free-pdk (https://github.com/free-pdk/) toolchain and hardware programmer?
Both chips are connected to the same 8 inputs, and 5 of the 10 outputs. (The two most significant bits of the 12-bit output value are always zero.)
This leaves an extra pin for latch/update, if you want/need one. (Or, you can extend to 9 bit input and 12-bit output, by supplying only the 7 most significant input bits to the chip providing the most significant 7 output bits.)
The programs themselves are trivial, except that you'll probably want to construct the input and output bit by bit, so you can get an optimal pin layout for your use case. (In particular, the least significant bit of the input always matches the least significant bit of the output, so at most you need 127 look-up entries.)
Each chip only consumes a couple of mA even running at 16 MHz; for this use case, you can use a much lower frequency, dropping the current consumption under 0.1mA.
This chips are really cheap, they are great for series production. As I read from datasheet they are OTP, one time programming. They are no use for me as i keep making mistakes and i'll need 100s of them :-//
This isn't anything comercial, just me having too much free time now and trying to build/learn new things. Thank you anyway for the infos..
There is a version with flash. It's more expensive, of course, but you only need one of them :-)
-
When writing the EPROM, I'd add 10k resistors to ground for each unused address and data pin.
When using the EPROM, I'd connect unused address pins to ground, and leave unused data pins floating.
By the way, here's the Arduino sketch I'd use to write the EPROM. This uses arbitrary pins, and waits for Enter (from Arduino serial monitor) before writing the data. I wrote it according to the datasheet you linked, and it compiles, but is otherwise untested. It does the full write-verify-rewrite-if-necessary thing, too, and reports each address written to the serial monitor. You do need to edit the pin numbers, and whether the logic (EPROM_ constants) match your circuit.
// Chip enable, output enable, program, and programming voltage control pins
#define EPROM_CE 1
#define EPROM_OE 2
#define EPROM_PGM 3
#define EPROM_PDG 4
// Logic used for the above pins
#define EPROM_CE_LOW LOW
#define EPROM_CE_HIGH HIGH
#define EPROM_OE_LOW LOW
#define EPROM_OE_HIGH HIGH
#define EPROM_PGM_LOW LOW
#define EPROM_PGM_HIGH HIGH
#define EPROM_PDG_12V LOW
#define EPROM_PDG_5V HIGH
// Address pins
#define EPROM_A0 5
#define EPROM_A1 6
#define EPROM_A2 7
#define EPROM_A3 8
#define EPROM_A4 9
#define EPROM_A5 10
#define EPROM_A6 11
#define EPROM_A7 12
// Data pins
#define EPROM_D0 13
#define EPROM_D1 14
#define EPROM_D2 15
#define EPROM_D3 16
#define EPROM_D4 17
#define EPROM_D5 18
#define EPROM_D6 19
#define EPROM_D7 20
#define EPROM_D8 21
#define EPROM_D9 22
// Write one word. Returns true if successful, false if failed.
static bool eprom_program(uint8_t address, uint16_t data)
{
const unsigned char data_hi = data >> 8,
data_lo = data;
unsigned char retries = 25;
unsigned char lo, hi;
// Make sure EPROM is in standby, outputs disabled.
digitalWrite(EPROM_CE, EPROM_CE_LOW);
digitalWrite(EPROM_OE, EPROM_OE_LOW);
// Set address.
digitalWrite(EPROM_A0, (address & 1) ? HIGH : LOW);
digitalWrite(EPROM_A1, (address & 2) ? HIGH : LOW);
digitalWrite(EPROM_A2, (address & 4) ? HIGH : LOW);
digitalWrite(EPROM_A3, (address & 8) ? HIGH : LOW);
digitalWrite(EPROM_A4, (address & 16) ? HIGH : LOW);
digitalWrite(EPROM_A5, (address & 32) ? HIGH : LOW);
digitalWrite(EPROM_A6, (address & 64) ? HIGH : LOW);
digitalWrite(EPROM_A7, (address & 128) ? HIGH : LOW);
while (1) {
// Set output data.
pinMode(EPROM_D0, OUTPUT); digitalWrite(EPROM_D0, (data_lo & 1) ? HIGH : LOW);
pinMode(EPROM_D1, OUTPUT); digitalWrite(EPROM_D1, (data_lo & 2) ? HIGH : LOW);
pinMode(EPROM_D2, OUTPUT); digitalWrite(EPROM_D2, (data_lo & 4) ? HIGH : LOW);
pinMode(EPROM_D3, OUTPUT); digitalWrite(EPROM_D3, (data_lo & 8) ? HIGH : LOW);
pinMode(EPROM_D4, OUTPUT); digitalWrite(EPROM_D4, (data_lo & 16) ? HIGH : LOW);
pinMode(EPROM_D5, OUTPUT); digitalWrite(EPROM_D5, (data_lo & 32) ? HIGH : LOW);
pinMode(EPROM_D6, OUTPUT); digitalWrite(EPROM_D6, (data_lo & 64) ? HIGH : LOW);
pinMode(EPROM_D7, OUTPUT); digitalWrite(EPROM_D7, (data_lo & 128) ? HIGH : LOW);
pinMode(EPROM_D8, OUTPUT); digitalWrite(EPROM_D8, (data_hi & 1) ? HIGH : LOW);
pinMode(EPROM_D9, OUTPUT); digitalWrite(EPROM_D9, (data_hi & 2) ? HIGH : LOW);
// Enable chip.
digitalWrite(EPROM_CE, EPROM_CE_HIGH);
delayMicroseconds(2);
// Pulse PGM.
digitalWrite(EPROM_PGM, EPROM_PGM_HIGH);
delayMicroseconds(100);
digitalWrite(EPROM_PGM, EPROM_PGM_LOW);
delayMicroseconds(2);
// Change data lines to inputs.
pinMode(EPROM_D0, INPUT);
pinMode(EPROM_D1, INPUT);
pinMode(EPROM_D2, INPUT);
pinMode(EPROM_D3, INPUT);
pinMode(EPROM_D4, INPUT);
pinMode(EPROM_D5, INPUT);
pinMode(EPROM_D6, INPUT);
pinMode(EPROM_D7, INPUT);
pinMode(EPROM_D8, INPUT);
pinMode(EPROM_D9, INPUT);
// Enable EPROM outputs; provides verification.
digitalWrite(EPROM_OE, EPROM_OE_HIGH);
delayMicroseconds(2);
lo = (digitalRead(EPROM_D0) == HIGH) ? 1 : 0
+ (digitalRead(EPROM_D1) == HIGH) ? 2 : 0
+ (digitalRead(EPROM_D2) == HIGH) ? 4 : 0
+ (digitalRead(EPROM_D3) == HIGH) ? 8 : 0
+ (digitalRead(EPROM_D4) == HIGH) ? 16 : 0
+ (digitalRead(EPROM_D5) == HIGH) ? 32 : 0
+ (digitalRead(EPROM_D6) == HIGH) ? 64 : 0
+ (digitalRead(EPROM_D7) == HIGH) ? 128 : 0;
hi = (digitalRead(EPROM_D8) == HIGH) ? 1 : 0
+ (digitalRead(EPROM_D9) == HIGH) ? 2 : 0;
digitalWrite(EPROM_OE, EPROM_OE_LOW);
// Put chip in standby.
digitalWrite(EPROM_CE, EPROM_CE_LOW);
delayMicroseconds(2);
// Verified correctly?
if (lo == data_lo && hi == data_hi)
return true;
// No more retries?
if (!retries--)
return false;
}
}
void setup() {
Serial.begin(115200);
// Make sure EPROM is in Standby mode
pinMode(EPROM_CE, OUTPUT);
digitalWrite(EPROM_CE, EPROM_CE_LOW);
// Make sure programming voltage is OFF (5V)
pinMode(EPROM_PDG, EPROM_PDG_5V);
// EPROM data output is disabled
pinMode(EPROM_OE, OUTPUT);
digitalWrite(EPROM_OE, HIGH);
// EPROM is not in program mode
pinMode(EPROM_PGM, OUTPUT);
digitalWrite(EPROM_PGM, HIGH);
// Address lines are always outputs.
pinMode(EPROM_A0, OUTPUT);
pinMode(EPROM_A1, OUTPUT);
pinMode(EPROM_A2, OUTPUT);
pinMode(EPROM_A3, OUTPUT);
pinMode(EPROM_A4, OUTPUT);
pinMode(EPROM_A5, OUTPUT);
pinMode(EPROM_A6, OUTPUT);
pinMode(EPROM_A7, OUTPUT);
}
void loop() {
uint16_t addr;
int c;
uint8_t h0, h1, h2;
// Wait for USB connection, if native USB MCU (Leonardo etc.)
while (!Serial);
delay(1000);
// Consume all pending serial input.
while (Serial.available())
Serial.read();
// Output the prompt.
Serial.println(F("Press Enter when ready to write ROM."));
Serial.flush();
// Consume serial input until newline.
do {
c = Serial.read();
} while (c != '\n' && c != '\r');
Serial.println(F("Writing EPROM."));
Serial.flush();
digitalWrite(EPROM_PDG, EPROM_PDG_12V);
delayMicroseconds(2);
// Data construction loops.
addr = 0;
for (h2 = 0; h2 < 10; h2++) {
for (h1 = 0; h1 < 10; h1++) {
for (h0 = 0; h0 < 10; h0++) {
if (addr < 256) {
const uint16_t value = h0 | (h1 << 4) | (((uint16_t)h2) << 8);
Serial.print(addr, DEC);
if (eprom_program(addr, value)) {
Serial.println(F(": OK"));
Serial.flush();
addr++;
} else {
Serial.println(F(": Failed"));
Serial.flush();
addr = 257;
}
}
}
}
}
digitalWrite(EPROM_PDG, EPROM_PDG_5V);
if (addr == 256) {
Serial.println(F("EPROM written successfully."));
} else {
Serial.println(F("EPROM write failed."));
}
Serial.flush();
}
-
This chips are really cheap, they are great for series production. As I read from datasheet they are OTP, one time programming. They are no use for me as i keep making mistakes and i'll need 100s of them :-//
This isn't anything comercial, just me having too much free time now and trying to build/learn new things. Thank you anyway for the infos..
There is a version with flash. It's more expensive, of course, but you only need one of them :-)
Yes, the Padauk PFS154-S16, which costs $0.10 at LCSC (https://lcsc.com/product-detail/Others_PADAUK-Tech-PFS154-S16_C317613.html), although you must buy them in sets of five. It's not exact equivalent to the OTP one, but is programmable using the free tools, and I guess $0.20 (for two) is still cheap enough...
Of course, one must also get/build the programmer (https://github.com/free-pdk/easy-pdk-programmer-hardware). LCSC seems to be out of STM32F072C8T6's, so one would have to source those from Digikey/Mouser/etc.
-
Since you're using the runtime-dispatch-on-pin digitalWrite() anyway I'd shorten that program a ton by using:
char eprom_a[] = {5, 6, 7, 8, 9, 10, 11, 12};
char eprom_d[] = {13, 14, 15, 16, 17, 18, 19, 20, 21, 22};
... and then looping over them instead of unrolling. It'll be slower, but not perceptibly.
// Set address.
for (int i=0; i<8; ++i) digitalWrite(eprom_a[i], (address & (1 << i)) ? HIGH : LOW);
Etc.
(I'm sure you know this .. it's for the OP)
-
It'll be slower, but not perceptibly.
The difference is huge on AVRs using e.g. Teensyduino, as there digitalWrite() (https://github.com/PaulStoffregen/cores/blob/master/teensy/core_pins.h#L825) simplifies to a single instruction if the pin and state are literal constants, three if the pin is a literal constant. The array/variable version (including when pin numbers are declared as const int instead of macros) uses the slow Arduino jump table approach.
The actual reason for using pin-specific macros, however, is that it makes it much easier to compare the code settings to the breadboard or PCB circuit. So much so that if I were to change the code to use arrays instead, I'd put the macros, not direct pin numbers, in the array initializer.
(I know I would need to check the pins between the board and the code twice, or I'd get at least one wrong. It might make the code longer than necessary, but it would for sure help me get the entire project right. I don't know whether others need that sort of thing, though.)
If having the pins change state as close to simultaneously as possible was important, then I'd use a different approach, shadowing the N output pin state registers. Each data and address bit would correspond to a constant mask (of N bytes), applied iff the corresponding bit is set. The N output registers take one cycle to set each, so on a 16 MHz AVR, the transition would take 250ns if the pins are across four ports; or 188ns if the pins are across only three ports. Constructing the new port state would take a couple of dozen cycles, though.
-
It'll be slower, but not perceptibly.
The difference is huge on AVRs using e.g. Teensyduino, as there digitalWrite() (https://github.com/PaulStoffregen/cores/blob/master/teensy/core_pins.h#L825) simplifies to a single instruction if the pin and state are literal constants, three if the pin is a literal constant. The array/variable version (including when pin numbers are declared as const int instead of macros) uses the slow Arduino jump table approach.
In this case, to get speed you probably needed:
if (address & 1) digitalWrite(EPROM_A0, HIGH) else digitalWrite(EPROM_A0, LOW);
if (address & 2) digitalWrite(EPROM_A1, HIGH) else digitalWrite(EPROM_A1, LOW);
:
Plus I'd write a macro so I could write:
SET_BITPIN(address, EPROM_A, 0);
SET_BITPIN(address, EPROM_A, 1);
:
Or write some Perl/Python/C to generate it. (Oh how I miss LISP macros when I'm programming in something lesser...)
-
always ground unused inputs!
congraduations!
-
It'll be slower, but not perceptibly.
The difference is huge on AVRs using e.g. Teensyduino, as there digitalWrite() (https://github.com/PaulStoffregen/cores/blob/master/teensy/core_pins.h#L825) simplifies to a single instruction if the pin and state are literal constants, three if the pin is a literal constant. The array/variable version (including when pin numbers are declared as const int instead of macros) uses the slow Arduino jump table approach.
In this case, to get speed you probably needed:
if (address & 1) digitalWrite(EPROM_A0, HIGH) else digitalWrite(EPROM_A0, LOW);
if (address & 2) digitalWrite(EPROM_A1, HIGH) else digitalWrite(EPROM_A1, LOW);
:
Well, not really... this is avr-gcc, after all. When EPROM_A0 etc. are literal constants, and optimizations are enabled, using the Teensyduino AVR digitalWrite(),
digitalWrite(EPROM_A0, (address & 1) ? HIGH : LOW);
digitalWrite(EPROM_A1, (address & 2) ? HIGH : LOW);
digitalWrite(EPROM_A2, (address & 4) ? HIGH : LOW);
digitalWrite(EPROM_A3, (address & 8) ? HIGH : LOW);
and
if (address & 1) digitalWrite(EPROM_A0, HIGH); else digitalWrite(EPROM_A0, LOW);
if (address & 2) digitalWrite(EPROM_A1, HIGH); else digitalWrite(EPROM_A1, LOW);
if (address & 4) digitalWrite(EPROM_A2, HIGH); else digitalWrite(EPROM_A2, LOW);
if (address & 8) digitalWrite(EPROM_A3, HIGH); else digitalWrite(EPROM_A3, LOW);
do compile to the same code – SBRS/SBRC (Skip if Bit in Register is Set/Cleared), RJMP, SBI (Set Bit in I/O register) in one path, and CBI (Clear Bit in I/O register), and a RJMP in SBI or CBI path.
The core Arduino AVR digitalWrite() is much slower, as it does not do that sort of optimization at all.
On say an ATmega32u4, one can use
#define DATA_0 D,3
#define DATA_1 D,2
#define DATA_2 D,1
#define DATA_3 D,0
#define DATA_4 D,4
#define DATA_5 C,6
#define DATA_6 D,7
#define DATA_7 E,6
#define DATA_8 B,4
#define DATA_9 B,5
#define ADDR_0 F,4
#define ADDR_1 F,5
#define ADDR_2 F,6
#define ADDR_3 F,7
#define ADDR_4 B,1
#define ADDR_5 B,3
#define ADDR_6 B,2
#define ADDR_7 B,6
// Helper macros
#define MERGE2_(a, b) a ## b
#define MERGE2(a, b) MERGE2_(a, b)
#define SET_(prefix,name,bit) (MERGE2(prefix,name) |= 1 << bit)
#define SET(...) SET_(__VA_ARGS__)
static volatile unsigned char high_B = 0,
high_C = 0,
high_D = 0,
high_E = 0,
high_F = 0;
void set_outputs(const uint8_t addr, const uint16_t data)
{
unsigned char port_B, port_C, port_D, port_E, port_F;
port_B = high_B;
port_C = high_C;
port_D = high_D;
port_E = high_E;
port_F = high_F;
if (addr & 1) SET(port_, ADDR_0);
if (addr & 2) SET(port_, ADDR_1);
if (addr & 4) SET(port_, ADDR_2);
if (addr & 8) SET(port_, ADDR_3);
if (addr & 16) SET(port_, ADDR_4);
if (addr & 32) SET(port_, ADDR_5);
if (addr & 64) SET(port_, ADDR_6);
if (addr & 128) SET(port_, ADDR_7);
if (data & 1) SET(port_, DATA_0);
if (data & 2) SET(port_, DATA_1);
if (data & 4) SET(port_, DATA_2);
if (data & 8) SET(port_, DATA_3);
if (data & 16) SET(port_, DATA_4);
if (data & 32) SET(port_, DATA_5);
if (data & 64) SET(port_, DATA_6);
if (data & 128) SET(port_, DATA_7);
if (data & 256) SET(port_, DATA_8);
if (data & 512) SET(port_, DATA_9);
PORTB = port_B;
PORTC = port_C;
PORTD = port_D;
PORTE = port_E;
PORTF = port_F;
}
which sets all data and output pins within five clock cycles (313 ns at 16 MHz), taking about 50 cycles (3.13 µs at 16 MHz) or so, total. The pin order is completely arbitrary, and won't affect the speed. The high_X variables need to "shadow" the port registers, containing 1 for non-address/data output pins that should stay high, as well as for input pins where the internal pullup should be enabled. To be any faster, one needs to rearrange the address and data pins to match the registers. (These can be found in the microcontroller schematics, or in the Arduino internal headers for that board.)
Unfortunately, this faster method does not play nicely with Arduino libraries, because digitalWrite(non-address/data-pin, HIGH/LOW) and pinMode(any-pin, INPUT/INPUT_PULLUP) do not update the "shadow" values, so it is only really useful if one writes AVR code on bare metal, i.e. using avr-gcc and avr-libc, without external libraries.
(I do not recommend using this faster method, though. I just wanted to show what I was talking about earlier, if the pins should transition close together, but one wants to use arbitrary pins for each bit.)
-
I would have been lazy and attacked your C program output with some editor macros to convert it to text that looked like a C structure definition with a .section attribute, and then used the standard compiler tools (gcc + objcopy, for gcc) to output just that section to a .hex file.
(or, you know, objcopy would have taken a raw binary file and output a .hex as well, I guess.)
-
Quick update. I got it all working. I will post the final improved code that you guys suggested. Thank you all for all your support.
https://youtu.be/_Y2V8trDlc0
-
However another problem has arrived. Before waiting for the minipro programmer to come, I made an arduino sketch for an Atmega32 in order to program the EPROM. It works fine but at the address 0 it writes 0xff instead of 0 and I have no idea why... All other addresses and data is fine. The code is messy.
After erasing the EEPROM, all cells contain 0xFF. Your programmer starts writing from address 1, skips 0, and so there remains 0xFF.