GSM module
Hardware
AT-commands
The GSM module can be operated with AT-commands (see Wikipedia
[1] ). A complete tutorial can be found
here.
The module can be configured, controlled and read out over the COM-port with the help of the AT-commands. A terminal program (e.g. Microsoft HyperTerminal, MTTTY) is used to send the commands. Due to the asynchronous communication via COM-port, both devices, PC and GSM module, have to be equally configured:
Baud rate: 9600, 8-bit data word, no parity, one stop bit (Short: 9600,8,n,1)
See below some command examples:
ATThe response "Ok" symbolizes a working connection between PC and module.
AT+CPIN?
Request, if a PIN is needed to access the device. Answer: +CPIN: <code> OK. <code> descript the state and can have the following meanings:
READY: PIN was already entered or is not needed
SIM PIN/PUK: PIN expected
AT+CPIN=”1234?
Inserts PIN 1234. If the response is "Ok" was the PIN correct and the device can be configured.
AT+CMGF=1
This important command sets the input and output of the GSM module to human readable text mode. Further operations are now easier. Response should be "Ok".
AT+CMGL="ALL"
Lists all SMS in the memory.
AT+CMGL="REC UNREAD"
Lists all unread SMS in the memory. Now all unread SMS are set to "REC READ".
AT+CMGD=<index>
Deletes the SMS with index <index> from the memory.
ATD<number>
Calls the number <number> and tries to establish a connection with the target (not used in this project).
Project SMS
Concept
The idea is to display an SMS on a LCD or LED ticker.
Realization
The SMS is send via mobile net to the GSM module, is read out with a FPGA board and is displayed finally on a LCD/LED ticker.
Video
Hardware
The FPGA board is connected via the RS232 interface to the GSM module. Both boards have an integrated voltage level converter, which is adapting to the corresponding level. That's why both modules can be easily connected to the PC or to each other.
The Nexys2 board communicates via AT-commands with the GSM module and is polling every second the current SMS in the memory. The GSM should send the SMS as human readable text to the board, which saves the SMS to a RAM.
Figure: GSM module, Nexys2 FPGA board, LCD, MAX232 bread board
The LCD is operated with 8-bit data words and is displaying the SMS from the RAM as a ticker. This LCD module needs a negative voltage for the contrast. I use a MAX232 IC to generate the negative voltage level. This IC is normally used as a voltage level converter, but fortunately it outputs the supply voltage VCC as a negative voltage -V.
Figure: Nexys2 FPGA board, LCD
The MAX232 is placed in the middle of the bread board. The negative supply voltage -V can be found at pin 6. This is a easy way to generate negative voltages without having two power supplies. In this case I use 5V from the Nexys2 board as power supply for the MAX232. The negative voltage of -5V is regulated with a poti (LCD contrast needs 0V down to -5V) and supplied to the contrast pin of the LCD. MAX232 is not able to deliver high current, but it's enough as reference voltage.
You can find the application note of the MAX232 datasheet below (ELKOs can be replaced with ceramic capacitors of 100nF).
Schematic I/Os # CLOCK
NET "CLK" CLOCK_DEDICATED_ROUTE = FALSE;
NET "CLK" LOC="B8";
# RS232
NET "RXD" LOC = "U6";
NET "TXD" LOC = "P9";
#NET "test_txd" LOC = "M18";
# Output
NET LED<0> LOC = J14;
NET LED<1> LOC = J15;
NET LED<2> LOC = K15;
NET LED<3> LOC = K14;
NET LED<4> LOC = E16;
NET LED<5> LOC = P16;
NET LED<6> LOC = E4;
NET LED<7> LOC = P4;
#RESET
NET "RESET" LOC = "G18";
#FLAG
NET "FLAG_R" LOC = "C17";
NET "FLAG_T" LOC = "J13";
NET "bit_clk" LOC = "G15";
#NET LED_OUT LOC = J14;
##FLAG
#NET "LED_OUT" LOC = "C17";
#LCD
NET DATA<0> LOC = L15;
NET DATA<1> LOC = K12;
NET DATA<2> LOC = L17;
NET DATA<3> LOC = M15;
NET DATA<4> LOC = K13;
NET DATA<5> LOC = L16;
NET DATA<6> LOC = M14;
NET DATA<7> LOC = M16;
NET ENA LOC = M13;
NET RS LOC = R18;
NET CLK_1000Hz LOC = R15;
Modules The following modules are realized in VHDL:
TX
The transmitter: Figure: Tx module The transmitter communicates via RS232 protocol. In detail, it is the driver to send 8-bit data words over the RS232 interface of the FPGA board. The Nexys2 board already has a voltage level converter. The corresponding pins of the module are directly connected to this IC, therefore it's not necessary to consider neither voltage levels nor inverting of the data bits (Serial interface: voltage levels from -12V up to 12V; '1' equals -12V and '0' equals 12V). The TX module is sending with 9600 baud and the data format 8,n,1 (8 data bits, no parity, 1 stop bit). The baud rate is generated by the module using the 50MHz board clock. The data word is read parallel and sent serially. Starting the transmission process sets the transmission flag (Flag_T). The flag is reset after the correct transmission of the stop bit. The baud rate of 9600Hz can be used externally via pin "bit_clk". An asynchronous reset is resetting all states to their default values. library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity transmitter is
Port ( CLK : in STD_LOGIC;
TXD : out STD_LOGIC;
bit_clk : out std_logic;
RESET : in STD_LOGIC;
DATA_IN : in STD_LOGIC_VECTOR (7 downto 0);
FLAG_T : inout STD_LOGIC);
end transmitter;
architecture Behavioral of transmitter is
constant baudDivide : std_logic_vector(9 downto 0) := "1010001011";
--baude rate divider for 9600 by 4*baude rate = sample rate
-- by dividing 50Mhz by 2 and 38400
signal clkDiv : std_logic_vector(9 downto 0) := "0000000000";
signal tClk : std_logic := '0'; -- sample clock (38400)
signal sendBuffer : std_logic_vector(7 downto 0);
begin
process (CLK) --Define rClk
begin
if CLK = '1' and CLK'Event then
if clkDiv = baudDivide then
tClk <= not tClk;
clkDiv <= "0000000000";
else
clkDiv <= clkDiv +1;
end if;
end if;
end process;
process(tClk,RESET)
variable count : integer;
begin
if(RESET='1') then -- asynchrous reset
FLAG_T <= '0';
count := 0;
sendBuffer <= "00000000";
elsif(tClk'event and tClk='1' ) then
if(FLAG_T='0')then
if not(DATA_IN = "00000000") then
FLAG_T <= '1' ;
end if;
count := 0;
elsif(FLAG_T='1') then
sendBuffer <= DATA_IN;
case count is
when 0 => TXD <= '0' ; bit_clk <= '1'; -- Start-Bit
when 4 => TXD <= sendBuffer(0);bit_clk <= '1'; -- 6. Takt LSB
when 8 => TXD <= sendBuffer(1);bit_clk <= '1'; -- 10. Takt
when 12 => TXD <= sendBuffer(2);bit_clk <= '1'; -- 14. Takt
when 16 => TXD <= sendBuffer(3);bit_clk <= '1'; -- 18. Takt
when 20 => TXD <= sendBuffer(4);bit_clk <= '1'; -- 22. Takt
when 24 => TXD <= sendBuffer(5);bit_clk <= '1'; -- 26. Takt
when 28 => TXD <= sendBuffer(6);bit_clk <= '1'; -- 30. Takt
when 32 => TXD <= sendBuffer(7);bit_clk <= '1'; -- 34. Takt MSB
when 36 => TXD <= '1' ;bit_clk <= '1'; --stop-Bit
when 60 => FLAG_T<='0' ;
when others => bit_clk <= '0'; -- 38. Takt Stop-Bit
end case ;
count := count + 1;
end if ;
end if ;
end process ;
end Behavioral;
RX
The receiver: Figure: Rx module
The receiver module operates with the same specifications as the transmitter. The RX signal of the RS232 interface is observed by the module. If the signal is put to ground (start bit) and the RX module is ready (Receiver flag FLAG_R is reset), the incoming data bits are sensed and saved in a buffer (sensing rate equals four times the baud rate). It's important that the data bit is sensed in the middle to verify a stable state on the RS232 line. FLAG_R is set during the receiving process and reset after the stop bit was received correctly. The received data is output on a 8bit data bus and is buffered until the next receiving process. An asynchronous reset is resetting all states and the data buffer. library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity receiver is
Port ( CLK : in STD_LOGIC;
RXD : in STD_LOGIC;
FLAG_R : inout STD_LOGIC;
DATA_OUT : out STD_LOGIC_VECTOR (7 downto 0);
RESET : in STD_LOGIC);
end receiver;
architecture Behavioral of receiver is
constant baudDivide : std_logic_vector(9 downto 0) := "1010001011";
--baude rate divider for 9600 by 4*baude rate = sample rate
-- by dividing 50Mhz by 2 and 38400
signal clkDiv : std_logic_vector(9 downto 0) := "0000000000";
signal rClk : std_logic := '0'; -- sample clock (38400)
signal readBuffer : std_logic_vector(7 downto 0);
begin
process (CLK) --Define rClk
begin
if CLK = '1' and CLK'Event then
if clkDiv = baudDivide then
rClk <= not rClk;
clkDiv <= "0000000000";
else
clkDiv <= clkDiv +1;
end if;
end if;
end process;
process(rClk,RESET)
variable count : integer;
begin
if(RESET='1') then -- asynchrous reset
FLAG_R <= '0' ;
count := 0;
DATA_OUT <= "00000000" ;
elsif(rClk'event and rClk='1' ) then
if(RXD='0' and FLAG_R='0')then -- Start-Bit
FLAG_R <= '1' ;
count := 0;
elsif(FLAG_R='1') then
case count is
when 5 => readBuffer(0) <= RXD ; -- 6. Takt LSB
when 9 => readBuffer(1) <= RXD ; -- 10. Takt
when 13 => readBuffer(2) <= RXD ; -- 14. Takt
when 17 => readBuffer(3) <= RXD ; -- 18. Takt
when 21 => readBuffer(4) <= RXD ; -- 22. Takt
when 25 => readBuffer(5) <= RXD ; -- 26. Takt
when 29 => readBuffer(6) <= RXD ; -- 30. Takt
when 33 => readBuffer(7) <= RXD ; -- 34. Takt MSB
when 37 => FLAG_R <= '0' ;
when others => null;
end case ;
count := count + 1;
DATA_OUT <= readBuffer;
end if ;
end if ;
end process ;
end Behavioral;
RAM Figure: RAM module (Dual-Port-RAM)
The RAM is internal data storage for the SMS. Basically it's a 8bit wide RAM with 256 possible addresses. You can compare it to a char array of 256 chars. This RAM features parallel writing and reading of different storage addresses (separate address and data busses for read and write access: So called Dual-Port-RAM). Read and write processes are enabled with separated control signals. Access is synchronized with the board clock. An asynchronous reset clears all data cells with the value 0x01h. library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity RAM is
Port ( DATA_OUT : out STD_LOGIC_VECTOR (7 downto 0);
DATA_IN : in STD_LOGIC_VECTOR (7 downto 0);
ADDRESS : in STD_LOGIC_VECTOR (7 downto 0);
ADDRESS_OUT : in STD_LOGIC_VECTOR (7 downto 0);
RD : in STD_LOGIC;
CLK : in STD_LOGIC;
RESET : in STD_LOGIC;
WRT : in STD_LOGIC);
end RAM;
architecture Behavioral of RAM is
type arr is array(0 to 255) of std_logic_vector(7 downto 0);
function vec_to_int (vec : in STD_LOGIC_VECTOR (7 downto 0)) return integer is
variable result : integer := 0;
begin
result := 0;
if (vec(7) = '1') then
result := result + 128;
end if;
if (vec(6) = '1') then
result := result + 64;
end if;
if (vec(5) = '1') then
result := result + 32;
end if;
if (vec(4) = '1') then
result := result + 16;
end if;
if (vec(3) = '1') then
result := result + 8;
end if;
if (vec(2) = '1') then
result := result + 4;
end if;
if (vec(1) = '1') then
result := result + 2;
end if;
if (vec(0) = '1') then
result := result + 1;
end if;
return result;
end;
begin
process(CLK, RESET, ADDRESS)
variable i : integer;
variable data : arr;
begin
if (RESET = '1') then
for i in 0 to 255 loop
data(i) := "00000001";
end loop;
DATA_OUT <= "00000001";
elsif (CLK'event and CLK = '1') then
--else
if (RD = '1') then --Read data
DATA_OUT <= data(vec_to_int(ADDRESS_OUT));
end if;
if (WRT = '1') then --Write data
data(vec_to_int(ADDRESS)) := DATA_IN;
end if;
end if;
end process;
end Behavioral;
LCD controller
Figure: LCD controller module
The LCD controller is used to control the LCD and to display the SMS on the LCD like a ticker. First step is to initialize the display (8bit data, 1 line, cursor off, cursor moves, cursor increments). Now the data is read from the RAM and is displayed on the LCD. The controller is responsible for the right offset of the text to invoke the effect of scrolling letters. The LCD is filled multiple times a second and the starting address is incremented after each of these cycles. That's why the text seems to scroll through the display. This module is working independently of the RAM content. The module tries immediately after reboot to read data from the RAM although there is no data stored. Maybe I'll add a control signal from the system controller to fix this problem. library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity LCD_Controller is
Port ( CLK : in STD_LOGIC;
CLK_1000Hz : inout STD_LOGIC;
RESET : in STD_LOGIC;
DATA_OUT : out STD_LOGIC_VECTOR (7 downto 0);
RAM_ADDRESS : out STD_LOGIC_VECTOR (7 downto 0);
ENABLE : out STD_LOGIC;
RD_RAM : out STD_LOGIC;
RS : out STD_LOGIC;
DATA_IN : in STD_LOGIC_VECTOR (7 downto 0));
end LCD_Controller;
architecture Behavioral of LCD_Controller is
type arr is array(0 to 36) of std_logic_vector(7 downto 0);
signal CLK_1ms : std_logic;
begin
process(CLK)
variable cnt : integer range 0 to 50000 := 0;
begin
if (CLK'Event) and (CLK = '1') then
if (cnt >= 25000) then
CLK_1ms <= not(CLK_1ms);
CLK_1000Hz <= not(CLK_1000Hz);
cnt := 0;
end if;
cnt := cnt + 1;
end if;
end process;
process(CLK_1ms, RESET)
variable token : arr;
variable isInit : boolean := false;
variable initStep : integer := 0;
variable writeStep : integer := 0;
variable cmd : std_logic_vector (7 downto 0);
variable ram_add : std_logic_vector (7 downto 0);
variable cntTime : integer := 0;
variable cntLimit : integer := 0;
variable letterOffset : integer := 0;
begin
if (RESET = '1') then
isInit := false; --controll var for initilize display
initStep := 0; --controll var for different commands
cntTime := 0; --timer for waiting times
cntLimit := 0; --timer limit
writeStep := 0; --controll var for writing on display (which char)
letterOffset := 0; --offset for shifting effect on display
ram_add := "00000000"; --RAM address lines resetz
RD_RAM <= '1'; --Read RAM allways 1
elsif (CLK_1ms'Event) and (CLK_1ms = '1') then
RAM_ADDRESS <= ram_add; --setting RAM address
if (isInit = false) then --initilize LCD
if (cntTime = 0) then
case initStep is -- define commands and waiting times
when 0 => cmd := "00111000"; cntLimit := 100; --0x38
when 1 => cmd := "00111000"; cntLimit := 50; --0x38
when 2 => cmd := "00111000"; cntLimit := 10; --0x38
when 3 => cmd := "00111000"; cntLimit := 10; --0x38
when 4 => cmd := "00111000"; cntLimit := 10; --0x38
when 5 => cmd := "00000110"; cntLimit := 10; --0x06
when 6 => cmd := "00001100"; cntLimit := 10; --0x0C
when 7 => cmd := "00010100"; cntLimit := 10; --0x14
when 8 => cmd := "00000001"; cntLimit := 50; --0x01
when 9 => cmd := "10000000"; cntLimit := 100; --0x10
when others => initStep := 0; isInit := true;
end case;
DATA_OUT <= cmd; --command ist outputted
RS <= '0'; --writing command
ENABLE <= '1'; --high stroke
end if;
cntTime := cntTime + 1;
if (cntTime = 5) then
ENABLE <= '0'; --end of stroke (4ms length)
end if;
if (cntTime >= cntLimit) then --waiting times
cntTime := 0; --reset waiting time counter
initStep := initStep + 1; --change between different commands
end if;
else --LCD is init
if (cntTime = 0) then
if (writeStep = 0) then --Reset cursor position
cntLimit := 100; --prev 10, now for waiting for shift
cmd := "10000000"; --Reset cursor position to 0x00
DATA_OUT <= cmd; --command ist outputted
RS <= '0'; --writing command
else
if (DATA_IN = "00000001") then --empty data from RAM changed to <space>
DATA_OUT <= "00100000";
else
DATA_OUT <= DATA_IN; --Output data from RAM
end if;
RS <= '1'; --writing data
cntLimit := 10; --define waiting time
end if;
ENABLE <= '1'; --high stroke
end if;
cntTime := cntTime + 1;
if (cntTime = 5) then
ENABLE <= '0'; --end of stroke (4ms length)
end if;
if (cntTime >= cntLimit) then --waiting time for LCD
cntTime := 0; --reset counter for waiting
writeStep := writeStep + 1; --writing next char
ram_add := ram_add + 1; --next databyte from RAM
if (writeStep > 20) then --end of display line
writeStep := 0; --start from beginning
letterOffset := letterOffset + 1; --Offset for shifting the LCD
if (letterOffset >= 180) then
letterOffset := 0;
end if;
ram_add := "00000000"; --reset RAM address
ram_add := ram_add + letterOffset;
--starting with offset for shifting effect on display
end if;
end if;
end if;
end if;
end process;
end Behavioral;
System controller
Figure: System controller module
The system controller is the central controlling device. It uses the RX and the TX component to communicate with the GSM module. The controller is sending the AT-command AT+CMGL="ALL" every second to poll the stored SMS from the GSM module. The received data is saved in the RAM. At the moment the GSM module has to be configured via PC (e.g. PIN, text mode, clear the memory), because the system controller is only capable of sending one command. It's planed to make the system work autonomously. Therefore it has to deal with all necessary commands. library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity Test_Data_out is
Port ( DATA_OUT : out STD_LOGIC_VECTOR (7 downto 0);
FLAG_T : in std_logic;
FLAG_R : in std_logic;
RESET : in STD_LOGIC;
DATA_IN : in STD_LOGIC_VECTOR (7 downto 0);
RAM_OUT : out STD_LOGIC_VECTOR (7 downto 0);
RAM_ADDRESS : out STD_LOGIC_VECTOR (7 downto 0);
--RD_RAM : out std_logic;
WRT_RAM : out std_logic;
clk : in std_logic);
end Test_Data_out;
architecture Behavioral of Test_Data_out is
type arr is array(0 to 13) of std_logic_vector(7 downto 0);
--shared variable token : arr;
signal at_cmgl_all : arr;
begin
process(clk, RESET)
variable cnt : integer range 0 to 13 := 0;
variable cnt_clk : integer range 0 to 100000000 := 90000000;
variable send : boolean;
variable ram_wrt : boolean;
variable sending : boolean;
variable ram_add : std_logic_vector (7 downto 0);
procedure init_tokens is
begin
at_cmgl_all(0) <= "01100001"; --a
at_cmgl_all(1) <= "01110100"; --t
at_cmgl_all(2) <= "00101011"; --+
at_cmgl_all(3) <= "01100011"; --c
at_cmgl_all(4) <= "01101101"; --m
at_cmgl_all(5) <= "01100111"; --g
at_cmgl_all(6) <= "01101100"; --l
at_cmgl_all(7) <= "00111101"; --=
at_cmgl_all(8) <= "00100010"; --"
at_cmgl_all(9) <= "01100001"; --a
at_cmgl_all(10) <= "01101100"; --l
at_cmgl_all(11) <= "01101100"; --l
at_cmgl_all(12) <= "00100010"; --"
at_cmgl_all(13) <= "00001101"; --<cr>
end init_tokens;
begin
init_tokens;
if (RESET = '1') then
ram_add := "00010100"; --20 for text start position
cnt_clk := 0;
elsif (clk = '1') and (clk'event) then
if (cnt_clk = 100000000) then
cnt_clk := 0;
send := true;
ram_add := "00010100"; --20 for text start position
end if;
if (send = true) then
if (FLAG_T = '0') then
DATA_OUT <= at_cmgl_all(cnt);
end if;
if (FLAG_T = '1') then
sending := true;
end if;
if (FLAG_T = '0') and (sending = true) then
sending := false;
cnt := cnt + 1;
end if;
if (cnt = 14) then
cnt := 0;
send := false;
DATA_OUT <= "00000000";
end if;
else
if (FLAG_R = '0') then
ram_wrt := true;
RAM_ADDRESS <= ram_add;
RAM_OUT <= DATA_IN;
WRT_RAM <= '1';
else
if (ram_wrt = true) then
ram_add := ram_add + 1;
ram_wrt := false;
end if;
end if;
end if;
cnt_clk := cnt_clk + 1;
end if;
end process;
end Behavioral;
|
- SMS_to_LCD.zip - on 26 Jul 2011 02:08 by Alexander Wegner (version 1)
4196k Download
Tidak ada komentar:
Posting Komentar