November 25, 2013
by jmuthe
|
Hello I saw the "USB Servo-Guided Water Squirter" in the tutorial section and I thought it was interesting how the user could use Putty to intput and output data between the microcontroller and the computer screen. I was wondering how I could take it a step further and output that data to a text file. I also want to know how to put data into a text file and have it sent to the microcontroller. When I tried to look up my answer on this forum, I think I found some examples but they all used Python programming. I was wondering if it is possible to do it in C and if so how would it be done? Does anybody have a simple bare-bone sample program that does this? Thanks for your help.
|
November 26, 2013
by scootergarrett
|
I was all about this a little while ago. Anyways a C program can talk to the microprocessor through the serial com that is used to load programs. The microprocessor end is easy. I had more trouble with the C program. The C program needs to set up with the com port then there is a function to send or receive a string over the com port. The data must then be parsed out of the string. My code below is some of the code form this project
Microprocessor sends A/D value to com port 100 times a second
// Analog Sensor.c
// for NerdKits with ATmega328p
// scootergarrett
// Outputs raw data 0000-1024 for most recient A/D conversion from port PC0
// PIN DEFINITIONS:
// PC0 -- Analog input
#define F_CPU 14745600
#include <stdio.h>
#include <math.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>
#include "../libnerdkits/io_328p.h"
#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"
volatile uint8_t OverFlowFlag = 0;
/// Returns 10 bit A/D conversiong of port ///
uint16_t adc_read(int port)
{
ADMUX = port;
ADCSRA |= (1<<ADSC);
while(ADCSRA&(1<<ADSC));
return (ADCL+(ADCH<<8));
}
/// Writes a 16 bit number to the computer ///
void uart_write16(uint16_t x)
{
// wait for empty receive buffer //
while ((UCSR0A & (1<<UDRE0))==0);
// send LSBF //
UDR0 = x;
// wait for empty receive buffer //
while ((UCSR0A & (1<<UDRE0))==0);
// send MSBL //
UDR0 = *((char*)&x + 1);
return;
}
int main()
{
DDRB |= (1<<PB1); // Make PC3 an output for testing
// start up the Analog to Digital Converter
ADMUX = 0;
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
ADCSRA |= (1<<ADSC);
// start up the serial port
uart_init();
FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
stdin = stdout = &uart_stream;
//Interrupt setup stuff
TCCR1B |= (1<<CS12) | (1<<CS10) | (1<<WGM12);
TIMSK1 |= (1<<OCIE1A);
OCR1A = 143; //Interrupt timming 14400 is 1 second 36 is 400Hz
sei();
while(1)
{
if(OverFlowFlag)
{
uart_write16(adc_read(0));
/// Write 'f' to show end because FF would not come up in a A/D conversion ///
uart_write(0xf);
uart_write(0xf);
OverFlowFlag = 0;
}
}
return 0;
}
ISR(TIMER1_COMPA_vect)
{
OverFlowFlag = 1;
return;
}
C code reads and displays
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <Windows.h>
#include <conio.h>
#define n 4 // Number of bits sent per communication
int main()
{
HANDLE hSerial;
DCB dcbSerialParams = {0};
COMMTIMEOUTS timeouts = {0};
char szBuff[n] = {0};
DWORD dwBytesRead = 1;
int k=0, h;
short T;
short *PT;
char c;
Sleep(1000);
{ // Serial Communication stuff including timming numbers //
hSerial = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if(hSerial==INVALID_HANDLE_VALUE)
{
if(GetLastError()==ERROR_FILE_NOT_FOUND)
printf("ERROR creating file\n");
printf("ERROR creating file Other\n");
return 1;
}
dcbSerialParams.DCBlength=sizeof(dcbSerialParams);
if (!GetCommState(hSerial, &dcbSerialParams))
printf("error getting state\n");
dcbSerialParams.BaudRate=CBR_115200;
dcbSerialParams.ByteSize=8;
dcbSerialParams.StopBits=ONESTOPBIT;
dcbSerialParams.Parity=NOPARITY;
if(!SetCommState(hSerial, &dcbSerialParams))
printf("error setting serial port state\n");
timeouts.ReadIntervalTimeout=MAXWORD;
timeouts.ReadTotalTimeoutConstant=10000;
timeouts.ReadTotalTimeoutMultiplier=0;
timeouts.WriteTotalTimeoutConstant=500;
timeouts.WriteTotalTimeoutMultiplier=100;
if(!SetCommTimeouts(hSerial, &timeouts))
printf("error occureed setting timeouts\n");
}
// reads one char at a time untill there is an FF (end of string) then moves on //
printf("\nSynchronizing\n");
do
{
ReadFile(hSerial, szBuff, 2, &dwBytesRead, NULL);
}while(szBuff[0] != 0xF && szBuff[1] != 0xF);
PT = (short*)&szBuff;
// Loop of plotting //
while(1)
{
// read the serial data and puts in string 'szBuff' //
ReadFile(hSerial, szBuff, n, &dwBytesRead, NULL);
// Error checking has to be an '0xFF' at end of string or something is wrong //
if(szBuff[2] != 0xF && szBuff[3] != 0xF)
{
printf("\n\nERRER :%s \tk: %i\tBR: %i\n\n", szBuff, k, dwBytesRead);
break;
}
// Parce the data into int starting from end working back //
T = *PT;
printf("%current number %ld\n", T);
if(kbhit()) // check if a key has been hit and exit if so
{
c = getchar();
}
}
CloseHandle(hSerial);
return 0;
}
I’m assuming you know C to the point where you could figure out what my code is doing, if you don’t let me know. |
December 02, 2013
by jmuthe
|
I appreciate the response but I am a little confused by it. First of all, I see two pieces of code that you wrote so I was a little confused with what to do with them. I sent the the first piece of code to the microprocessor and I compiled the second piece of code to my PC. I had the microprocessor connected to the computer and then I ran the second code on the computer, while simultaneously running the other code on the ATMEL328 chip. The result was that I kept seeing the message "current number 4" printed on the screen. I used a potentiometer to control the voltage instead of your alcohol gas sensor but no matter what I set it to I kept getting the same message. The number "4" never changed.
However, my goal is not to simply make the Nerdkit communicate to the computer and display values on the screen. I want to actually make it either create a text file (like in notepad) or read information from a text file. For example, this is an example of a C code that creates a file called "out" and writes the numbers 1 to 10 on it.
#include <stdio.h>
#define MAX 10
int main()
{
FILE *f;
int x;
f=fopen("out","w");
if (!f)
return 1;
for(x=1; x<=MAX; x++)
fprintf(f,"%d\n",x);
fclose(f);
return 0;
}
This is a code that reads 1000 character from a text file called "infile" and prints them on the screen.
#include <stdio.h>
int main()
{
FILE *f;
char s[1000];
f=fopen("infile","r");
if (!f)
return 1;
while (fgets(s,1000,f)!=NULL)
printf("%s",s);
fclose(f);
return 0;
}
Both examples are from "HowStuffWorks.com" The following code that I wrote in the bottom is basically the simpliest C code that I could use to write to he PC. I basically used the "USB Servo-Guided Water Squirter" video in the tutorial section and got rid of everything in it except the part of the program that prints to the PC. The user has to open up a PUTTY link just like in the "USB Servo-Guided Water Squirter" and the program asks the user to type a number on the keyboard and it simply doubles it and displays it on the LCD and PUTTY screen. How do I modify this program to not just print it on the screen but to also create a text file and write the information to it just like in my first program shown above.
#include <stdio.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <inttypes.h>
#include "../libnerdkits/io_328p.h"
#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"
int main() {
int a=0;
int b=0;
lcd_init();
FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
uart_init();
FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
stdin = stdout = &uart_stream;
while(1) {
// Wait for a character to be sent to the serial port.
a = uart_read();
a=a-48; //This will convert the ASCII character on the keyboard with the corresponding number.
b=2*a;
// Print the values to the serial port.
printf_P(PSTR("a = %d. b = %d \r\n"), a,b);
//Print the values to the LCD.
lcd_line_two();
fprintf_P(&lcd_stream, PSTR("a = %d. b = %d "), a,b);
}
return 0;
}
|
December 03, 2013
by scootergarrett
|
I'm not sure why it always returns 4. I wrote some more code that is a little more stripped down. This first set if for the microprocessor, it samples PC0 and then sends the data in raw format to the computer. I send 0xFF after for error checking.
// Analog SendData.c
// for NerdKits with ATmega328p
// Outputs raw data 0000-1024 for most recient A/D conversion from port PC0
// PIN DEFINITIONS:
// PC0 -- Analog input
#define F_CPU 14745600
#include <stdio.h>
#include <math.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>
#include "../libnerdkits/io_328p.h"
#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"
volatile uint8_t OverFlowFlag = 0;
uint16_t adc_read(int port)
{
ADMUX = port;
ADCSRA |= (1<<ADSC);
while(ADCSRA&(1<<ADSC));
return (ADCL+(ADCH<<8));
}
void uart_write16(uint16_t x)
{
// wait for empty receive buffer //
while ((UCSR0A & (1<<UDRE0))==0);
// send LSBF //
UDR0 = x;
// wait for empty receive buffer //
while ((UCSR0A & (1<<UDRE0))==0);
// send MSBL //
UDR0 = *((char*)&x + 1);
return;
}
int main()
{
DDRB |= (1<<PB1); // Make PC3 an output for testing
// start up the Analog to Digital Converter
ADMUX = 0;
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
ADCSRA |= (1<<ADSC);
// start up the serial port
uart_init();
FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
stdin = stdout = &uart_stream;
//Interrupt setup stuff
TCCR1B |= (1<<CS12) | (1<<CS10) | (1<<WGM12);
TIMSK1 |= (1<<OCIE1A);
OCR1A = 143; //Interrupt timming 14400 is 1 second 36 is 400Hz
sei();
while(1)
{
if(OverFlowFlag)
{
uart_write16(adc_read(0));
uart_write(0xf);
uart_write(0xf);
OverFlowFlag = 0;
}
}
return 0;
}
ISR(TIMER1_COMPA_vect)
{
OverFlowFlag = 1;
return;
}
Then this code is compiled and run on my PC after the microprocessor is up and running. it sets up the communication then starts pulling values. It prints every 50th to the screen and saves all the data to a text file 'Data.txt'.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <Windows.h>
#include <conio.h>
#define n 4 // Number of bits sent per communication
// two data bits then 0xFF
int main()
{
HANDLE hSerial;
DCB dcbSerialParams = {0};
COMMTIMEOUTS timeouts = {0};
DWORD dwBytesRead = 1;
char szBuff[n] = {0};
FILE *DataFile;
int k=0;
short A = 0;
short *PT;
Sleep(100);
{ /// Serial Communication stuff including timming numbers ///
/// Make sure Com port is correct 'COM1' ///
hSerial = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if(hSerial==INVALID_HANDLE_VALUE)
{
if(GetLastError()==ERROR_FILE_NOT_FOUND)
printf("ERROR creating file\n");
printf("ERROR creating file Other\n");
return 1;
}
dcbSerialParams.DCBlength=sizeof(dcbSerialParams);
if (!GetCommState(hSerial, &dcbSerialParams))
printf("error getting state\n");
dcbSerialParams.BaudRate=CBR_115200;
dcbSerialParams.ByteSize=8;
dcbSerialParams.StopBits=ONESTOPBIT;
dcbSerialParams.Parity=NOPARITY;
if(!SetCommState(hSerial, &dcbSerialParams))
printf("error setting serial port state\n");
timeouts.ReadIntervalTimeout=MAXWORD;
timeouts.ReadTotalTimeoutConstant=10000;
timeouts.ReadTotalTimeoutMultiplier=0;
timeouts.WriteTotalTimeoutConstant=500;
timeouts.WriteTotalTimeoutMultiplier=100;
if(!SetCommTimeouts(hSerial, &timeouts))
printf("error occureed setting timeouts\n");
}
// reads one char at a time untill there is an A (end of string) then moves on //
printf("\nSynchronizing\n");
do
{
ReadFile(hSerial, szBuff, 2, &dwBytesRead, NULL);
}while(szBuff[0] != 0xF && szBuff[1] != 0xF);
PT = (short*)&szBuff; // Points at start of the string where the data will be
DataFile = fopen("Data.txt", "w"); // Data file
// Loop //
while(1)
{
// read the serial data and puts in string 'szBuff' //
ReadFile(hSerial, szBuff, n, &dwBytesRead, NULL);
// Error checking has to be an '0xFF' at end of string or something is wrong //
if(szBuff[2] != 0xF && szBuff[3] != 0xF)
{
printf("\n\nERRER :%s \tk: %i\tBR: %i\n\n", szBuff, k, dwBytesRead);
break;
}
// Get data out of the strinf into A //
A = *PT;
/// Only print every 50th ///
if(k%50 == 0)
{
printf("Cur: %i\n", A);
}
++k;
/// Add code for saving results to file here ///
fprintf(DataFile, "%i\n", A);
if(kbhit()) // check if a key has been hit and exit if so
{
break;
}
}
fclose(DataFile);
CloseHandle(hSerial);
return 0;
}
I just test it by connecting PC0 to ground then power, this is what the screen looks like as number scroll by |
December 03, 2013
by scootergarrett
|
And here is communication in the other direction. this microprocessor code takes data (uint8_t) from the com port and displays it as a character on the LCD.
// LCD_display.c
#define F_CPU 14745600
#include <math.h>
#include <stdio.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <inttypes.h>
#include "../libnerdkits/io_328p.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"
int main()
{
uint8_t c;
// Start up the LCD //
lcd_init();
FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
lcd_clear_and_home();
// start up the serial port //
uart_init();
FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
stdin = stdout = &uart_stream;
while(1)
{
if(uart_char_is_waiting())
{
c = uart_read();
fprintf_P(&lcd_stream, PSTR("%c"), c);
}
}
return 0;
}
so then this code runs on my PC after the Microprocessor is running. It takes the character hit from the keyboard and sends it to the com port.
#include <stdio.h>
#include <Windows.h>
int main(void)
{
HANDLE hSerial;
DCB dcbSerialParams = {0};
COMMTIMEOUTS timeouts = {0};
DWORD dwBytesRead = 1;
char c;
{ /// Serial Communication stuff including timming numbers ///
/// Make sure Com port is correct 'COM1' ///
hSerial = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if(hSerial==INVALID_HANDLE_VALUE)
{
if(GetLastError()==ERROR_FILE_NOT_FOUND)
printf("ERROR creating file\n");
printf("ERROR creating file Other\n");
return 1;
}
dcbSerialParams.DCBlength=sizeof(dcbSerialParams);
if (!GetCommState(hSerial, &dcbSerialParams))
printf("error getting state\n");
dcbSerialParams.BaudRate=CBR_115200;
dcbSerialParams.ByteSize=8;
dcbSerialParams.StopBits=ONESTOPBIT;
dcbSerialParams.Parity=NOPARITY;
if(!SetCommState(hSerial, &dcbSerialParams))
printf("error setting serial port state\n");
// I messed with this part alot back it the now that it works I just leave it ///
timeouts.ReadIntervalTimeout=MAXWORD;
timeouts.ReadTotalTimeoutConstant=100;
timeouts.ReadTotalTimeoutMultiplier=0;
timeouts.WriteTotalTimeoutConstant=500;
timeouts.WriteTotalTimeoutMultiplier=100;
if(!SetCommTimeouts(hSerial, &timeouts))
printf("error occureed setting timeouts\n");
}
while(1)
{
if(kbhit()) // check if a key has been hit
{
c = getch(); // get that character
if(c == 13) // break out if its enter
break;
printf("%c", c); // Print it to the screen
// Send it accross Com port //
WriteFile(hSerial, &c, 1, &dwBytesRead, NULL);
}
}
return 0;
}
Overall this code displays the what you type on the LCD screen. note that backspace needs work and the program ends when you hit enter.
Example screen and LCD |
December 04, 2013
by jmuthe
|
Thanks a lot. The programs worked. |
December 13, 2013
by scootergarrett
|
I just started getting into GUI so now I have a computer program that opens a new window with a slider and this controls a PWM pin and the brightness of a LED.
This works on Code blocks on windows XP, I had to do include some libraries in the compiler and debugger setting stuff I don’t understand.
Here is the computer code:
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <Windows.h>
#include <windows.h>
#include <time.h>
#include <sys/timeb.h>
#include <conio.h>
#include <complex.h>
#include <stdbool.h>
#include <commctrl.h>
#define MY_SLIDER (WS_CHILD | WS_VISIBLE | TBS_AUTOTICKS | TBS_ENABLESELRANGE)
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
HWND hwnd;
HWND hwndTrack;
MSG msg;
bool change;
short SliderPos;
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
WNDCLASSW wc = {0};
change = false;
HANDLE hSerial;
DCB dcbSerialParams = {0};
COMMTIMEOUTS timeouts = {0};
DWORD dwBytesRead = 2;
short c;
wc.lpszClassName = L"Trackbar";
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0,IDC_ARROW);
RegisterClassW(&wc);
hwnd = CreateWindowW(wc.lpszClassName, L"Window Name",
WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 350, 350, 0, 0, hInstance, 0);
{ /// Serial Communication stuff including timming numbers ///
/// Make sure Com port is correct 'COM1' ///
hSerial = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, 0,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if(hSerial==INVALID_HANDLE_VALUE)
{
if(GetLastError()==ERROR_FILE_NOT_FOUND)
printf("ERROR creating file\n");
printf("ERROR creating file Other\n");
return 1;
}
dcbSerialParams.DCBlength=sizeof(dcbSerialParams);
if (!GetCommState(hSerial, &dcbSerialParams))
printf("error getting state\n");
dcbSerialParams.BaudRate=CBR_115200;
dcbSerialParams.ByteSize=8;
dcbSerialParams.StopBits=ONESTOPBIT;
dcbSerialParams.Parity=NOPARITY;
if(!SetCommState(hSerial, &dcbSerialParams))
printf("error setting serial port state\n");
// I messed with this part alot back it the now that it works I just leave it ///
timeouts.ReadIntervalTimeout=MAXWORD;
timeouts.ReadTotalTimeoutConstant=100;
timeouts.ReadTotalTimeoutMultiplier=0;
timeouts.WriteTotalTimeoutConstant=500;
timeouts.WriteTotalTimeoutMultiplier=100;
if(!SetCommTimeouts(hSerial, &timeouts))
printf("error occureed setting timeouts\n");
}
while( GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if(change)
{
change = false;
DUMP(SliderPos);
c = SliderPos * 10;
WriteFile(hSerial, &c, 2, &dwBytesRead, NULL);
}
}
CloseHandle(hSerial);
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
int k;
DWORD dwPos; // current position of slider
switch(msg)
{
case WM_CREATE:
InitCommonControls(); // loads common control's DLL
hwndTrack = CreateWindowEx( 0, TRACKBAR_CLASS, "Trackbar Control",
MY_SLIDER, 10, 10, 200, 30, hwnd, (HMENU) 1, NULL, NULL);
SetFocus(hwnd);
break;
case WM_HSCROLL:
change = true;
SliderPos = (int) SendMessage(hwndTrack, TBM_GETPOS, 0, 0);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
And the MCU code, it could use work but is fine for now:
// for NerdKits with ATmega328
#define F_CPU 14745600
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>
#include <avr/sleep.h>
#include "C:\Libraries_Mine\libnerdkits\io_328p.h"
#include "C:\Libraries_Mine\libnerdkits\uart.h"
#include "C:\Libraries_Mine\libnerdkits\delay.h"
#include "C:\Libraries_Mine\libnerdkits\lcd.h"
void PWM_init()
{
// set PB2,PB3 as output
DDRB |= (1<<PB2) | (1<<PB3);
OCR1A = 1024; // sets PWM to repeat pulse every ?
TCCR1A = (1<<COM1B1) | (1<<WGM11) | (1<<WGM10);
TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS11);
return;
}
uint16_t uart_read16(void)
{
uint16_t x;
while(!uart_char_is_waiting());
x = UDR0;
while(!uart_char_is_waiting());
*((char*)&x + 1) = UDR0;
return x;
}
void PWM(uint16_t N)
{
OCR1B = N;
return;
}
int main()
{
uint16_t x = 100;
// start up the serial port
uart_init();
FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
stdin = stdout = &uart_stream;
while ((UCSR0A & (1<<UDRE0))==0);
PWM_init();
while(1)
{
PWM(x);
x = uart_read16();
}
return 0;
}
I want to get some more GUI stuff with the microprocessor set up in the future, maybe a slider that controls the set thermostat temperature. Just thought I would share |