Win10 <-> USB <-> Arduino - Problem to communicate with Autohotkey - arduino
My desire is to communicate with an Arduino Uno over an USB-port (COM3 - 9600,N,8,1).
I was going to manage the information with Autohotkey on a computer running Windows 10.
This test is only intended to read the information from the Arduino. But later I also want to be able to send information from the PC to the Arduino Uno.
The Arduino Uno has an extra card attached (Funduino JoyStick Shield V1.A) - only for the test.
This is the program I use on the Arduino .:
// Armuino --- Funduino Joystick Shield ---
// Playlist: https://www.youtube.com/playlist?list=PLRFnGJH1nJiKIpz_ZyaU-uAZOkMH8GAcw
//
// Part 1. Introduction - Basic Functions: https://www.youtube.com/watch?v=lZPZuBCFMH4
// Arduino digital pins associated with buttons
const byte PIN_BUTTON_A = 2;
const byte PIN_BUTTON_B = 3;
const byte PIN_BUTTON_C = 4;
const byte PIN_BUTTON_D = 5;
const byte PIN_BUTTON_E = 6;
const byte PIN_BUTTON_F = 7;
// Arduino analog pins associated with joystick
const byte PIN_ANALOG_X = 0;
const byte PIN_ANALOG_Y = 1;
void setup() {
Serial.begin(9600);
pinMode(PIN_BUTTON_B, INPUT);
digitalWrite(PIN_BUTTON_B, HIGH);
pinMode(PIN_BUTTON_E, INPUT);
digitalWrite(PIN_BUTTON_E, HIGH);
pinMode(PIN_BUTTON_C, INPUT);
digitalWrite(PIN_BUTTON_C, HIGH);
pinMode(PIN_BUTTON_D, INPUT);
digitalWrite(PIN_BUTTON_D, HIGH);
pinMode(PIN_BUTTON_A, INPUT);
digitalWrite(PIN_BUTTON_A, HIGH);
}
void loop() {
Serial.print("Buttons A:");
Serial.print(digitalRead(PIN_BUTTON_A));
Serial.print(" ");
Serial.print("B:");
Serial.print(digitalRead(PIN_BUTTON_B));
Serial.print(" ");
Serial.print("C:");
Serial.print(digitalRead(PIN_BUTTON_C));
Serial.print(" ");
Serial.print("D:");
Serial.print(digitalRead(PIN_BUTTON_D));
Serial.print(" ");
Serial.print("E:");
Serial.print(digitalRead(PIN_BUTTON_E));
Serial.print(" ");
Serial.print("F:");
Serial.print(digitalRead(PIN_BUTTON_F));
Serial.print(" -- ");
Serial.print("Position X:");
Serial.print(analogRead(PIN_ANALOG_X));
Serial.print(" ");
Serial.print("Y:");
Serial.print(analogRead(PIN_ANALOG_Y));
Serial.print(" ");
Serial.println();
delay(1000);
}
This program seems to work when I run the Arduino Serial Editor on the PC.
The values is coming row for row on the screen, like this .:
**00:58:44.434 -> Buttons A:1 B:1 C:1 D:1 E:1 F:1 -- Position X:334 Y:321**
(I can't see any wrong values in the Serial Editor. - maybe in higher speed)
But if I try to "do the same" with Autohotkey, characters may be missing - some times.
Like this .:
But:322
Buttons A:1 B:1 C:1 D:1 E:1 F:1 -- Position X:334 Y:322
334 Y:322
Buttons A:1 B:1 C:1 D:1 E:1 F:1 -- Position X:334 Y:322
I have no idea how the communication with USB should look like in Windows 10.
(I think it is not the same as eg. Windows XP)
I have looked on this solution .: Arduino + AutoHotKey > Serial Connection
- Not sure I found the right Arduino.ahk
- Maybe require Windows XP?
- It doesn't work for me!
This tip is better (but old) .: Arduino.ahk beta .01
The most of tests have been based on this link and I created the following AHK-program .:
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn ; Enable warnings to assist with detecting common errors.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
#Singleinstance force
COM = 3
Num_Bytes = 500
Mode =
; Initialize COM-port
COM_Port = COM%COM%
COM_Baud = 9600
COM_Parity = N
COM_Data = 8
COM_Stop = 1
COM_DTR = Off
FilNamn := A_ScriptDir "/Test.txt"
IfExist %FilNamn%
FileDelete %FilNamn%
COM_Settings = %COM_Port%:baud=%COM_Baud% parity=%COM_Parity% data=%COM_Data% stop=%COM_Stop% dtr=%COM_DTR%
COM_FileHandle := Serial_Initialize(COM_Settings)
Loop 100
{ ; ReadResult := Serial_Read(COM_FileHandle, Num_Bytes, Mode)
ReadResult := Serial_Read_Raw(COM_FileHandle, Num_Bytes, Mode)
asciiString := Hex2ASCII(ReadResult)
sleep 20
FileAppend %asciiString%, %FilNamn%, UTF-8
; SplashTextOn 800, 100, Arduino Read, %asciiString%
; Sleep 1000
; MsgBox ,, Rad %A_LineNumber% -> %A_ScriptName%, % COM_Settings "`n`n" ReadResult "`n`n- " StrLen(ReadResult) "`n`n- " Bytes_Received "`n`n- " asciiString, 1
}
Serial_Close(COM_FileHandle)
; MsgBox ,, Rad %A_LineNumber% -> %A_ScriptName%, % COM_Settings "`n`n" ReadResult "`n`n- " StrLen(ReadResult) "`n`n- " Bytes_Received
; asciiString := Hex2ASCII(ReadResult)
; MsgBox ,, Rad %A_LineNumber% -> %A_ScriptName%, % ReadResult "`n`n"asciiString
MsgBox ,, Rad %A_LineNumber% -> %A_ScriptName%, Klart!
ExitApp
ESC::
SplashTextOff
Serial_Close(COM_FileHandle)
MsgBox ,,, Programmet avslutas!, 1
ExitApp
Return
Hex2ASCII(fHexString)
{ Loop Parse, fHexString
NewHexString .= A_LoopField (Mod(A_Index,2) ? "" : ",")
Loop Parse, NewHexString, `,
ConvString .= Chr("0x" A_LoopField)
Return ConvString
} ;http://www.autohotkey.com/forum/post-211769.html#211769
;########################################################################
;###### Initialize COM Subroutine #######################################
;########################################################################
Serial_Initialize(SERIAL_Settings){
;Global SERIAL_FileHandle ;uncomment this if there is a problem
;###### Build COM DCB ######
;Creates the structure that contains the COM Port number, baud rate,...
VarSetCapacity(DCB, 28)
BCD_Result := DllCall("BuildCommDCB"
,"str" , SERIAL_Settings ;lpDef
,"UInt", &DCB) ;lpDCB
If (BCD_Result <> 1){
error := DllCall("GetLastError")
MsgBox, There is a problem with Serial Port communication. `nFailed Dll BuildCommDCB, BCD_Result=%BCD_Result% `nLasterror=%error%`nThe Script Will Now Exit.
ExitApp
}
;###### Extract/Format the COM Port Number ######
StringSplit, SERIAL_Port_Temp, SERIAL_Settings, `:
SERIAL_Port_Temp1_Len := StrLen(SERIAL_Port_Temp1) ;For COM Ports > 9 \\.\ needs to prepended to the COM Port name.
If (SERIAL_Port_Temp1_Len > 4) ;So the valid names are
SERIAL_Port = \\.\%SERIAL_Port_Temp1% ; ... COM8 COM9 \\.\COM10 \\.\COM11 \\.\COM12 and so on...
Else ;
SERIAL_Port = %SERIAL_Port_Temp1%
;MsgBox, SERIAL_Port=%SERIAL_Port%
;###### Create COM File ######
;Creates the COM Port File Handle
;StringLeft, SERIAL_Port, SERIAL_Settings, 4 ; 7/23/08 This line is replaced by the "Extract/Format the COM Port Number" section above.
SERIAL_FileHandle := DllCall("CreateFile"
,"Str" , SERIAL_Port ;File Name
,"UInt", 0xC0000000 ;Desired Access
,"UInt", 3 ;Safe Mode
,"UInt", 0 ;Security Attributes
,"UInt", 3 ;Creation Disposition
,"UInt", 0 ;Flags And Attributes
,"UInt", 0 ;Template File
,"Cdecl Int")
If (SERIAL_FileHandle < 1){
error := DllCall("GetLastError")
MsgBox, There is a problem with Serial Port communication. `nFailed Dll CreateFile, SERIAL_FileHandle=%SERIAL_FileHandle% `nLasterror=%error%`nThe Script Will Now Exit.
ExitApp
}
;###### Set COM State ######
;Sets the COM Port number, baud rate,...
SCS_Result := DllCall("SetCommState"
,"UInt", SERIAL_FileHandle ;File Handle
,"UInt", &DCB) ;Pointer to DCB structure
If (SCS_Result <> 1){
error := DllCall("GetLastError")
MsgBox, There is a problem with Serial Port communication. `nFailed Dll SetCommState, SCS_Result=%SCS_Result% `nLasterror=%error%`nThe Script Will Now Exit.
Serial_Close(SERIAL_FileHandle)
ExitApp
}
;###### Create the SetCommTimeouts Structure ######
ReadIntervalTimeout = 0xffffffff
ReadTotalTimeoutMultiplier = 0x00000000
ReadTotalTimeoutConstant = 0x00000000
WriteTotalTimeoutMultiplier= 0x00000000
WriteTotalTimeoutConstant = 0x00000000
VarSetCapacity(Data, 20, 0) ; 5 * sizeof(DWORD)
NumPut(ReadIntervalTimeout, Data, 0, "UInt")
NumPut(ReadTotalTimeoutMultiplier, Data, 4, "UInt")
NumPut(ReadTotalTimeoutConstant, Data, 8, "UInt")
NumPut(WriteTotalTimeoutMultiplier, Data, 12, "UInt")
NumPut(WriteTotalTimeoutConstant, Data, 16, "UInt")
;###### Set the COM Timeouts ######
SCT_result := DllCall("SetCommTimeouts"
,"UInt", SERIAL_FileHandle ;File Handle
,"UInt", &Data) ;Pointer to the data structure
If (SCT_result <> 1){
error := DllCall("GetLastError")
MsgBox, There is a problem with Serial Port communication. `nFailed Dll SetCommState, SCT_result=%SCT_result% `nLasterror=%error%`nThe Script Will Now Exit.
Serial_Close(SERIAL_FileHandle)
ExitApp
}
Return SERIAL_FileHandle
}
;########################################################################
;###### Close COM Subroutine ############################################
;########################################################################
Serial_Close(SERIAL_FileHandle){
;###### Close the COM File ######
CH_result := DllCall("CloseHandle", "UInt", SERIAL_FileHandle)
If (CH_result <> 1)
MsgBox, Failed Dll CloseHandle CH_result=%CH_result%
Return
}
;########################################################################
;###### Write to COM Subroutines ########################################
;########################################################################
Serial_Write(SERIAL_FileHandle, Message){
;Global SERIAL_FileHandle
OldIntegerFormat := A_FormatInteger
SetFormat, Integer, DEC
;Parse the Message. Byte0 is the number of bytes in the array.
StringSplit, Byte, Message, `,
Data_Length := Byte0
;msgbox, Data_Length=%Data_Length% b1=%Byte1% b2=%Byte2% b3=%Byte3% b4=%Byte4%
;Set the Data buffer size, prefill with 0xFF.
VarSetCapacity(Data, Byte0, 0xFF)
;Write the Message into the Data buffer
i=1
Loop %Byte0% {
NumPut(Byte%i%, Data, (i-1) , "UChar")
;msgbox, %i%
i++
}
;msgbox, Data string=%Data%
;###### Write the data to the COM Port ######
WF_Result := DllCall("WriteFile"
,"UInt" , SERIAL_FileHandle ;File Handle
,"UInt" , &Data ;Pointer to string to send
,"UInt" , Data_Length ;Data Length
,"UInt*", Bytes_Sent ;Returns pointer to num bytes sent
,"Int" , "NULL")
If (WF_Result <> 1 or Bytes_Sent <> Data_Length)
MsgBox, Failed Dll WriteFile to COM Port, result=%WF_Result% `nData Length=%Data_Length% `nBytes_Sent=%Bytes_Sent%
SetFormat, Integer, %OldIntegerFormat%
Return Bytes_Sent
}
;########################################################################
;###### Read from COM Subroutines #######################################
;########################################################################
;########################################################################
;###### Read from COM Subroutines #######################################
;########################################################################
Serial_Read(COM_FileHandle, Num_Bytes, mode = "",byref Bytes_Received = "")
{
;Global COM_FileHandle
;Global COM_Port
;Global Bytes_Received
SetFormat, Integer, HEX
;Set the Data buffer size, prefill with 0x55 = ASCII character "U"
;VarSetCapacity won't assign anything less than 3 bytes. Meaning: If you
; tell it you want 1 or 2 byte size variable it will give you 3.
Data_Length := VarSetCapacity(Data, Num_Bytes, 0x55)
;msgbox, Data_Length=%Data_Length%
;###### Read the data from the COM Port ######
;msgbox, COM_FileHandle=%COM_FileHandle% `nNum_Bytes=%Num_Bytes%
Read_Result := DllCall("ReadFile"
,"UInt" , COM_FileHandle ; hFile
,"Str" , Data ; lpBuffer
,"Int" , Num_Bytes ; nNumberOfBytesToRead
,"UInt*", Bytes_Received ; lpNumberOfBytesReceived
,"Int" , 0) ; lpOverlapped
;MsgBox, Read_Result=%Read_Result% `nBR=%Bytes_Received% ,`nData=%Data%
If (Read_Result <> 1)
{
MsgBox, There is a problem with Serial Port communication. `nFailed Dll ReadFile on COM Port, result=%Read_Result% - The Script Will Now Exit.
Serial_Close(COM_FileHandle)
Exit
}
;if you know the data coming back will not contain any binary zeros (0x00), you can request the 'raw' response
If (mode = "raw")
Return Data
;###### Format the received data ######
;This loop is necessary because AHK doesn't handle NULL (0x00) characters very nicely.
;Quote from AHK documentation under DllCall:
; "Any binary zero stored in a variable by a function will hide all data to the right
; of the zero; that is, such data cannot be accessed or changed by most commands and
; functions. However, such data can be manipulated by the address and dereference operators
; (& and *), as well as DllCall itself."
i = 0
Data_HEX =
Loop %Bytes_Received%
{
;First byte into the Rx FIFO ends up at position 0
Data_HEX_Temp := NumGet(Data, i, "UChar") ;Convert to HEX byte-by-byte
StringTrimLeft, Data_HEX_Temp, Data_HEX_Temp, 2 ;Remove the 0x (added by the above line) from the front
;If there is only 1 character then add the leading "0'
Length := StrLen(Data_HEX_Temp)
If (Length =1)
Data_HEX_Temp = 0%Data_HEX_Temp%
i++
;Put it all together
Data_HEX .= Data_HEX_Temp
}
;MsgBox, Read_Result=%Read_Result% `nBR=%Bytes_Received% ,`nData_HEX=%Data_HEX%
SetFormat, Integer, DEC
Data := Data_HEX
Return Data
}
;########################################################################
;###### Read from COM Subroutines #######################################
;########################################################################
Serial_Read_Raw(SERIAL_FileHandle, Num_Bytes, mode = "",byref Bytes_Received = ""){
;Global SERIAL_FileHandle
;Global SERIAL_Port
;Global Bytes_Received
OldIntegerFormat := A_FormatInteger
SetFormat, Integer, HEX
;Set the Data buffer size, prefill with 0x55 = ASCII character "U"
;VarSetCapacity won't assign anything less than 3 bytes. Meaning: If you
; tell it you want 1 or 2 byte size variable it will give you 3.
Data_Length := VarSetCapacity(Data, Num_Bytes, 0)
;msgbox, Data_Length=%Data_Length%
;###### Read the data from the COM Port ######
;msgbox, SERIAL_FileHandle=%SERIAL_FileHandle% `nNum_Bytes=%Num_Bytes%
Read_Result := DllCall("ReadFile"
,"UInt" , SERIAL_FileHandle ; hFile
,"Str" , Data ; lpBuffer
,"Int" , Num_Bytes ; nNumberOfBytesToRead
,"UInt*", Bytes_Received ; lpNumberOfBytesReceived
,"Int" , 0) ; lpOverlapped
;MsgBox, Read_Result=%Read_Result% `nBR=%Bytes_Received% ,`nData=%Data%
If (Read_Result <> 1){
MsgBox, There is a problem with Serial Port communication. `nFailed Dll ReadFile on COM Port, result=%Read_Result% - The Script Will Now Exit.
Serial_Close(SERIAL_FileHandle)
Exit
}
;if you know the data coming back will not contain any binary zeros (0x00), you can request the 'raw' response
If (mode = "raw")
Return Data
;###### Format the received data ######
;This loop is necessary because AHK doesn't handle NULL (0x00) characters very nicely.
;Quote from AHK documentation under DllCall:
; "Any binary zero stored in a variable by a function will hide all data to the right
; of the zero; that is, such data cannot be accessed or changed by most commands and
; functions. However, such data can be manipulated by the address and dereference operators
; (& and *), as well as DllCall itself."
i = 0
Data_HEX =
Loop %Bytes_Received%
{
;First byte into the Rx FIFO ends up at position 0
Data_HEX_Temp := NumGet(Data, i, "UChar") ;Convert to HEX byte-by-byte
StringTrimLeft, Data_HEX_Temp, Data_HEX_Temp, 2 ;Remove the 0x (added by the above line) from the front
;If there is only 1 character then add the leading "0'
Length := StrLen(Data_HEX_Temp)
If (Length =1)
Data_HEX_Temp = 0%Data_HEX_Temp%
i++
;Put it all together
Data_HEX .= Data_HEX_Temp
}
;MsgBox, Read_Result=%Read_Result% `nBR=%Bytes_Received% ,`nData_HEX=%Data_HEX%
SetFormat, Integer, DEC
Data := Data_HEX
SetFormat, Integer, %OldIntegerFormat%
Return Data
}
Thanks for your time!
Has managed to get better communication between Windows and the Arduino. (with a different configuration).Made two windows in a GUI
One where I can enter characters.
Arduinon echoes back what is typed and returns that character to the PC
The other show the returned characters..
It works (in 9600bps), but first I send a headline and if the baud rate increases, "strange" characters always appear at the beginning of the test.
Don't know if the serial buffer in the Arduino / PC needs to be cleared before the characters start to read in sharp mode? (how to do that?)
In the Arduino SerialMonitor I got not readable information with higher baudrate (from another test program)
My ECHO-program in the Arduino
int incomingByte;
void setup() {
Serial.begin(9600);
}
void loop(){
if (Serial.available() > 0) {
incomingByte = Serial.read();
//Serial.print(incomingByte);
Serial.write(incomingByte);
}
}
Related
Reading multiple bytes in Arduino sent using Pyserial
I'm constantly sending structs of int64 via Pyserial with: with serial.Serial(port='COM4', baudrate=115200, timeout=.1) as arduino: value = write_read(struct.pack(">q", int_array[1][i])) #this sends signed int.64 in bytes print(value) the struct.pack has this shape, for example: b'\xff\xff\xff\xff\xff\xff\xff\xef' and the function write_read consists of: def write_read(x): arduino.write((x)) data = arduino.readline() #the idea is to receive an ACK from the Arduino after 8 bytes (the full #number) return data The code I'm trying to develop in arduino is the following: void loop() { // send data only when you receive data: if (Serial.available() \> 0) { // read the incoming byte: incomingByte = Serial.read(); //read 8 bytes and create the result r= function_to_read_8_last_bytes // or similar // say what you got: Serial.print("I received: "); Serial.printlesultn(r, DEC); Serial.write("ACK"); } } I'm very curious how I could do a robust "read 8 bytes" function. Should I add some especial character in the Python part to indentify when it ends one value? Thanks! I'll appreciate any help :)
Given the discussion in the comments, it's hard to receive a stream of bytes and be sure that the receiver is completely synchronized. However let's make some assumptions to ease the problem: The serial buffer is empty when you connect your laptop to Arduino. This ensures you won't receive spurious data with no meaning. I had this problem happens a lot when the serial connection was ended abruptly by any cause. You are not constantly sending bytes, Arduino has time to process them until the start of the new sequence. You only send this data, so there is no need to create a higher level protocol on top of it. Bare in mind that the serial communication is almost just an hardware stack, you receive bytes with no headers. For assumption 1 you can write a simple piece of code to consume all the spurious bytes in the serial buffer as soon as your main starts from Arudino, so this will be done everytime you connect the serial (as this is also where the power supply comes from). Something like this: void serialFlush(){ while(Serial.available() > 0) { char t = Serial.read(); } } You can send a "READY" signal back to the Python interface, so that the program knows you are ready to receive data. Going on with the solution you can implement an easy CRC in python, an additional byte which contains a XOR of all the previous bytes, and you check that in Arduino upon reception complete. def xor_reduce_long_int(li): res = 0; for i in range(8): mask = (0xFF)<<(i*8) print(hex(mask)) masked = (li&mask)>>(i*8) res ^= masked return res with serial.Serial(port='COM4', baudrate=115200, timeout=.1) as arduino: crc=xor_reduce_long_int(int_array[1][i]) value = write_read(struct.pack(">qc", int_array[1][i],crc)) #this sends signed int.64 in bytes print(value) And with Arduino I would read 8 bytes when they are available and put them into an unsigned char buffer. I would then define a union that alias such buffer to interpret it as long long int. typedef struct long_int_CRC { union { unsigned char bytes[8]; long int data; }; unsigned char CRC; }data_T; // .. Later in main data_T = received_data; int received_bytes=0 unsigned char my_CRC = 0; unsigned char rec_byte= 0; while( received_bytes < 8 ) { if(Serial.available() ) { // Get the byte rec_byte = Serial.read() // Store the byte and calc CRC received_data.bytes[received_bytes] = rec_byte; my_CRC ^= rec_byte; // Increment counter for next byte received_bytes++; } } // Reception complete, check CRC unsigned char rec_CRC; if(Serial.available() ) { rec_CRC = Serial.read() } if( my_CRC != rec_CRC ) { // Something was wrong! } // Now access your data as a long int Serial.print("I received: "); Serial.printlesultn(received_data.data, DEC); Serial.write("ACK");
Why i can not receive string from USART using this code?
After using this code i am not getting anything on serial monitor while it should give me the string which i am sending .am i right that strings which i am sending from serial monitor are null terminated in arduino (and that's why i am waiting for null character in order to get the string) ? Please help void setup() { UBRR0 = 103; // for configuring baud rate of 9600bps UCSR0C |= (1 << UCSZ01) | (1 << UCSZ00); // Use 8-bit character sizes UCSR0B |= (1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0); //// Turn on the transmission, reception, and Receive interrupt sei();// enable global interrupt } ISR(USART_RX_vect) { while (1) { while (!(UCSR0A & (1 << RXC0))); if (UDR0 != '\0') { buf[i] = UDR0; i++; } else { break; } } temp=String(buf); uart_send_string(temp); //this function is working properly }
No, you won't receive a null '\0' character usually. Unless the sender explicitly sends one. But this is unusual for non-binary data transmission via Serial. The SerialMonitor definitely doesn't! If you need to notice the end of a message, a newline ( LF , '\n' or 0x0A ) is very common. But any other character might do as well. BTW: As you tag the question Arduino, why don't you use the Arduino library for Serial UART communication?
generate arbitrary PWM signal using ATMEGA128
I am well familiar with PWM generation in Atmega128 and its family microcontrollers. I have been using prescalar and other registers for generating frequency. But I have to generate 20KHz pwm signal. I tried but I could not get the desired output. Can anyone suggest me or help me how to do it ? As far as I know, in atmega128, 1 instruction takes 1 cycle. Using 16MHz crystal, 1 instruction completes in 1/16M sec. I tried to generate 20Khz signal (50 us)with 25us duty cycle. But I get different frequency (277.78 Hz) in oscilloscope which is far less than 20KHz My calculation was 16MH = 20000Hz * 800. for 0-399 count, I made port high and 399-799 count, I made port low. void frequency(void){ // 20kHz Frequency if (cnt1 <= 399){ PORTB |= (1<<7); } else { PORTB &= ~(1<<7); } cnt1++; if (cnt1 >= 800) cnt1 = 0; }
I don't have access to the 128 but verified its 16-bit Timer 1 is similar to that in the 328 and 32U4 so the following should work with minor modification (the main sticking point is probably looking up what pin the overflow register is bound to): #include <avr/io.h> #include <util/delay.h> struct CTC1 { static void setup() { // CTC mode with TOP-OCR1A TCCR1A = 0; TCCR1B = _BV(WGM12); // toggle channel A on compare match TCCR1A = (TCCR1A & ~(_BV(COM1A1) | _BV(COM1A0))) | _BV(COM1A0); // set channel A bound pin PB1 to output mode #if defined(__AVR_ATmega32U4__) DDRB |= _BV(5); #else DDRB |= _BV(1); #endif } static void set_freq(float f) { static const float f1 = min_freq(1), f8 = min_freq(8), f64 = min_freq(64), f256 = min_freq(256); uint16_t n; if (f >= f1) n = 1; else if (f >= f8) n = 8; else if (f >= f64) n = 64; else if (f >= f256) n = 256; else n = 1024; prescale(n); OCR1A = static_cast<uint16_t>(round(F_CPU / (2 * n * f) - 1)); } static void prescale(uint16_t n) { uint8_t bits = 0; switch (n) { case 1: bits = _BV(CS10); break; case 8: bits = _BV(CS11); break; case 64: bits = _BV(CS11) | _BV(CS10); break; case 256: bits = _BV(CS12); break; case 1024: bits = _BV(CS12) | _BV(CS10); break; default: bits = 0; } TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11) | _BV(CS10))) | bits; } static inline float min_freq(uint16_t n) { return ceil(F_CPU / (2 * n * 65536)); } }; void setup() { CTC1::setup(); CTC1::set_freq(20e3); } void loop() { // do whatever _delay_ms(1); } int main() { setup(); for (;;) loop(); } I tested on my scope and measure exactly 20kHz off a 328p running at 16MHz. If 20kHz is the only frequency you need then you can simplify this substantially. Translating the code to use one of the 8-bit timers is also straightforward though I haven't verified that it's possible to hit exactly 20kHz with those.
It's not a good idea to use counter in C to implement the PWM or anything time critical really. Although C converts your code to specific machine code, you don't really know how much time it will take. Your code does not translate to: make port B high 400 times (PORTB |= (1<<7);) make port B low 400 times (PORTB &= ~(1<<7);) , but rather something like this (simplification, human-readable): load variable cnt1 to memA; load 399 to memB compare mem A to memB put result to memC if memC eq "somthing indicating <=" do PORTB |= (1<<7); if memC something else do PORTB &= ~(1<<7); load cnt1 to memD and increment; write memD to cnt1; load 800 to memE load cnt1 to memF compare memF to memE put result to memG if memG eq "somthing indicating <=" do memF = 0, write memF to cnt1; if memG something else go to start; If you look at this from "C" point of view you need to do at least: 1. comare cnt1-399 2. if ok - do / else 3. port high / port low 4. add one to cnt1 5. compare cnt1 and 800 It then depends on you compiler how good it is at optimizing all the loads and writes (usually quite good). You can have control on what the delays will be if you really know your compiler and don't use to much optimization (it is usually to complex to follow) or by writing the code in assembler. But then you will have to use logic similar to my explanation of the machine code (assembler is close to human-readable machine code). I think the solution for you are timer interrupts. There's a good tutorial for atmega128 this here. Also what do you mean with: I tried to generate 20Khz signal (50 us)with 25us duty cycle. Do you mean 20kHz signal with 50% duty cycle? so 25us low, 25 us high? If this is the case you can do this with one timer interrupt and one (binary) counter. Exactly the "8 bit timer example" you can read about in the provided link.
Arduino stepper control by program
I try to control a stepper motor with a program that uses a protocol (see below) I am able to control the stepper with the Accelstepper (see below) but have no idea how i can program the Arduino so it is able to communicate according te protocol through the serial port. #include <AccelStepper.h> // Define a stepper and the pins it will use AccelStepper stepper(1, 3, 4); int pos = 8192; void setup() { stepper.setMaxSpeed(5000); stepper.setAcceleration(1500); } void loop() { if (stepper.distanceToGo() == 0) { delay(500); pos = -pos; stepper.moveTo(pos); } stepper.run(); } All commands sent to the rotary table are in simple character format including the motor numbers. Only the parts marked as xxx passed to the table as byte data. For example if you want table 1 rotate 4 steps instead of passing "I1M004" you pass "I1M" + (char)0 + (char)0 + (char)4 In general all commands get a reply in the form of: ^XXXXXX Commands V Request the status of the rotary table. Usual reply would be ^R1R2R3R4 indicating rotary 1 ready, rotary 2 ready, etc. ^B1xxxR2R3R4 means rotary 1 is busy where xxx are 3 bytes indicates how many steps the rotary still has to perform. SmMxxx Sets the speed of the motor m to xxx, where xxx is a 3 bytes of data indicating the speed. Example code: port.Write("S1M" + (char)0 + (char)6 + (char)255); // set motor 1 to speed 1791. The standard speed range of our rotary table is: 0x000001 to 0x0012FF (1 to 4863). Controller will respond with ^mxx mirroring the motor number and 2 last bytes of speed setting. ImMxxx Turns motor m xxx number of steps. Controller will acknowledge with ^Bmxxx DmCWLO Set motor number m to rotate clockwise (So each consecutive command to rotate the motor m will rotate it clockwise). DmCWHi Sets rotary m to rotate counterclockwise. EmHALT Rotary m stop. Rotary Sample Command Sequence Motor numbers are passed as characters but the number of steps and speed are passed as 3 bytes of binary for simplicity. send: V reply: ^R1R2R3R4 send: S1M1791 reply: ^191 send: D1CWLO reply: ^ send: I1M100 reply: ^B1100
I had a similar project for my dissertation work where I controlled an inverted pendulum from a PC via an arduino uno. I'm assuming you have a PC program what sends out the commands to the arduino, and the problem is to receive and interpret it on the Arduino board. I wrote the code below with major help (some copy paste modify) from here It basically opens the com port and then listens to the incoming commands from the PC. When a command is received, it breaks it up (the incoming commands come in a #00parameter format). All commands start with #. The following 2 digits define the command itself, and the following text/numbers are the parameters for the command. Once the command and its parameters are known, the actual process related to the command can be executed. In your case this supposed to be the motor control related to the incoming commands. The below code obviously needs to be updated to match with your motor control functions, but the incoming command handling works just fine. String inputString = ""; // a string to hold incoming data boolean stringComplete = false; // whether the incloming string is complete float kp = 10; //sample parameter 1 float kd = 5; //sample parameter 2 float ki = 2; //sample parameter 3 void setup() { Serial.begin(9600); //Start serial communication inputString.reserve(200); //Reserves 200 bytes for the string } void loop() { //This becomes true when the serial port receives a "\n" character (end of line) if (stringComplete) { SerialProc(); //the function which runs when a full line is received inputString = ""; //once processed, the string is cleared stringComplete = false; //set flag to false to indicate there is nothing in the buffer waiting } } void serialEvent() //This serial event runs between each loop cycles { while (Serial.available()) //if there is anything in the incoming buffer this while loop runs { // get the next new byte: char inChar = (char)Serial.read(); // add it to the inputString: inputString += inChar; // if the incoming character is a newline, set a flag // so the main loop can do something about it: if (inChar == '\n') { stringComplete = true; //This indicates the line is complete, and the main program can process it } } } void SerialProc() //the function which processes the incoming commands. It needs to be modified to your needs { //cmd is the first three characters of the incoming string / line String cmd = inputString.substring(0,3); //first three characters in incoming string specifies the command //param is the rest of the string to the end of the line (excluding the first three characters) String param = inputString.substring(3, inputString.length()); //rest of incoming string is making up the parameter //creating a buffer as an array of characters, same size as the length of the parameters string char buf[param.length()]; //moving the parameters from string to the char array param.toCharArray(buf,param.length()); //the above string to char array conversion is required for the string to float //conversion below (atof) //the below part is the command execution. Could have used a switch below, but the series of ifs //just did the trick if (cmd == "#00") SendReply(); //Executing command 1 else if (cmd == "#01") kp = atof(buf); //executing command 2 (setting parameter kp) else if (cmd == "#02") kd = atof(buf); //executing command 3 (setting parameter kd) else if (cmd == "#03") ki = atof(buf); //executing command 4 (setting parameter ki) } void SendReply() { //This is called from the SerialProc function when the #00 command is received //After the last parameter (TimeDelay) it sends the carrige return characters via the Serial.println Serial.println("reply"); }
MSP430 not able to handle double
I am trying to program a MSP430 with a simple "FIR filter" program, that looks like the following: #include "msp430x22x4.h" #include "legacymsp430.h" #define FILTER_LENGTH 4 #define TimerA_counter_value 12000 // 12000 counts/s -> 12000 counts ~ 1 Hz int i; double x[FILTER_LENGTH+1] = {0,0,0,0,0}; double y = 0; double b[FILTER_LENGTH+1] = {0.0338, 0.2401, 0.4521, 0.2401, 0.0338}; signed char floor_and_convert(double y); void setup(void) { WDTCTL = WDTPW + WDTHOLD; // Stop WDT BCSCTL1 = CALBC1_8MHZ; // Set DCO DCOCTL = CALDCO_8MHZ; /* Setup Port 3 */ P3SEL |= BIT4 + BIT5; // P3.4,5 = USART0 TXD/RXD P3DIR |= BIT4; // P3.4 output direction /* UART */ UCA0CTL1 = UCSSEL_2; // SMCLK UCA0BR0 = 0x41; // 9600 baud from 8Mhz UCA0BR1 = 0x3; UCA0MCTL = UCBRS_2; UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine** IE2 |= UCA0RXIE; // Enable USCI_A0 RX interrupt /* Setup TimerA */ BCSCTL3 |= LFXT1S_2; // LFXT1S_2: Mode 2 for LFXT1 = VLO // VLO provides a typical frequency of 12kHz TACCTL0 = CCIE; // TACCR0 Capture/compare interrupt enable TACCR0 = TimerA_counter_value; // Timer A Capture/Compare 0: -> 25 Hz TACTL = TASSEL_1; // TASSEL_1: Timer A clock source select: 1 - ACLK TACTL |= MC_1; // Start Timer_A in up mode __enable_interrupt(); } void main(void) // Beginning of program { setup(); // Call Function setup (see above) _BIS_SR(LPM3_bits); // Enter LPM0 } /* USCIA interrupt service routine */ /*#pragma vector=USCIAB0RX_VECTOR;*/ /*__interrupt void USCI0RX_ISR(void)*/ interrupt (USCIAB0RX_VECTOR) USCI0RX_ISR(void) { TACTL |= MC_1; // Start Timer_A in up mode x[0] = (double)((signed char)UCA0RXBUF); // Read received sample and perform type casts y = 0; for(i = 0;i <= FILTER_LENGTH;i++) // Run FIR filter for each received sample { y += b[i]*x[i]; } for(i = FILTER_LENGTH-1;i >= 0;i--) // Roll x array in order to hold old sample inputs { x[i+1] = x[i]; } while (!(IFG2&UCA0TXIFG)); // Wait until USART0 TX buffer is ready? UCA0TXBUF = (signed char) y; TACTL |= TACLR; // Clear TimerA (prevent interrupt during receive) } /* Timer A interrupt service routine */ /*#pragma vector=TIMERA0_VECTOR;*/ /*__interrupt void TimerA_ISR (void)*/ interrupt (TIMERA0_VECTOR) TimerA_ISR(void) { for(i = 0;i <= FILTER_LENGTH;i++) // Clear x array if no data has arrived after 1 sec { x[i] = 0; } TACTL &= ~MC_1; // Stops TimerA } The program interacts with a MatLab code, that sends 200 doubles to the MSP, for processing in the FIR filter. My problem is, that the MSP is not able to deal with the doubles. I am using the MSPGCC to compile the code. When I send a int to the MSP it will respond be sending a int back again.
Your problem looks like it is in the way that the data is being sent to the MSP. The communications from MATLAB is, according to your code, a sequence of 4 binary byte values that you then take from the serial port and cast it straight to a double. The value coming in will have a range -128 to +127. If your source data is any other data size then your program will be broken. If your data source is providing binary "double" data then each value may be 4 or 8 bytes long depending upon its internal data representation. Sending one of these values over the serial port will be interpreted by the MSP as a full set of 4 input samples, resulting in absolute garbage for a set of answers. The really big question is WHY ON EARTH ARE YOU DOING THIS IN FLOATING POINT - on a 16 bit integer processor that (many versions) have integer multiplier hardware.
As Ian said, You're taking an 8bit value (UCA0RXBUF is only 8 bits wide anyway) and expecting to get a 32bit or 64 bit value out of it. In order to get a proper sample you would need to read UCA0RXBUF multiple times and then concatenate each 8 bit value into 32/64 bits which you then would cast to a double. Like Ian I would also question the wisdom of doing floating point math in a Low power embedded microcontroller. This type of task is much better suited to a DSP. At least you should use fixed point math, seewikipedia (even in a DSP you would use fixed point arithmetic).
Hmm. Actually the code is made of my teacher, I'm just trying to make it work on my Mac, and not in AIR :-) MATLAB code is like this: function FilterTest(comport) Fs = 100; % Sampling Frequency Ts = 1/Fs; % Sampling Periode L = 200; % Number of samples N = 4; % Filter order Fcut = 5; % Cut-off frequency B = fir1(N,Fcut/(Fs/2)) % Filter coefficients in length N+1 vector B t = [0:L-1]*Ts; % time array A_m = 80; % Amplitude of main component F_m = 5; % Frequency of main component P_m = 80; % Phase of main component y_m = A_m*sin(2*pi*F_m*t - P_m*(pi/180)); A_s = 40; % Amplitude of secondary component F_s = 40; % Frequency of secondary component P_s = 20; % Phase of secondary component y_s = A_s*sin(2*pi*F_s*t - P_s*(pi/180)); y = round(y_m + y_s); % sum of main and secondary components (rounded to integers) y_filt = round(filter(B,1,y)); % filtered data (rounded to integers) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Serial_port_object = serial(comport); % create Serial port object set(Serial_port_object,'InputBufferSize',L) % set InputBufferSize to length of data set(Serial_port_object,'OutputBufferSize',L) % set OutputBufferSize to length of data fopen(Serial_port_object) % open Com Port fwrite(Serial_port_object,y,'int8'); % send out data data = fread(Serial_port_object,L,'int8'); % read back data fclose(Serial_port_object) % close Com Port %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% subplot(2,1,1) hold off plot(t,y) hold on plot(t,y_filt,'r') plot(t,y_filt,'ro') plot(t,data,'k.') ylabel('Amplitude') legend('y','y filt (PC)','y filt (PC)','y filt (muP)') subplot(2,1,2) hold off plot(t,data'-y_filt) hold on xlabel('time') ylabel('muP - PC') figure(1)
It is also not advised to keep interrupt routines doing long processing routines, because you will impact on interrupt latency. Bytes comming from the PC can get easily lost, because of buffer overrun on the serial port. The best is to build a FIFO buffer holding a resonable number of input values. The USCI routine fills the FIFO while the main program keeps looking for data inside it and process them as they are available. This way, while the data is being processed, the USCI can interrupt to handle new incomming bytes. When the FIFO is empty, you can put the main process in a suitable LPM mode to conserve power (and this is the best MSP430 feature). The USCI routine will wake the CPU up when a data is ready (just put the WAKEUP attribute in the USCI handler if you are using MSPGCC). In such a scenario be sure to declare volatile every variable that are shared between interrupt routines and the main process.