This assignment is done in class on 4/19/2012.
This assignment is due on 4/23/2012.
Create a new directory called transistors:
cd cse251 mkdir transistors cd transistors
Create this main program file transistors.c:
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include "transistors.h"
/*
* Name : < insert name here >
* Description : Simple transistor description example program
*/
int main()
{
printf("transistors!\n");
}
And create this empty header file transistors.h:
#ifndef TRANSISTORS_H #define TRANSISTORS_H #endif
Next, create a Makefile that builds this program from its component parts.
Be
sure it compiles and runs. I'll not indicate
specific places to compile very often. You will need to do so regularly.
We are going to create a program that lets us input common transistors. I need a way to describe a transistor. For that I will use a struct. Add this code to transistors.h:
/* Types */
#define NPN 1
#define PNP 2
/* Cases */
#define TO18 1
#define TO92A 2
#define TO92C 3
#define TO220 4
#define TO39 5
struct Transistor
{
char number[10];
int type; /* NPN, PNP, etc. */
int caseStyle; /* TO18, etc. */
double pmax; /* Maximum power dissipation watts */
double icmax; /* Maximum collector current amps */
};
Of course, this goes between the include guards. Don't put it before the #ifndef or after the #endif.
Now add this variable to the function main:
int main()
{
struct Transistor t1;
printf("transistors!\n");
}
Be
sure it compiles and runs okay. We're not doing anything yet, but be sure the
syntax is right.
I have created a variable named t1 of type struct Transistor. We can populate it like this:
strcpy(t1.number, "2N3053");
t1.type = NPN;
t1.caseStyle = TO39;
t1.pmax = 0.500; /* 500mw */
t1.icmax = 0.700; /* 700ma */
Add this code to the end of the main function and be sure it compiles and runs.
Now, create a new file io.c and add this function to it:
void DisplayTransistor(struct Transistor tran)
{
printf("Number: %s\n", tran.number);
switch(tran.type)
{
case NPN:
printf("Type: NPN\n");
break;
case PNP:
printf("Type: PNP\n");
break;
}
printf("pMax: %.3f\n", tran.pmax);
printf("icMax: %.3f\n", tran.icmax);
}
We are going to put our input/output functions into this .c file. You are going to have to #include <stdio.h> in io.c.
Put the declaration for this function into transistors.h after the Transistor struct has been created:
void DisplayTransistor(struct Transistor tran);
Add
io.c to your Makefile and be sure it compiles and runs okay.
Now add this line of code to the end of the main function to display the transistor we just entered. Click here if you are unsure where this goes.
DisplayTransistor(t1);
int main()
{
struct Transistor t1;
printf("transistors!\n");
strcpy(t1.number, "2N3053");
t1.type = NPN;
t1.caseStyle = TO39;
t1.pmax = 0.500; /* 500mw */
t1.icmax = 0.700; /* 700ma */
DisplayTransistor(t1);
}
When you run this program you will see the transistor you entered displayed:
cbowen@ubuntu:~/cse251/transistors$ ./transistors transistors! Number: 2N3053 Type: NPN pMax: 0.500 icMax: 0.700
I would like to have code to enter a transistor. One of the great things about a struct is we can not only pass it to a function, we can return it from a function.
Add this function to io.c and put the declaration in transistors.h:
struct Transistor InputTransistor()
{
struct Transistor t1;
strcpy(t1.number, "2N3053");
t1.type = NPN;
t1.caseStyle = TO39;
t1.pmax = 0.500; /* 500mw */
t1.icmax = 0.700; /* 700ma */
return t1;
}
Because this function uses strcpy, you are going to have to #include <string.h> in io.c.
Now change the main function to this:
int main()
{
struct Transistor t1;
printf("transistors!\n");
t1 = InputTransistor();
DisplayTransistor(t1);
}
Notice how we have moved the code to create a transistor to InputTransistor(). I put in dummy code initially to get the function written, compiling, and working. Now we'll fill in the code to input a transistor. In InputTransistor, replace this line:
strcpy(t1.number, "2N3053");
with:
scanf("%s", t1.number);
When you run the program you should now be able to input the transistor number. The rest is all still generated automatically.
A problem with this type of input is that it is not very safe or robust. Let's add a couple more input operations. Replace these two lines of code:
t1.pmax = 0.500; /* 500mw */
t1.icmax = 0.700; /* 700ma */
with this:
printf("Input pMax: ");
scanf("%lf", &t1.pmax);
printf("Input icMax: ");
scanf("%lf", &t1.icmax);
Now run the program. When it asks for pMax, enter "a". See what happens. Because it is looking for a floating point number, when it sees a, it stops. Then the next scanf also stops. Also, if you enter a transistor number greater than 9 characters long, you should cause the program to fail. Try running the program and type this for the number:
2N2222 0.5 0.7
You will notice it continues to get numbers from that first line.
Let's create a more robust and safe way to input a string. Add this function to io.c:
void InputString(char *str, int max)
{
char buffer[100];
/* Get a line of up to 100 characters */
fgets(buffer, sizeof(buffer), stdin);
/* Remove any stray newlines from the buffer */
while (buffer[0] == '\n')
fgets(buffer, sizeof(buffer), stdin);
/* Remove any \n we may have input */
if(strlen(buffer) > 0)
buffer[strlen(buffer)-1] = '\0';
/* Copy up to max characters to our string */
strncpy(str, buffer, max);
str[max-1] = '\0';
}
Read this carefully and be sure you understand how it works. Now, change this line in InputTransistor:
scanf("%s", t1.number);
to:
InputString(t1.number, sizeof(t1.number));
You should be able to enter any string you want for the transistor number with no problems.
Now we'll make a more robust function to input a double value:
double InputPositiveValue(char *prompt)
{
char buffer[100];
double value = 0;
printf("%s", prompt);
/* Get a line of up to 100 characters */
fgets(buffer, sizeof(buffer), stdin);
/* Remove any \n we may have input */
if(strlen(buffer) > 0)
buffer[strlen(buffer)-1] = '\0';
sscanf(buffer, "%lf", &value);
return value;
}
This function gets a line of text. It then uses the function sscanf to scan off the value. sscanf works like scanf, except is reads from a string instead of the standard input device.
Replace these two lines in InputTransistor:
printf("Input pMax: ");
scanf("%lf", &t1.pmax);
with:
t1.pmax = InputPositiveValue("Input pMax: ");
Notice how we use a function to create a more robust and safe way to input the maximum power.
Of course, this does not ensure the value is positive. For that we need to add a loop to the InputPositiveValue function:
double InputPositiveValue(char *prompt)
{
char buffer[100];
double value = 0;
do
{
printf("%s", prompt);
/* Get a line of up to 100 characters */
fgets(buffer, sizeof(buffer), stdin);
/* Remove any \n we may have input */
if(strlen(buffer) > 0)
buffer[strlen(buffer)-1] = '\0';
sscanf(buffer, "%lf", &value);
} while(value <= 0);
return value;
}
We now have a more robust way to enter values.
At this point do the following tasks:
1. Change the input for t1.icmax to use InputPositiveValue.
2. Create a safe and robust function to input the transistor type. It should look something like this:
int InputTransistorType(char *prompt)
{
/* ... */
}
And you should use it in InputTransistor like this:
t1.type = InputTransistorType("Input type: ");
3. Add code to display the case style. The options can be found in transistors.h.
4. Create a safe and robust function to input the transistor case style.
Many programmers get tired of typing "struct". We can use typedef to avoid that problem. Change the definition of struct Transistor to:
typedef struct Transistor
{
char number[10];
int type; /* NPN, PNP, etc. */
int caseStyle; /* TO18, etc. */
double pmax; /* Maximum power dissipation watts */
double icmax; /* Maximum collector current amps */
} Tran;
This should compile and run exactly the same as before. But, now we can refer to our transistor using the type Tran. Change the declaration of the variable t1 in the main function to this:
int main()
{
Tran t1;
printf("transistors!\n");
t1 = InputTransistor();
DisplayTransistor(t1);
}
This should work exactly the same, but should avoid you having to type "struct" all of the time. For example, we can change the function DisplayTransistor to this:
void DisplayTransistor(Tran tran)
Try changing it to that. Be sure to change both the function and the declaration in transistors.h.
Change the main function to this:
int main()
{
int i;
Tran trans[3];
printf("transistors!\n");
for(i=0; i<3; i++)
{
trans[i] = InputTransistor();
}
printf("\nThe transistors:\n");
DisplayTransistor(trans[0]);
}
When you run this you should be able to enter three transistors. If you are wanting some real values to use, here are some examples:
| NPN transistors | |||||||
| Code | Type | Case style |
IC max. |
VCE max. |
hFE min. |
Ptot max. |
Category (typical use) |
| BC107 | NPN | TO18 | 100mA | 45V | 110 | 300mW | Audio, low power |
| BC108 | NPN | TO18 | 100mA | 20V | 110 | 300mW | General purpose, low power |
| BC108C | NPN | TO18 | 100mA | 20V | 420 | 600mW | General purpose, low power |
| BC109 | NPN | TO18 | 200mA | 20V | 200 | 300mW | Audio (low noise), low power |
| BC182 | NPN | TO92C | 100mA | 50V | 100 | 350mW | General purpose, low power |
| BC182L | NPN | TO92A | 100mA | 50V | 100 | 350mW | General purpose, low power |
| BC547B | NPN | TO92C | 100mA | 45V | 200 | 500mW | Audio, low power |
| BC548B | NPN | TO92C | 100mA | 30V | 220 | 500mW | General purpose, low power |
| BC549B | NPN | TO92C | 100mA | 30V | 240 | 625mW | Audio (low noise), low power |
| 2N3053 | NPN | TO39 | 700mA | 40V | 50 | 500mW | General purpose, low power |
| BFY51 | NPN | TO39 | 1A | 30V | 40 | 800mW | General purpose, medium power |
| BC639 | NPN | TO92A | 1A | 80V | 40 | 800mW | General purpose, medium power |
| TIP29A | NPN | TO220 | 1A | 60V | 40 | 30W | General purpose, high power |
| TIP31A | NPN | TO220 | 3A | 60V | 10 | 40W | General purpose, high power |
| TIP31C | NPN | TO220 | 3A | 100V | 10 | 40W | General purpose, high power |
| TIP41A | NPN | TO220 | 6A | 60V | 15 | 65W | General purpose, high power |
| 2N3055 | NPN | TO3 | 15A | 60V | 20 | 117W | General purpose, high power |
| PNP transistors | |||||||
| Code | Structure | Case style |
IC max. |
VCE max. |
hFE min. |
Ptot max. |
Category (typical use) |
| BC177 | PNP | TO18 | 100mA | 45V | 125 | 300mW | Audio, low power |
| BC178 | PNP | TO18 | 200mA | 25V | 120 | 600mW | General purpose, low power |
| BC179 | PNP | TO18 | 200mA | 20V | 180 | 600mW | Audio (low noise), low power |
| BC477 | PNP | TO18 | 150mA | 80V | 125 | 360mW | Audio, low power |
| BC478 | PNP | TO18 | 150mA | 40V | 125 | 360mW | General purpose, low power |
| TIP32A | PNP | TO220 | 3A | 60V | 25 | 40W | General purpose, high power |
| TIP32C | PNP | TO220 | 3A | 100V | 10 | 40W | General purpose, high power |
Add a loop to output the three transistors as well.
As you can see, arrays work the same for structs as they did for other values.
A problem with this approach is that we may not know how may transistors to input. Suppose I want to be able to input a variable number of transistors. For this purpose, we are going to use dynamic memory allocation.
Change the main function to this version:
int main()
{
int i;
Tran *trans;
int numTrans = 0;
printf("transistors!\n");
/* Allocate space for one transistor */
trans = malloc(sizeof(Tran));
numTrans = 1;
/* Input the transistor */
trans[0] = InputTransistor();
/* Output the transistors */
printf("\nThe transistors:\n");
for(i=0; i<numTrans; i++)
{
DisplayTransistor(trans[i]);
}
/* Free the memory */
free(trans);
}
You will have to #include <stdlib.h> to use malloc and free.
The function malloc allocates memory to your program. The size we are allocating is the size of one of our transistor structs. I'm also keeping track of the fact I have space for one transistor in numTrans. After allocation, the space works just like an array, in this case of size 1. So, this code works the same as if I had declared:
Tran trans[1];
But, what is interesting here is that I can now increase that allocate any size I want. For example, if I need space for 100 transistors, I would allocate it like this:
trans = malloc(sizeof(Tran) * 100);
numTrans = 100;
At the end of the program you will see a function called free. The function malloc has asked the operating system for some memory. It has given it to you. It expects you to return it when you are through with it. That's what free does. Only call free on a pointer to memory you have allocated.
Suppose I have 10 transistors, but I want to add one. When we use malloc, we can make the allocated memory larger using realloc. Add this code after the call to InputTransistor:
/* Increase the space by one transistor */
trans = realloc(trans, sizeof(Tran) * (numTrans + 1));
numTrans++;
trans[numTrans-1] = InputTransistor();
Now we have the ability to save records, such as the description of a student or an inventory item. And, because we now use malloc and realloc, we can store any number we want to.
1. Be sure you completed the tasks mentioned earlier. You should be able to input and output all of the fields that describe our transistors.
2. Make sure your Makefile has a clean rule that will delete the file transistors (the executable) and the .o files (*.o)
3. Add code to the function main to input any number of transistors. After you enter each transistor, it should ask this question:
Would you like to enter another transistor (Y/N)?
If the users enters "Y" or "y" (either case), input one more transistor. When the user enters "N" or "n", display all of the transistors that have been entered. Be sure your input for the yes/no response is safe and robust.
When you are done, do the following:
A: Do a make clean to remove the executable and intermediate object files.
B: Type these commands:
cd .. tar cvzf transistors.tar.gz transistors
This will create a file transistors.tar.gz. This is the file you will turn in.
Turn in transistors.tar.gz via http://www.cse.msu.edu/handin